diff --git a/cache/NOTICE b/cache/NOTICE deleted file mode 100644 index 8f0040336..000000000 --- a/cache/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Cache Component -Copyright 2009 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/cache/src/main/scala/sbt/Cache.scala b/cache/src/main/scala/sbt/Cache.scala deleted file mode 100644 index bdfd8cb51..000000000 --- a/cache/src/main/scala/sbt/Cache.scala +++ /dev/null @@ -1,248 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import sbinary.{ CollectionTypes, DefaultProtocol, Format, Input, JavaFormats, Output => Out } -import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, File, InputStream, OutputStream } -import java.net.{ URI, URL } -import Types.:+: -import DefaultProtocol.{ asProduct2, asSingleton, BooleanFormat, ByteFormat, IntFormat, wrap } -import scala.xml.NodeSeq - -trait Cache[I, O] { - def apply(file: File)(i: I): Either[O, O => Unit] -} -trait SBinaryFormats extends CollectionTypes with JavaFormats { - implicit def urlFormat: Format[URL] = DefaultProtocol.UrlFormat - implicit def uriFormat: Format[URI] = DefaultProtocol.UriFormat -} -object Cache extends CacheImplicits { - def cache[I, O](implicit c: Cache[I, O]): Cache[I, O] = c - - def cached[I, O](file: File)(f: I => O)(implicit cache: Cache[I, O]): I => O = - in => - cache(file)(in) match { - case Left(value) => value - case Right(store) => - val out = f(in) - store(out) - out - } - - def debug[I](label: String, c: InputCache[I]): InputCache[I] = - new InputCache[I] { - type Internal = c.Internal - def convert(i: I) = c.convert(i) - def read(from: Input) = - { - val v = c.read(from) - println(label + ".read: " + v) - v - } - def write(to: Out, v: Internal): Unit = { - println(label + ".write: " + v) - c.write(to, v) - } - def equiv: Equiv[Internal] = new Equiv[Internal] { - def equiv(a: Internal, b: Internal) = - { - val equ = c.equiv.equiv(a, b) - println(label + ".equiv(" + a + ", " + b + "): " + equ) - equ - } - } - } -} -trait CacheImplicits extends BasicCacheImplicits with SBinaryFormats with HListCacheImplicits with UnionImplicits -trait BasicCacheImplicits { - implicit def basicCache[I, O](implicit in: InputCache[I], outFormat: Format[O]): Cache[I, O] = - new BasicCache()(in, outFormat) - def basicInput[I](implicit eq: Equiv[I], fmt: Format[I]): InputCache[I] = InputCache.basicInputCache(fmt, eq) - - def defaultEquiv[T]: Equiv[T] = new Equiv[T] { def equiv(a: T, b: T) = a == b } - - implicit def optInputCache[T](implicit t: InputCache[T]): InputCache[Option[T]] = - new InputCache[Option[T]] { - type Internal = Option[t.Internal] - def convert(v: Option[T]): Internal = v.map(x => t.convert(x)) - def read(from: Input) = - { - val isDefined = BooleanFormat.reads(from) - if (isDefined) Some(t.read(from)) else None - } - def write(to: Out, j: Internal): Unit = - { - BooleanFormat.writes(to, j.isDefined) - j foreach { x => t.write(to, x) } - } - def equiv = optEquiv(t.equiv) - } - - def wrapEquiv[S, T](f: S => T)(implicit eqT: Equiv[T]): Equiv[S] = - new Equiv[S] { - def equiv(a: S, b: S) = - eqT.equiv(f(a), f(b)) - } - - implicit def optEquiv[T](implicit t: Equiv[T]): Equiv[Option[T]] = - new Equiv[Option[T]] { - def equiv(a: Option[T], b: Option[T]) = - (a, b) match { - case (None, None) => true - case (Some(va), Some(vb)) => t.equiv(va, vb) - case _ => false - } - } - implicit def urlEquiv(implicit uriEq: Equiv[URI]): Equiv[URL] = wrapEquiv[URL, URI](_.toURI)(uriEq) - implicit def uriEquiv: Equiv[URI] = defaultEquiv - implicit def stringSetEquiv: Equiv[Set[String]] = defaultEquiv - implicit def stringMapEquiv: Equiv[Map[String, String]] = defaultEquiv - - def streamFormat[T](write: (T, OutputStream) => Unit, f: InputStream => T): Format[T] = - { - val toBytes = (t: T) => { val bos = new ByteArrayOutputStream; write(t, bos); bos.toByteArray } - val fromBytes = (bs: Array[Byte]) => f(new ByteArrayInputStream(bs)) - wrap(toBytes, fromBytes)(DefaultProtocol.ByteArrayFormat) - } - - implicit def xmlInputCache(implicit strEq: InputCache[String]): InputCache[NodeSeq] = wrapIn[NodeSeq, String](_.toString, strEq) - - implicit def seqCache[T](implicit t: InputCache[T]): InputCache[Seq[T]] = - new InputCache[Seq[T]] { - type Internal = Seq[t.Internal] - def convert(v: Seq[T]) = v.map(x => t.convert(x)) - def read(from: Input) = - { - val size = IntFormat.reads(from) - def next(left: Int, acc: List[t.Internal]): Internal = - if (left <= 0) acc.reverse else next(left - 1, t.read(from) :: acc) - next(size, Nil) - } - def write(to: Out, vs: Internal): Unit = { - val size = vs.length - IntFormat.writes(to, size) - for (v <- vs) t.write(to, v) - } - def equiv: Equiv[Internal] = seqEquiv(t.equiv) - } - - implicit def arrEquiv[T](implicit t: Equiv[T]): Equiv[Array[T]] = - wrapEquiv((x: Array[T]) => x: Seq[T])(seqEquiv[T](t)) - - implicit def seqEquiv[T](implicit t: Equiv[T]): Equiv[Seq[T]] = - new Equiv[Seq[T]] { - def equiv(a: Seq[T], b: Seq[T]) = - a.length == b.length && - ((a, b).zipped forall t.equiv) - } - implicit def seqFormat[T](implicit t: Format[T]): Format[Seq[T]] = - wrap[Seq[T], List[T]](_.toList, _.toSeq)(DefaultProtocol.listFormat) - - def wrapIn[I, J](implicit f: I => J, jCache: InputCache[J]): InputCache[I] = - new InputCache[I] { - type Internal = jCache.Internal - def convert(i: I) = jCache.convert(f(i)) - def read(from: Input) = jCache.read(from) - def write(to: Out, j: Internal) = jCache.write(to, j) - def equiv = jCache.equiv - } - - def singleton[T](t: T): InputCache[T] = - basicInput(trueEquiv, asSingleton(t)) - - def trueEquiv[T] = new Equiv[T] { def equiv(a: T, b: T) = true } -} - -trait HListCacheImplicits { - implicit def hConsCache[H, T <: HList](implicit head: InputCache[H], tail: InputCache[T]): InputCache[H :+: T] = - new InputCache[H :+: T] { - type Internal = (head.Internal, tail.Internal) - def convert(in: H :+: T) = (head.convert(in.head), tail.convert(in.tail)) - def read(from: Input) = - { - val h = head.read(from) - val t = tail.read(from) - (h, t) - } - def write(to: Out, j: Internal): Unit = { - head.write(to, j._1) - tail.write(to, j._2) - } - def equiv = new Equiv[Internal] { - def equiv(a: Internal, b: Internal) = - head.equiv.equiv(a._1, b._1) && - tail.equiv.equiv(a._2, b._2) - } - } - - implicit def hNilCache: InputCache[HNil] = Cache.singleton(HNil: HNil) - - implicit def hConsFormat[H, T <: HList](implicit head: Format[H], tail: Format[T]): Format[H :+: T] = new Format[H :+: T] { - def reads(from: Input) = - { - val h = head.reads(from) - val t = tail.reads(from) - HCons(h, t) - } - def writes(to: Out, hc: H :+: T): Unit = { - head.writes(to, hc.head) - tail.writes(to, hc.tail) - } - } - - implicit def hNilFormat: Format[HNil] = asSingleton(HNil) -} -trait UnionImplicits { - def unionInputCache[UB, HL <: HList](implicit uc: UnionCache[HL, UB]): InputCache[UB] = - new InputCache[UB] { - type Internal = Found[_] - def convert(in: UB) = uc.find(in) - def read(in: Input) = - { - val index = ByteFormat.reads(in) - val (cache, clazz) = uc.at(index) - val value = cache.read(in) - new Found[cache.Internal](cache, clazz, value, index) - } - def write(to: Out, i: Internal): Unit = { - def write0[I](f: Found[I]): Unit = { - ByteFormat.writes(to, f.index.toByte) - f.cache.write(to, f.value) - } - write0(i) - } - def equiv: Equiv[Internal] = new Equiv[Internal] { - def equiv(a: Internal, b: Internal) = - { - if (a.clazz == b.clazz) - force(a.cache.equiv, a.value, b.value) - else - false - } - def force[T <: UB, UB](e: Equiv[T], a: UB, b: UB) = e.equiv(a.asInstanceOf[T], b.asInstanceOf[T]) - } - } - - implicit def unionCons[H <: UB, UB, T <: HList](implicit head: InputCache[H], mf: Manifest[H], t: UnionCache[T, UB]): UnionCache[H :+: T, UB] = - new UnionCache[H :+: T, UB] { - val size = 1 + t.size - def c = mf.runtimeClass - def find(value: UB): Found[_] = - if (c.isInstance(value)) new Found[head.Internal](head, c, head.convert(value.asInstanceOf[H]), size - 1) else t.find(value) - def at(i: Int): (InputCache[_ <: UB], Class[_]) = if (size == i + 1) (head, c) else t.at(i) - } - - implicit def unionNil[UB]: UnionCache[HNil, UB] = new UnionCache[HNil, UB] { - def size = 0 - def find(value: UB) = sys.error("No valid sum type for " + value) - def at(i: Int) = sys.error("Invalid union index " + i) - } - - final class Found[I](val cache: InputCache[_] { type Internal = I }, val clazz: Class[_], val value: I, val index: Int) - sealed trait UnionCache[HL <: HList, UB] { - def size: Int - def at(i: Int): (InputCache[_ <: UB], Class[_]) - def find(forValue: UB): Found[_] - } -} diff --git a/cache/src/main/scala/sbt/CacheIO.scala b/cache/src/main/scala/sbt/CacheIO.scala deleted file mode 100644 index a50da7ee7..000000000 --- a/cache/src/main/scala/sbt/CacheIO.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.{ File, FileNotFoundException } -import sbinary.{ DefaultProtocol, Format, Operations } -import scala.reflect.Manifest - -object CacheIO { - def toBytes[T](format: Format[T])(value: T)(implicit mf: Manifest[Format[T]]): Array[Byte] = - toBytes[T](value)(format, mf) - def toBytes[T](value: T)(implicit format: Format[T], mf: Manifest[Format[T]]): Array[Byte] = - Operations.toByteArray(value)(stampedFormat(format)) - def fromBytes[T](format: Format[T], default: => T)(bytes: Array[Byte])(implicit mf: Manifest[Format[T]]): T = - fromBytes(default)(bytes)(format, mf) - def fromBytes[T](default: => T)(bytes: Array[Byte])(implicit format: Format[T], mf: Manifest[Format[T]]): T = - if (bytes.isEmpty) default else Operations.fromByteArray(bytes)(stampedFormat(format)) - - def fromFile[T](format: Format[T], default: => T)(file: File)(implicit mf: Manifest[Format[T]]): T = - fromFile(file, default)(format, mf) - def fromFile[T](file: File, default: => T)(implicit format: Format[T], mf: Manifest[Format[T]]): T = - fromFile[T](file) getOrElse default - def fromFile[T](file: File)(implicit format: Format[T], mf: Manifest[Format[T]]): Option[T] = - try { Some(Operations.fromFile(file)(stampedFormat(format))) } - catch { case e: Exception => None } - - def toFile[T](format: Format[T])(value: T)(file: File)(implicit mf: Manifest[Format[T]]): Unit = - toFile(value)(file)(format, mf) - def toFile[T](value: T)(file: File)(implicit format: Format[T], mf: Manifest[Format[T]]): Unit = - { - IO.createDirectory(file.getParentFile) - Operations.toFile(value)(file)(stampedFormat(format)) - } - def stampedFormat[T](format: Format[T])(implicit mf: Manifest[Format[T]]): Format[T] = - { - import DefaultProtocol._ - withStamp(stamp(format))(format) - } - def stamp[T](format: Format[T])(implicit mf: Manifest[Format[T]]): Int = typeHash(mf) - def typeHash[T](implicit mf: Manifest[T]) = mf.toString.hashCode - def manifest[T](implicit mf: Manifest[T]): Manifest[T] = mf - def objManifest[T](t: T)(implicit mf: Manifest[T]): Manifest[T] = mf -} \ No newline at end of file diff --git a/cache/src/main/scala/sbt/FileInfo.scala b/cache/src/main/scala/sbt/FileInfo.scala deleted file mode 100644 index c735adcb0..000000000 --- a/cache/src/main/scala/sbt/FileInfo.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.{ File, IOException } -import sbinary.{ DefaultProtocol, Format } -import DefaultProtocol._ -import scala.reflect.Manifest - -sealed trait FileInfo extends NotNull { - val file: File -} -sealed trait HashFileInfo extends FileInfo { - val hash: List[Byte] -} -sealed trait ModifiedFileInfo extends FileInfo { - val lastModified: Long -} -sealed trait PlainFileInfo extends FileInfo { - def exists: Boolean -} -sealed trait HashModifiedFileInfo extends HashFileInfo with ModifiedFileInfo - -private final case class PlainFile(file: File, exists: Boolean) extends PlainFileInfo -private final case class FileHash(file: File, hash: List[Byte]) extends HashFileInfo -private final case class FileModified(file: File, lastModified: Long) extends ModifiedFileInfo -private final case class FileHashModified(file: File, hash: List[Byte], lastModified: Long) extends HashModifiedFileInfo - -object FileInfo { - implicit def existsInputCache: InputCache[PlainFileInfo] = exists.infoInputCache - implicit def modifiedInputCache: InputCache[ModifiedFileInfo] = lastModified.infoInputCache - implicit def hashInputCache: InputCache[HashFileInfo] = hash.infoInputCache - implicit def fullInputCache: InputCache[HashModifiedFileInfo] = full.infoInputCache - - sealed trait Style { - type F <: FileInfo - implicit def apply(file: File): F - implicit def unapply(info: F): File = info.file - implicit val format: Format[F] - import Cache._ - implicit def fileInfoEquiv: Equiv[F] = defaultEquiv - def infoInputCache: InputCache[F] = basicInput - implicit def fileInputCache: InputCache[File] = wrapIn[File, F] - } - object full extends Style { - type F = HashModifiedFileInfo - implicit def apply(file: File): HashModifiedFileInfo = make(file, Hash(file).toList, file.lastModified) - def make(file: File, hash: List[Byte], lastModified: Long): HashModifiedFileInfo = FileHashModified(file.getAbsoluteFile, hash, lastModified) - implicit val format: Format[HashModifiedFileInfo] = wrap(f => (f.file, f.hash, f.lastModified), (make _).tupled) - } - object hash extends Style { - type F = HashFileInfo - implicit def apply(file: File): HashFileInfo = make(file, computeHash(file)) - def make(file: File, hash: List[Byte]): HashFileInfo = FileHash(file.getAbsoluteFile, hash) - implicit val format: Format[HashFileInfo] = wrap(f => (f.file, f.hash), (make _).tupled) - private def computeHash(file: File): List[Byte] = try { Hash(file).toList } catch { case e: Exception => Nil } - } - object lastModified extends Style { - type F = ModifiedFileInfo - implicit def apply(file: File): ModifiedFileInfo = make(file, file.lastModified) - def make(file: File, lastModified: Long): ModifiedFileInfo = FileModified(file.getAbsoluteFile, lastModified) - implicit val format: Format[ModifiedFileInfo] = wrap(f => (f.file, f.lastModified), (make _).tupled) - } - object exists extends Style { - type F = PlainFileInfo - implicit def apply(file: File): PlainFileInfo = make(file) - def make(file: File): PlainFileInfo = { val abs = file.getAbsoluteFile; PlainFile(abs, abs.exists) } - implicit val format: Format[PlainFileInfo] = asProduct2[PlainFileInfo, File, Boolean](PlainFile.apply)(x => (x.file, x.exists)) - } -} - -final case class FilesInfo[F <: FileInfo] private (files: Set[F]) -object FilesInfo { - sealed abstract class Style { - type F <: FileInfo - val fileStyle: FileInfo.Style { type F = Style.this.F } - - //def manifest: Manifest[F] = fileStyle.manifest - implicit def apply(files: Set[File]): FilesInfo[F] - implicit def unapply(info: FilesInfo[F]): Set[File] = info.files.map(_.file) - implicit val formats: Format[FilesInfo[F]] - val manifest: Manifest[Format[FilesInfo[F]]] - def empty: FilesInfo[F] = new FilesInfo[F](Set.empty) - import Cache._ - def infosInputCache: InputCache[FilesInfo[F]] = basicInput - implicit def filesInputCache: InputCache[Set[File]] = wrapIn[Set[File], FilesInfo[F]] - implicit def filesInfoEquiv: Equiv[FilesInfo[F]] = defaultEquiv - } - private final class BasicStyle[FI <: FileInfo](style: FileInfo.Style { type F = FI })(implicit val manifest: Manifest[Format[FilesInfo[FI]]]) extends Style { - type F = FI - val fileStyle: FileInfo.Style { type F = FI } = style - private implicit val infoFormat: Format[FI] = fileStyle.format - implicit def apply(files: Set[File]): FilesInfo[F] = FilesInfo(files.map(_.getAbsoluteFile).map(fileStyle.apply)) - implicit val formats: Format[FilesInfo[F]] = wrap(_.files, (fs: Set[F]) => new FilesInfo(fs)) - } - lazy val full: Style { type F = HashModifiedFileInfo } = new BasicStyle(FileInfo.full) - lazy val hash: Style { type F = HashFileInfo } = new BasicStyle(FileInfo.hash) - lazy val lastModified: Style { type F = ModifiedFileInfo } = new BasicStyle(FileInfo.lastModified) - lazy val exists: Style { type F = PlainFileInfo } = new BasicStyle(FileInfo.exists) - - implicit def existsInputsCache: InputCache[FilesInfo[PlainFileInfo]] = exists.infosInputCache - implicit def hashInputsCache: InputCache[FilesInfo[HashFileInfo]] = hash.infosInputCache - implicit def modifiedInputsCache: InputCache[FilesInfo[ModifiedFileInfo]] = lastModified.infosInputCache - implicit def fullInputsCache: InputCache[FilesInfo[HashModifiedFileInfo]] = full.infosInputCache -} \ No newline at end of file diff --git a/cache/src/main/scala/sbt/SeparatedCache.scala b/cache/src/main/scala/sbt/SeparatedCache.scala deleted file mode 100644 index 9d11f1f3c..000000000 --- a/cache/src/main/scala/sbt/SeparatedCache.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import Types.:+: -import sbinary.{ DefaultProtocol, Format, Input, Output => Out } -import DefaultProtocol.ByteFormat -import java.io.{ File, InputStream, OutputStream } - -trait InputCache[I] { - type Internal - def convert(i: I): Internal - def read(from: Input): Internal - def write(to: Out, j: Internal): Unit - def equiv: Equiv[Internal] -} -object InputCache { - implicit def basicInputCache[I](implicit fmt: Format[I], eqv: Equiv[I]): InputCache[I] = - new InputCache[I] { - type Internal = I - def convert(i: I) = i - def read(from: Input): I = fmt.reads(from) - def write(to: Out, i: I) = fmt.writes(to, i) - def equiv = eqv - } - def lzy[I](mkIn: => InputCache[I]): InputCache[I] = - new InputCache[I] { - lazy val ic = mkIn - type Internal = ic.Internal - def convert(i: I) = ic convert i - def read(from: Input): ic.Internal = ic.read(from) - def write(to: Out, i: ic.Internal) = ic.write(to, i) - def equiv = ic.equiv - } -} - -class BasicCache[I, O](implicit input: InputCache[I], outFormat: Format[O]) extends Cache[I, O] { - def apply(file: File)(in: I) = - { - val j = input.convert(in) - try { applyImpl(file, j) } - catch { case e: Exception => Right(update(file)(j)) } - } - protected def applyImpl(file: File, in: input.Internal) = - { - Using.fileInputStream(file) { stream => - val previousIn = input.read(stream) - if (input.equiv.equiv(in, previousIn)) - Left(outFormat.reads(stream)) - else - Right(update(file)(in)) - } - } - protected def update(file: File)(in: input.Internal) = (out: O) => - { - Using.fileOutputStream(false)(file) { stream => - input.write(stream, in) - outFormat.writes(stream, out) - } - } -} \ No newline at end of file diff --git a/cache/src/test/scala/CacheTest.scala b/cache/src/test/scala/CacheTest.scala deleted file mode 100644 index ca66ba925..000000000 --- a/cache/src/test/scala/CacheTest.scala +++ /dev/null @@ -1,31 +0,0 @@ -package sbt - -import java.io.File -import Types.:+: - -object CacheTest // extends Properties("Cache test") -{ - val lengthCache = new File("/tmp/length-cache") - val cCache = new File("/tmp/c-cache") - - import Cache._ - import FileInfo.hash._ - import Ordering._ - import sbinary.DefaultProtocol.FileFormat - def test(): Unit = { - lazy val create = new File("test") - - val length = cached(lengthCache) { - (f: File) => { println("File length: " + f.length); f.length } - } - - lazy val fileLength = length(create) - - val c = cached(cCache) { (in: (File :+: Long :+: HNil)) => - val file :+: len :+: HNil = in - println("File: " + file + " (" + file.exists + "), length: " + len) - (len + 1) :+: file :+: HNil - } - c(create :+: fileLength :+: HNil) - } -} diff --git a/cache/tracking/NOTICE b/cache/tracking/NOTICE deleted file mode 100644 index c7c0531d9..000000000 --- a/cache/tracking/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Tracking Component -Copyright 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/cache/tracking/src/main/scala/sbt/ChangeReport.scala b/cache/tracking/src/main/scala/sbt/ChangeReport.scala deleted file mode 100644 index 8502f9d3f..000000000 --- a/cache/tracking/src/main/scala/sbt/ChangeReport.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt - -object ChangeReport { - def modified[T](files: Set[T]) = - new EmptyChangeReport[T] { - override def checked = files - override def modified = files - override def markAllModified = this - } - def unmodified[T](files: Set[T]) = - new EmptyChangeReport[T] { - override def checked = files - override def unmodified = files - } -} -/** The result of comparing some current set of objects against a previous set of objects.*/ -trait ChangeReport[T] extends NotNull { - /** The set of all of the objects in the current set.*/ - def checked: Set[T] - /** All of the objects that are in the same state in the current and reference sets.*/ - def unmodified: Set[T] - /** - * All checked objects that are not in the same state as the reference. This includes objects that are in both - * sets but have changed and files that are only in one set. - */ - def modified: Set[T] // all changes, including added - /** All objects that are only in the current set.*/ - def added: Set[T] - /** All objects only in the previous set*/ - def removed: Set[T] - def +++(other: ChangeReport[T]): ChangeReport[T] = new CompoundChangeReport(this, other) - /** - * Generate a new report with this report's unmodified set included in the new report's modified set. The new report's - * unmodified set is empty. The new report's added, removed, and checked sets are the same as in this report. - */ - def markAllModified: ChangeReport[T] = - new ChangeReport[T] { - def checked = ChangeReport.this.checked - def unmodified = Set.empty[T] - def modified = ChangeReport.this.checked - def added = ChangeReport.this.added - def removed = ChangeReport.this.removed - override def markAllModified = this - } - override def toString = - { - val labels = List("Checked", "Modified", "Unmodified", "Added", "Removed") - val sets = List(checked, modified, unmodified, added, removed) - val keyValues = labels.zip(sets).map { case (label, set) => label + ": " + set.mkString(", ") } - keyValues.mkString("Change report:\n\t", "\n\t", "") - } -} -class EmptyChangeReport[T] extends ChangeReport[T] { - def checked = Set.empty[T] - def unmodified = Set.empty[T] - def modified = Set.empty[T] - def added = Set.empty[T] - def removed = Set.empty[T] - override def toString = "No changes" -} -private class CompoundChangeReport[T](a: ChangeReport[T], b: ChangeReport[T]) extends ChangeReport[T] { - lazy val checked = a.checked ++ b.checked - lazy val unmodified = a.unmodified ++ b.unmodified - lazy val modified = a.modified ++ b.modified - lazy val added = a.added ++ b.added - lazy val removed = a.removed ++ b.removed -} \ No newline at end of file diff --git a/cache/tracking/src/main/scala/sbt/Tracked.scala b/cache/tracking/src/main/scala/sbt/Tracked.scala deleted file mode 100644 index 0de466686..000000000 --- a/cache/tracking/src/main/scala/sbt/Tracked.scala +++ /dev/null @@ -1,254 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.{ File, IOException } -import CacheIO.{ fromFile, toFile } -import sbinary.Format -import scala.pickling.PicklingException -import scala.reflect.Manifest -import scala.collection.mutable -import IO.{ delete, read, write } -import sbt.serialization._ - -object Tracked { - /** - * Creates a tracker that provides the last time it was evaluated. - * If 'useStartTime' is true, the recorded time is the start of the evaluated function. - * If 'useStartTime' is false, the recorded time is when the evaluated function completes. - * In both cases, the timestamp is not updated if the function throws an exception. - */ - def tstamp(cacheFile: File, useStartTime: Boolean = true): Timestamp = new Timestamp(cacheFile, useStartTime) - /** Creates a tracker that only evaluates a function when the input has changed.*/ - //def changed[O](cacheFile: File)(implicit format: Format[O], equiv: Equiv[O]): Changed[O] = - // new Changed[O](cacheFile) - - /** Creates a tracker that provides the difference between a set of input files for successive invocations.*/ - def diffInputs(cache: File, style: FilesInfo.Style): Difference = - Difference.inputs(cache, style) - /** Creates a tracker that provides the difference between a set of output files for successive invocations.*/ - def diffOutputs(cache: File, style: FilesInfo.Style): Difference = - Difference.outputs(cache, style) - - def lastOutput[I, O](cacheFile: File)(f: (I, Option[O]) => O)(implicit o: Format[O], mf: Manifest[Format[O]]): I => O = in => - { - val previous: Option[O] = fromFile[O](cacheFile) - val next = f(in, previous) - toFile(next)(cacheFile) - next - } - // Todo: This function needs more testing. - private[sbt] def lastOutputWithJson[I, O: Pickler: Unpickler](cacheFile: File)(f: (I, Option[O]) => O): I => O = in => - { - val previous: Option[O] = try { - fromJsonFile[O](cacheFile).toOption - } catch { - case e: PicklingException => None - case e: IOException => None - } - val next = f(in, previous) - IO.createDirectory(cacheFile.getParentFile) - toJsonFile(next, cacheFile) - next - } - def inputChanged[I, O](cacheFile: File)(f: (Boolean, I) => O)(implicit ic: InputCache[I]): I => O = in => - { - val help = new CacheHelp(ic) - val conv = help.convert(in) - val changed = help.changed(cacheFile, conv) - val result = f(changed, in) - - if (changed) - help.save(cacheFile, conv) - - result - } - private[sbt] def inputChangedWithJson[I: Pickler: Unpickler, O](cacheFile: File)(f: (Boolean, I) => O): I => O = in => - { - val help = new JsonCacheHelp[I] - val conv = help.convert(in) - val changed = help.changed(cacheFile, conv) - val result = f(changed, in) - - if (changed) - help.save(cacheFile, conv) - - result - } - def outputChanged[I, O](cacheFile: File)(f: (Boolean, I) => O)(implicit ic: InputCache[I]): (() => I) => O = in => - { - val initial = in() - val help = new CacheHelp(ic) - val changed = help.changed(cacheFile, help.convert(initial)) - val result = f(changed, initial) - - if (changed) - help.save(cacheFile, help.convert(in())) - - result - } - private[sbt] def outputChangedWithJson[I: Pickler, O](cacheFile: File)(f: (Boolean, I) => O): (() => I) => O = in => - { - val initial = in() - val help = new JsonCacheHelp[I] - val changed = help.changed(cacheFile, help.convert(initial)) - val result = f(changed, initial) - - if (changed) - help.save(cacheFile, help.convert(in())) - - result - } - final class CacheHelp[I](val ic: InputCache[I]) { - def convert(i: I): ic.Internal = ic.convert(i) - def save(cacheFile: File, value: ic.Internal): Unit = - Using.fileOutputStream()(cacheFile)(out => ic.write(out, value)) - def changed(cacheFile: File, converted: ic.Internal): Boolean = - try { - val prev = Using.fileInputStream(cacheFile)(x => ic.read(x)) - !ic.equiv.equiv(converted, prev) - } catch { case e: Exception => true } - } - private[sbt] final class JsonCacheHelp[I: Pickler] { - def convert(i: I): String = toJsonString(i) - def save(cacheFile: File, value: String): Unit = - IO.write(cacheFile, value, IO.utf8) - def changed(cacheFile: File, converted: String): Boolean = - try { - val prev = IO.read(cacheFile, IO.utf8) - converted != prev - } catch { case e: Exception => true } - } -} - -trait Tracked { - /** Cleans outputs and clears the cache.*/ - def clean(): Unit -} -class Timestamp(val cacheFile: File, useStartTime: Boolean) extends Tracked { - def clean() = delete(cacheFile) - /** - * Reads the previous timestamp, evaluates the provided function, - * and then updates the timestamp if the function completes normally. - */ - def apply[T](f: Long => T): T = - { - val start = now() - val result = f(readTimestamp) - write(cacheFile, (if (useStartTime) start else now()).toString) - result - } - private def now() = System.currentTimeMillis - def readTimestamp: Long = - try { read(cacheFile).toLong } - catch { case _: NumberFormatException | _: java.io.FileNotFoundException => 0 } -} - -class Changed[O](val cacheFile: File)(implicit equiv: Equiv[O], format: Format[O]) extends Tracked { - def clean() = delete(cacheFile) - def apply[O2](ifChanged: O => O2, ifUnchanged: O => O2): O => O2 = value => - { - if (uptodate(value)) - ifUnchanged(value) - else { - update(value) - ifChanged(value) - } - } - - def update(value: O): Unit = Using.fileOutputStream(false)(cacheFile)(stream => format.writes(stream, value)) - def uptodate(value: O): Boolean = - try { - Using.fileInputStream(cacheFile) { - stream => equiv.equiv(value, format.reads(stream)) - } - } catch { - case _: Exception => false - } -} -object Difference { - def constructor(defineClean: Boolean, filesAreOutputs: Boolean): (File, FilesInfo.Style) => Difference = - (cache, style) => new Difference(cache, style, defineClean, filesAreOutputs) - - /** - * Provides a constructor for a Difference that removes the files from the previous run on a call to 'clean' and saves the - * hash/last modified time of the files as they are after running the function. This means that this information must be evaluated twice: - * before and after running the function. - */ - val outputs = constructor(true, true) - /** - * Provides a constructor for a Difference that does nothing on a call to 'clean' and saves the - * hash/last modified time of the files as they were prior to running the function. - */ - val inputs = constructor(false, false) -} -class Difference(val cache: File, val style: FilesInfo.Style, val defineClean: Boolean, val filesAreOutputs: Boolean) extends Tracked { - def clean() = - { - if (defineClean) delete(raw(cachedFilesInfo)) else () - clearCache() - } - private def clearCache() = delete(cache) - - private def cachedFilesInfo = fromFile(style.formats, style.empty)(cache)(style.manifest).files - private def raw(fs: Set[style.F]): Set[File] = fs.map(_.file) - - def apply[T](files: Set[File])(f: ChangeReport[File] => T): T = - { - val lastFilesInfo = cachedFilesInfo - apply(files, lastFilesInfo)(f)(_ => files) - } - - def apply[T](f: ChangeReport[File] => T)(implicit toFiles: T => Set[File]): T = - { - val lastFilesInfo = cachedFilesInfo - apply(raw(lastFilesInfo), lastFilesInfo)(f)(toFiles) - } - - private def abs(files: Set[File]) = files.map(_.getAbsoluteFile) - private[this] def apply[T](files: Set[File], lastFilesInfo: Set[style.F])(f: ChangeReport[File] => T)(extractFiles: T => Set[File]): T = - { - val lastFiles = raw(lastFilesInfo) - val currentFiles = abs(files) - val currentFilesInfo = style(currentFiles) - - val report = new ChangeReport[File] { - lazy val checked = currentFiles - lazy val removed = lastFiles -- checked // all files that were included previously but not this time. This is independent of whether the files exist. - lazy val added = checked -- lastFiles // all files included now but not previously. This is independent of whether the files exist. - lazy val modified = raw(lastFilesInfo -- currentFilesInfo.files) ++ added - lazy val unmodified = checked -- modified - } - - val result = f(report) - val info = if (filesAreOutputs) style(abs(extractFiles(result))) else currentFilesInfo - toFile(style.formats)(info)(cache)(style.manifest) - result - } -} - -object FileFunction { - type UpdateFunction = (ChangeReport[File], ChangeReport[File]) => Set[File] - - def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style = FilesInfo.lastModified, outStyle: FilesInfo.Style = FilesInfo.exists)(action: Set[File] => Set[File]): Set[File] => Set[File] = - cached(cacheBaseDirectory)(inStyle, outStyle)((in, out) => action(in.checked)) - - def cached(cacheBaseDirectory: File)(inStyle: FilesInfo.Style, outStyle: FilesInfo.Style)(action: UpdateFunction): Set[File] => Set[File] = - { - import Path._ - lazy val inCache = Difference.inputs(cacheBaseDirectory / "in-cache", inStyle) - lazy val outCache = Difference.outputs(cacheBaseDirectory / "out-cache", outStyle) - inputs => - { - inCache(inputs) { inReport => - outCache { outReport => - if (inReport.modified.isEmpty && outReport.modified.isEmpty) - outReport.checked - else - action(inReport, outReport) - } - } - } - } -} \ No newline at end of file diff --git a/compile/NOTICE b/compile/NOTICE deleted file mode 100644 index daddd2fdb..000000000 --- a/compile/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Compile Component -Copyright 2009, 2010 Mark Harrah, Seth Tisue, Jason Zaugg -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/compile/api/NOTICE b/compile/api/NOTICE deleted file mode 100644 index 55eeb6d67..000000000 --- a/compile/api/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Source API Component -Copyright 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/compile/api/src/main/scala/sbt/ClassToAPI.scala b/compile/api/src/main/scala/sbt/ClassToAPI.scala deleted file mode 100644 index 37e8a5919..000000000 --- a/compile/api/src/main/scala/sbt/ClassToAPI.scala +++ /dev/null @@ -1,387 +0,0 @@ -package sbt - -import java.lang.reflect.{ Array => _, _ } -import java.lang.annotation.Annotation -import annotation.tailrec -import sbt.classfile.ClassFile -import xsbti.api -import xsbti.SafeLazy -import SafeLazy.strict -import collection.mutable - -object ClassToAPI { - def apply(c: Seq[Class[_]]): api.SourceAPI = process(c)._1 - - // (api, public inherited classes) - def process(c: Seq[Class[_]]): (api.SourceAPI, Set[Class[_]]) = - { - val pkgs = packages(c).map(p => new api.Package(p)) - val cmap = emptyClassMap - val defs = c.filter(isTopLevel).flatMap(toDefinitions(cmap)) - val source = new api.SourceAPI(pkgs.toArray, defs.toArray) - cmap.lz.foreach(_.get()) // force thunks to ensure all inherited dependencies are recorded - val inDeps = cmap.inherited.toSet - cmap.clear() - (source, inDeps) - } - - // Avoiding implicit allocation. - private def arrayMap[T <: AnyRef, U <: AnyRef: reflect.ClassTag](xs: Array[T])(f: T => U): Array[U] = { - val len = xs.length - var i = 0 - val res = new Array[U](len) - while (i < len) { - res(i) = f(xs(i)) - i += 1 - } - res - } - - def packages(c: Seq[Class[_]]): Set[String] = - c.flatMap(packageName).toSet - - def isTopLevel(c: Class[_]): Boolean = - c.getEnclosingClass eq null - - final class ClassMap private[sbt] (private[sbt] val memo: mutable.Map[String, Seq[api.ClassLike]], private[sbt] val inherited: mutable.Set[Class[_]], private[sbt] val lz: mutable.Buffer[xsbti.api.Lazy[_]]) { - def clear(): Unit = { - memo.clear() - inherited.clear() - lz.clear() - } - } - def emptyClassMap: ClassMap = new ClassMap(new mutable.HashMap, new mutable.HashSet, new mutable.ListBuffer) - - def toDefinitions(cmap: ClassMap)(c: Class[_]): Seq[api.ClassLike] = - cmap.memo.getOrElseUpdate(c.getName, toDefinitions0(c, cmap)) - def toDefinitions0(c: Class[_], cmap: ClassMap): Seq[api.ClassLike] = - { - import api.DefinitionType.{ ClassDef, Module, Trait } - val enclPkg = packageName(c) - val mods = modifiers(c.getModifiers) - val acc = access(c.getModifiers, enclPkg) - val annots = annotations(c.getAnnotations) - val name = c.getName - val tpe = if (Modifier.isInterface(c.getModifiers)) Trait else ClassDef - lazy val (static, instance) = structure(c, enclPkg, cmap) - val cls = new api.ClassLike(tpe, strict(Empty), lzy(instance, cmap), emptyStringArray, typeParameters(typeParameterTypes(c)), name, acc, mods, annots) - val stat = new api.ClassLike(Module, strict(Empty), lzy(static, cmap), emptyStringArray, emptyTypeParameterArray, name, acc, mods, annots) - val defs = cls :: stat :: Nil - cmap.memo(c.getName) = defs - defs - } - - /** Returns the (static structure, instance structure, inherited classes) for `c`. */ - def structure(c: Class[_], enclPkg: Option[String], cmap: ClassMap): (api.Structure, api.Structure) = { - lazy val cf = classFileForClass(c) - val methods = mergeMap(c, c.getDeclaredMethods, c.getMethods, methodToDef(enclPkg)) - val fields = mergeMap(c, c.getDeclaredFields, c.getFields, fieldToDef(c, cf, enclPkg)) - val constructors = mergeMap(c, c.getDeclaredConstructors, c.getConstructors, constructorToDef(enclPkg)) - val classes = merge[Class[_]](c, c.getDeclaredClasses, c.getClasses, toDefinitions(cmap), (_: Seq[Class[_]]).partition(isStatic), _.getEnclosingClass != c) - val all = methods ++ fields ++ constructors ++ classes - val parentJavaTypes = allSuperTypes(c) - if (!Modifier.isPrivate(c.getModifiers)) - cmap.inherited ++= parentJavaTypes.collect { case c: Class[_] => c } - val parentTypes = types(parentJavaTypes) - val instanceStructure = new api.Structure(lzyS(parentTypes.toArray), lzyS(all.declared.toArray), lzyS(all.inherited.toArray)) - val staticStructure = new api.Structure(lzyEmptyTpeArray, lzyS(all.staticDeclared.toArray), lzyS(all.staticInherited.toArray)) - (staticStructure, instanceStructure) - } - - /** TODO: over time, ClassToAPI should switch the majority of access to the classfile parser */ - private[this] def classFileForClass(c: Class[_]): ClassFile = { - val file = new java.io.File(IO.classLocationFile(c), s"${c.getName.replace('.', '/')}.class") - classfile.Parser.apply(file) - } - - private[this] def lzyS[T <: AnyRef](t: T): xsbti.api.Lazy[T] = lzy(t) - def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t) - private[this] def lzy[T <: AnyRef](t: => T, cmap: ClassMap): xsbti.api.Lazy[T] = { - val s = lzy(t) - cmap.lz += s - s - } - - private val emptyStringArray = new Array[String](0) - private val emptyTypeArray = new Array[xsbti.api.Type](0) - private val emptyAnnotationArray = new Array[xsbti.api.Annotation](0) - private val emptyTypeParameterArray = new Array[xsbti.api.TypeParameter](0) - private val emptySimpleTypeArray = new Array[xsbti.api.SimpleType](0) - private val lzyEmptyTpeArray = lzyS(emptyTypeArray) - private val lzyEmptyDefArray = lzyS(new Array[xsbti.api.Definition](0)) - - private def allSuperTypes(t: Type): Seq[Type] = - { - @tailrec def accumulate(t: Type, accum: Seq[Type] = Seq.empty): Seq[Type] = t match { - case c: Class[_] => - val (parent, interfaces) = (c.getGenericSuperclass, c.getGenericInterfaces) - accumulate(parent, (accum :+ parent) ++ flattenAll(interfaces)) - case p: ParameterizedType => - accumulate(p.getRawType, accum) - case _ => - accum - } - @tailrec def flattenAll(interfaces: Seq[Type], accum: Seq[Type] = Seq.empty): Seq[Type] = - { - if (interfaces.nonEmpty) { - val raw = interfaces map { case p: ParameterizedType => p.getRawType; case i => i } - val children = raw flatMap { case i: Class[_] => i.getGenericInterfaces; case _ => Seq.empty } - flattenAll(children, accum ++ interfaces ++ children) - } else - accum - } - accumulate(t).filterNot(_ == null).distinct - } - - @deprecated("No longer used", "0.13.0") - def parents(c: Class[_]): Seq[api.Type] = types(allSuperTypes(c)) - def types(ts: Seq[Type]): Array[api.Type] = ts filter (_ ne null) map reference toArray; - def upperBounds(ts: Array[Type]): api.Type = - new api.Structure(lzy(types(ts)), lzyEmptyDefArray, lzyEmptyDefArray) - - @deprecated("Use fieldToDef[4] instead", "0.13.9") - def fieldToDef(enclPkg: Option[String])(f: Field): api.FieldLike = { - val c = f.getDeclaringClass() - fieldToDef(c, classFileForClass(c), enclPkg)(f) - } - - def fieldToDef(c: Class[_], cf: => ClassFile, enclPkg: Option[String])(f: Field): api.FieldLike = - { - val name = f.getName - val accs = access(f.getModifiers, enclPkg) - val mods = modifiers(f.getModifiers) - val annots = annotations(f.getDeclaredAnnotations) - val fieldTpe = reference(returnType(f)) - // generate a more specific type for constant fields - val specificTpe: Option[api.Type] = - if (mods.isFinal) { - try { - cf.constantValue(name).map(singletonForConstantField(c, f, _)) - } catch { - case e: Throwable => - throw new IllegalStateException( - s"Failed to parse class $c: this may mean your classfiles are corrupted. Please clean and try again.", - e - ) - } - } else { - None - } - val tpe = specificTpe.getOrElse(fieldTpe) - if (mods.isFinal) { - new api.Val(tpe, name, accs, mods, annots) - } else { - new api.Var(tpe, name, accs, mods, annots) - } - } - - /** - * Creates a Singleton type that includes both the type and ConstantValue for the given Field. - * - * Since java compilers are allowed to inline constant (static final primitive) fields in - * downstream classfiles, we generate a type that will cause APIs to match only when both - * the type and value of the field match. We include the classname mostly for readability. - * - * Because this type is purely synthetic, it's fine that the name might contain filename- - * banned characters. - */ - private def singletonForConstantField(c: Class[_], field: Field, constantValue: AnyRef) = - new api.Singleton( - pathFromStrings( - c.getName.split("\\.").toSeq :+ (field.getName + "$" + returnType(field) + "$" + constantValue) - ) - ) - - def methodToDef(enclPkg: Option[String])(m: Method): api.Def = - defLike(m.getName, m.getModifiers, m.getDeclaredAnnotations, typeParameterTypes(m), m.getParameterAnnotations, parameterTypes(m), Option(returnType(m)), exceptionTypes(m), m.isVarArgs, enclPkg) - - def constructorToDef(enclPkg: Option[String])(c: Constructor[_]): api.Def = - defLike("", c.getModifiers, c.getDeclaredAnnotations, typeParameterTypes(c), c.getParameterAnnotations, parameterTypes(c), None, exceptionTypes(c), c.isVarArgs, enclPkg) - - def defLike[T <: GenericDeclaration](name: String, mods: Int, annots: Array[Annotation], tps: Array[TypeVariable[T]], paramAnnots: Array[Array[Annotation]], paramTypes: Array[Type], retType: Option[Type], exceptions: Array[Type], varArgs: Boolean, enclPkg: Option[String]): api.Def = - { - val varArgPosition = if (varArgs) paramTypes.length - 1 else -1 - val isVarArg = List.tabulate(paramTypes.length)(_ == varArgPosition) - val pa = (paramAnnots, paramTypes, isVarArg).zipped map { case (a, p, v) => parameter(a, p, v) } - val params = new api.ParameterList(pa, false) - val ret = retType match { case Some(rt) => reference(rt); case None => Empty } - new api.Def(Array(params), ret, typeParameters(tps), name, access(mods, enclPkg), modifiers(mods), annotations(annots) ++ exceptionAnnotations(exceptions)) - } - - def exceptionAnnotations(exceptions: Array[Type]): Array[api.Annotation] = - if (exceptions.length == 0) emptyAnnotationArray - else arrayMap(exceptions)(t => new api.Annotation(Throws, Array(new api.AnnotationArgument("value", t.toString)))) - - def parameter(annots: Array[Annotation], parameter: Type, varArgs: Boolean): api.MethodParameter = - new api.MethodParameter("", annotated(reference(parameter), annots), false, if (varArgs) api.ParameterModifier.Repeated else api.ParameterModifier.Plain) - - def annotated(t: api.SimpleType, annots: Array[Annotation]): api.Type = ( - if (annots.length == 0) t - else new api.Annotated(t, annotations(annots)) - ) - - case class Defs(declared: Seq[api.Definition], inherited: Seq[api.Definition], staticDeclared: Seq[api.Definition], staticInherited: Seq[api.Definition]) { - def ++(o: Defs) = Defs(declared ++ o.declared, inherited ++ o.inherited, staticDeclared ++ o.staticDeclared, staticInherited ++ o.staticInherited) - } - def mergeMap[T <: Member](of: Class[_], self: Seq[T], public: Seq[T], f: T => api.Definition): Defs = - merge[T](of, self, public, x => f(x) :: Nil, splitStatic _, _.getDeclaringClass != of) - - def merge[T](of: Class[_], self: Seq[T], public: Seq[T], f: T => Seq[api.Definition], splitStatic: Seq[T] => (Seq[T], Seq[T]), isInherited: T => Boolean): Defs = - { - val (selfStatic, selfInstance) = splitStatic(self) - val (inheritedStatic, inheritedInstance) = splitStatic(public filter isInherited) - Defs(selfInstance flatMap f, inheritedInstance flatMap f, selfStatic flatMap f, inheritedStatic flatMap f) - } - - def splitStatic[T <: Member](defs: Seq[T]): (Seq[T], Seq[T]) = - defs partition isStatic - - def isStatic(c: Class[_]): Boolean = Modifier.isStatic(c.getModifiers) - def isStatic(a: Member): Boolean = Modifier.isStatic(a.getModifiers) - - def typeParameters[T <: GenericDeclaration](tps: Array[TypeVariable[T]]): Array[api.TypeParameter] = - if (tps.length == 0) emptyTypeParameterArray - else arrayMap(tps)(typeParameter) - - def typeParameter[T <: GenericDeclaration](tp: TypeVariable[T]): api.TypeParameter = - new api.TypeParameter(typeVariable(tp), emptyAnnotationArray, emptyTypeParameterArray, api.Variance.Invariant, NothingRef, upperBounds(tp.getBounds)) - - // needs to be stable across compilations - def typeVariable[T <: GenericDeclaration](tv: TypeVariable[T]): String = - name(tv.getGenericDeclaration) + " " + tv.getName - - def reduceHash(in: Array[Byte]): Int = - (0 /: in)((acc, b) => (acc * 43) ^ b) - - def name(gd: GenericDeclaration): String = - gd match { - case c: Class[_] => c.getName - case m: Method => m.getName - case c: Constructor[_] => c.getName - } - - def modifiers(i: Int): api.Modifiers = - { - import Modifier.{ isAbstract, isFinal } - new api.Modifiers(isAbstract(i), false, isFinal(i), false, false, false, false) - } - def access(i: Int, pkg: Option[String]): api.Access = - { - import Modifier.{ isPublic, isPrivate, isProtected } - if (isPublic(i)) Public else if (isPrivate(i)) Private else if (isProtected(i)) Protected else packagePrivate(pkg) - } - - def annotations(a: Array[Annotation]): Array[api.Annotation] = if (a.length == 0) emptyAnnotationArray else arrayMap(a)(annotation) - def annotation(a: Annotation): api.Annotation = - new api.Annotation(reference(a.annotationType), Array(javaAnnotation(a.toString))) - - // full information not available from reflection - def javaAnnotation(s: String): api.AnnotationArgument = - new api.AnnotationArgument("toString", s) - - def array(tpe: api.Type): api.SimpleType = new api.Parameterized(ArrayRef, Array(tpe)) - def reference(c: Class[_]): api.SimpleType = - if (c.isArray) array(reference(c.getComponentType)) else if (c.isPrimitive) primitive(c.getName) else reference(c.getName) - - // does not handle primitives - def reference(s: String): api.SimpleType = - { - val (pkg, cls) = packageAndName(s) - pkg match { - // translate all primitives? - case None => new api.Projection(Empty, cls) - case Some(p) => - new api.Projection(new api.Singleton(pathFromString(p)), cls) - } - } - def referenceP(t: ParameterizedType): api.Parameterized = - { - val targs = t.getActualTypeArguments - val args = if (targs.isEmpty) emptyTypeArray else arrayMap(targs)(t => reference(t): api.Type) - val base = reference(t.getRawType) - new api.Parameterized(base, args.toArray[api.Type]) - } - def reference(t: Type): api.SimpleType = - t match { - case w: WildcardType => reference("_") - case tv: TypeVariable[_] => new api.ParameterRef(typeVariable(tv)) - case pt: ParameterizedType => referenceP(pt) - case gat: GenericArrayType => array(reference(gat.getGenericComponentType)) - case c: Class[_] => reference(c) - } - - def pathFromString(s: String): api.Path = - pathFromStrings(s.split("\\.")) - def pathFromStrings(ss: Seq[String]): api.Path = - new api.Path((ss.map(new api.Id(_)) :+ ThisRef).toArray) - def packageName(c: Class[_]) = packageAndName(c)._1 - def packageAndName(c: Class[_]): (Option[String], String) = - packageAndName(c.getName) - def packageAndName(name: String): (Option[String], String) = - { - val lastDot = name.lastIndexOf('.') - if (lastDot >= 0) - (Some(name.substring(0, lastDot)), name.substring(lastDot + 1)) - else - (None, name) - } - - val Empty = new api.EmptyType - val ThisRef = new api.This - - val Public = new api.Public - val Unqualified = new api.Unqualified - val Private = new api.Private(Unqualified) - val Protected = new api.Protected(Unqualified) - def packagePrivate(pkg: Option[String]): api.Access = new api.Private(new api.IdQualifier(pkg getOrElse "")) - - val ArrayRef = reference("scala.Array") - val Throws = reference("scala.throws") - val NothingRef = reference("scala.Nothing") - - private[this] def PrimitiveNames = Seq("boolean", "byte", "char", "short", "int", "long", "float", "double") - private[this] def PrimitiveMap = PrimitiveNames.map(j => (j, j.capitalize)) :+ ("void" -> "Unit") - private[this] val PrimitiveRefs = PrimitiveMap.map { case (n, sn) => (n, reference("scala." + sn)) }.toMap - def primitive(name: String): api.SimpleType = PrimitiveRefs(name) - - // Workarounds for https://github.com/sbt/sbt/issues/1035 - // these catch the GenericSignatureFormatError and return the erased type - - private[this] def returnType(f: Field): Type = try f.getGenericType catch { - case _: GenericSignatureFormatError => f.getType - } - private[this] def parameterTypes(c: Constructor[_]): Array[Type] = try c.getGenericParameterTypes catch { - case _: GenericSignatureFormatError => convert(c.getParameterTypes) - } - private[this] def exceptionTypes(c: Constructor[_]): Array[Type] = try c.getGenericExceptionTypes catch { - case _: GenericSignatureFormatError => convert(c.getExceptionTypes) - } - private[this] def parameterTypes(m: Method): Array[Type] = try m.getGenericParameterTypes catch { - case _: GenericSignatureFormatError => convert(m.getParameterTypes) - } - private[this] def returnType(m: Method): Type = try m.getGenericReturnType catch { - case _: GenericSignatureFormatError => m.getReturnType - } - private[this] def exceptionTypes(m: Method): Array[Type] = try m.getGenericExceptionTypes catch { - case _: GenericSignatureFormatError => convert(m.getExceptionTypes) - } - - private[this] def typeParameterTypes[T](m: Constructor[T]): Array[TypeVariable[Constructor[T]]] = try m.getTypeParameters catch { - case _: GenericSignatureFormatError => new Array(0) - } - private[this] def typeParameterTypes[T](m: Class[T]): Array[TypeVariable[Class[T]]] = try m.getTypeParameters catch { - case _: GenericSignatureFormatError => new Array(0) - } - private[this] def typeParameterTypes(m: Method): Array[TypeVariable[Method]] = try m.getTypeParameters catch { - case _: GenericSignatureFormatError => new Array(0) - } - private[this] def superclassType(c: Class[_]): Type = try c.getGenericSuperclass catch { - case _: GenericSignatureFormatError => c.getSuperclass - } - private[this] def interfaces(c: Class[_]): Array[Type] = try c.getGenericInterfaces catch { - case _: GenericSignatureFormatError => convert(c.getInterfaces) - } - - private[this] def convert(classes: Array[Class[_]]): Array[Type] = - classes.asInstanceOf[Array[Type]] // ok: treat Arrays as read-only -} diff --git a/compile/api/src/main/scala/xsbt/api/APIUtil.scala b/compile/api/src/main/scala/xsbt/api/APIUtil.scala deleted file mode 100644 index e7c4ac1c8..000000000 --- a/compile/api/src/main/scala/xsbt/api/APIUtil.scala +++ /dev/null @@ -1,66 +0,0 @@ -package xsbt.api - -import xsbti.SafeLazy -import xsbti.api._ -import scala.collection.mutable.HashSet - -object APIUtil { - val modifiersToByte = (m: Modifiers) => { - import m._ - def x(b: Boolean, bit: Int) = if (b) 1 << bit else 0 - (x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) | x(isMacro, 6)).toByte - } - val byteToModifiers = (b: Byte) => { - def x(bit: Int) = (b & (1 << bit)) != 0 - new Modifiers(x(0), x(1), x(2), x(3), x(4), x(5), x(6)) - } - - def isScalaSourceName(name: String): Boolean = name.endsWith(".scala") - - def hasMacro(s: SourceAPI): Boolean = - { - val check = new HasMacro - check.visitAPI(s) - check.hasMacro - } - - private[this] class HasMacro extends Visit { - var hasMacro = false - - // Don't visit inherited definitions since we consider that a class - // that inherits a macro does not have a macro. - override def visitStructure0(structure: Structure): Unit = { - visitTypes(structure.parents) - visitDefinitions(structure.declared) - } - - override def visitModifiers(m: Modifiers): Unit = { - hasMacro ||= m.isMacro - super.visitModifiers(m) - } - } - - def minimize(api: SourceAPI): SourceAPI = - new SourceAPI(api.packages, minimizeDefinitions(api.definitions)) - def minimizeDefinitions(ds: Array[Definition]): Array[Definition] = - ds flatMap minimizeDefinition - def minimizeDefinition(d: Definition): Array[Definition] = - d match { - case c: ClassLike => Array(minimizeClass(c)) - case _ => Array() - } - def minimizeClass(c: ClassLike): ClassLike = - { - val savedAnnotations = Discovery.defAnnotations(c.structure, (_: Any) => true).toArray[String] - val struct = minimizeStructure(c.structure, c.definitionType == DefinitionType.Module) - new ClassLike(c.definitionType, lzy(emptyType), lzy(struct), savedAnnotations, c.typeParameters, c.name, c.access, c.modifiers, c.annotations) - } - - def minimizeStructure(s: Structure, isModule: Boolean): Structure = - new Structure(lzy(s.parents), filterDefinitions(s.declared, isModule), filterDefinitions(s.inherited, isModule)) - def filterDefinitions(ds: Array[Definition], isModule: Boolean): Lazy[Array[Definition]] = - lzy(if (isModule) ds filter Discovery.isMainMethod else Array()) - private[this] def lzy[T <: AnyRef](t: T): Lazy[T] = SafeLazy.strict(t) - - private[this] val emptyType = new EmptyType -} diff --git a/compile/api/src/main/scala/xsbt/api/Discovered.scala b/compile/api/src/main/scala/xsbt/api/Discovered.scala deleted file mode 100644 index f19793190..000000000 --- a/compile/api/src/main/scala/xsbt/api/Discovered.scala +++ /dev/null @@ -1,11 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - -final case class Discovered(baseClasses: Set[String], annotations: Set[String], hasMain: Boolean, isModule: Boolean) { - def isEmpty = baseClasses.isEmpty && annotations.isEmpty -} -object Discovered { - def empty = new Discovered(Set.empty, Set.empty, false, false) -} \ No newline at end of file diff --git a/compile/api/src/main/scala/xsbt/api/Discovery.scala b/compile/api/src/main/scala/xsbt/api/Discovery.scala deleted file mode 100644 index 9cd07d4d2..000000000 --- a/compile/api/src/main/scala/xsbt/api/Discovery.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - -import xsbti.api.{ Path => APath, _ } - -import Discovery._ - -class Discovery(baseClasses: Set[String], annotations: Set[String]) { - def apply(s: Seq[Definition]): Seq[(Definition, Discovered)] = - s.map { d => (d, apply(d)) } - def apply(d: Definition): Discovered = - d match { - case c: ClassLike if isConcrete(c.modifiers) => - if (isPublic(c)) - discover(c) - else if (isModule(c) && hasMainMethod(c)) // jvm does not require a main class to be public - new Discovered(Set.empty, Set.empty, true, true) - else - Discovered.empty - case _ => Discovered.empty - } - def discover(c: ClassLike): Discovered = - { - val onClass = Discovery.findAnnotations(c.annotations, annotations) - val onDefs = Discovery.defAnnotations(c.structure, annotations) ++ c.savedAnnotations.filter(annotations) - val module = isModule(c) - new Discovered(bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module) - } - - def bases(own: String, c: Seq[Type]): Set[String] = - (own +: c.flatMap(simpleName)).filter(baseClasses).toSet - -} -object Discovery { - def apply(subclasses: Set[String], annotations: Set[String])(definitions: Seq[Definition]): Seq[(Definition, Discovered)] = - { - val d = new Discovery(subclasses, annotations) - d(definitions) - } - def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] = - apply(Set.empty, Set.empty)(definitions) - - def findAnnotations(as: Seq[Annotation], pred: String => Boolean): Set[String] = - as.flatMap { a => simpleName(a.base).filter(pred) }.toSet - def defAnnotations(s: Structure, pred: String => Boolean): Set[String] = - defAnnotations(s.declared, pred) ++ defAnnotations(s.inherited, pred) - def defAnnotations(defs: Seq[Definition], pred: String => Boolean): Set[String] = - findAnnotations(defs.flatMap { case d: Def if isPublic(d) => d.annotations.toSeq; case _ => Nil }, pred) - - def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers) - def isConcrete(m: Modifiers) = !m.isAbstract - def isPublic(a: Definition): Boolean = isPublic(a.access) - def isPublic(a: Access): Boolean = a.isInstanceOf[Public] - def isModule(c: ClassLike) = c.definitionType == DefinitionType.Module - - def hasMainMethod(c: ClassLike): Boolean = - hasMainMethod(c.structure.declared) || hasMainMethod(c.structure.inherited) - def hasMainMethod(defs: Seq[Definition]): Boolean = - defs.exists(isMainMethod) - def isMainMethod(d: Definition): Boolean = - d match { - case d: Def => d.name == "main" && isPublic(d) && isConcrete(d) && isUnit(d.returnType) && isStringArray(d.valueParameters) - case _ => false - } - def isStringArray(vp: IndexedSeq[ParameterList]): Boolean = vp.length == 1 && isStringArray(vp(0).parameters) - def isStringArray(params: Seq[MethodParameter]): Boolean = params.length == 1 && isStringArray(params(0)) - def isStringArray(p: MethodParameter): Boolean = (p.modifier == ParameterModifier.Plain || p.modifier == ParameterModifier.Repeated) && isStringArray(p.tpe) - def isStringArray(t: Type): Boolean = isParameterized(t, "scala.Array", "java.lang.String") // doesn't handle scala.this#Predef#String, should API phase dealias? - - def isParameterized(t: Type, base: String, args: String*): Boolean = t match { - case p: Parameterized => - named(p.baseType, base) && p.typeArguments.length == args.length && p.typeArguments.flatMap(simpleName).sameElements(args) - case _ => false - } - def named(t: Type, nme: String) = simpleName(t) == Some(nme) - - def simpleName(t: Type): Option[String] = t match { - case a: Annotated => simpleName(a.baseType) - case sing: Singleton => None - case p: Projection => - p.prefix match { - case s: Singleton => pathName(s.path, p.id) - case e: EmptyType => Some(p.id) - case _ => None - } - case _ => None - } - - def pathName(p: APath, id: String): Option[String] = - { - val cs = p.components - cs.last match { - case _: This => - val ids = cs.init.collect { case i: Id => i.id } - if (ids.length == cs.length - 1) Some((ids ++ Seq(id)).mkString(".")) else None - case _ => None - } - } - - def isUnit(t: Type): Boolean = named(t, "scala.Unit") -} diff --git a/compile/api/src/main/scala/xsbt/api/HashAPI.scala b/compile/api/src/main/scala/xsbt/api/HashAPI.scala deleted file mode 100644 index 6ed4054e0..000000000 --- a/compile/api/src/main/scala/xsbt/api/HashAPI.scala +++ /dev/null @@ -1,369 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010, 2011 Mark Harrah - */ -package xsbt.api - -import scala.util -import xsbti.api._ -import util.MurmurHash -import HashAPI.Hash - -object HashAPI { - type Hash = Int - def apply(a: SourceAPI): Hash = - (new HashAPI(false, true, true)).hashAPI(a) - - def apply(x: Def): Hash = { - val hashApi = new HashAPI(false, true, true) - hashApi.hashDefinition(x) - hashApi.finalizeHash - } - - def hashDefinitionsWithExtraHashes(ds: Seq[(Definition, Hash)]): Hash = { - val hashAPI = new HashAPI(false, true, false) - hashAPI.hashDefinitionsWithExtraHashes(ds) - hashAPI.finalizeHash - } -} - -/** - * Implements hashing of public API. - * - * @param includePrivate should private definitions be included in a hash sum - * @param includeParamNames should parameter names for methods be included in a hash sum - * @param includeDefinitions when hashing a structure (e.g. of a class) should hashes of definitions (members) - * be included in a hash sum. Structure can appear as a type (in structural type) and in that case we - * always include definitions in a hash sum. - */ -final class HashAPI(includePrivate: Boolean, includeParamNames: Boolean, includeDefinitions: Boolean) { - // this constructor variant is for source and binary backwards compatibility with sbt 0.13.0 - def this(includePrivate: Boolean, includeParamNames: Boolean) { - // in the old logic we used to always include definitions hence - // includeDefinitions=true - this(includePrivate, includeParamNames, includeDefinitions = true) - } - - import scala.collection.mutable - import MurmurHash.{ extendHash, nextMagicA, nextMagicB, startHash, startMagicA, startMagicB, stringHash, symmetricHash } - - private[this] val visitedStructures = visitedMap[Structure] - private[this] val visitedClassLike = visitedMap[ClassLike] - private[this] def visitedMap[T] = new mutable.HashMap[T, List[Hash]] - private[this] def visit[T](map: mutable.Map[T, List[Hash]], t: T)(hashF: T => Unit): Unit = { - map.put(t, hash :: map.getOrElse(t, Nil)) match { - case Some(x :: _) => extend(x) - case _ => - hashF(t) - for (hs <- map(t)) - extend(hs) - map.put(t, hash :: Nil) - } - } - - private[this] def isTrait(cl: ClassLike) = cl.definitionType == DefinitionType.Trait - - private[this] final val ValHash = 1 - private[this] final val VarHash = 2 - private[this] final val DefHash = 3 - private[this] final val ClassHash = 4 - private[this] final val TypeDeclHash = 5 - private[this] final val TypeAliasHash = 6 - - private[this] final val PublicHash = 30 - private[this] final val ProtectedHash = 31 - private[this] final val PrivateHash = 32 - private[this] final val UnqualifiedHash = 33 - private[this] final val ThisQualifierHash = 34 - private[this] final val IdQualifierHash = 35 - - private[this] final val IdPathHash = 20 - private[this] final val SuperHash = 21 - private[this] final val ThisPathHash = 22 - - private[this] final val ValueParamsHash = 40 - private[this] final val ClassPendingHash = 41 - private[this] final val StructurePendingHash = 42 - - private[this] final val EmptyTypeHash = 51 - private[this] final val ParameterRefHash = 52 - private[this] final val SingletonHash = 53 - private[this] final val ProjectionHash = 54 - private[this] final val ParameterizedHash = 55 - private[this] final val AnnotatedHash = 56 - private[this] final val PolymorphicHash = 57 - private[this] final val ConstantHash = 58 - private[this] final val ExistentialHash = 59 - private[this] final val StructureHash = 60 - - private[this] final val TrueHash = 97 - private[this] final val FalseHash = 98 - - private[this] var hash: Hash = startHash(0) - private[this] var magicA: Hash = startMagicA - private[this] var magicB: Hash = startMagicB - - @inline final def hashString(s: String): Unit = extend(stringHash(s)) - @inline final def hashBoolean(b: Boolean): Unit = extend(if (b) TrueHash else FalseHash) - @inline final def hashSeq[T](s: Seq[T], hashF: T => Unit): Unit = { - extend(s.length) - s foreach hashF - } - final def hashSymmetric[T](ts: TraversableOnce[T], hashF: T => Unit): Unit = { - val current = hash - val mA = magicA - val mB = magicB - val (hashes, mAs, mBs) = ts.toList.map { t => - hash = startHash(1) - magicA = startMagicA - magicB = startMagicB - hashF(t) - (finalizeHash, magicA, magicB) - } unzip3; - hash = current - magicA = mA - magicB = mB - extend(symmetricHash(hashes, 0xb592f7ae)) // constant from MurmurHash3 - } - - @inline final def extend(a: Hash): Unit = { - hash = extendHash(hash, a, magicA, magicB) - magicA = nextMagicA(magicA) - magicB = nextMagicB(magicB) - } - - def finalizeHash: Hash = MurmurHash.finalizeHash(hash) - - def hashModifiers(m: Modifiers) = extend(m.raw) - - def hashAPI(s: SourceAPI): Hash = - { - hash = startHash(0) - hashSymmetric(s.packages, hashPackage) - hashDefinitions(s.definitions, true) - finalizeHash - } - - def hashPackage(p: Package) = hashString(p.name) - - def hashDefinitions(ds: Seq[Definition], topLevel: Boolean): Unit = - { - val defs = SameAPI.filterDefinitions(ds, topLevel, includePrivate) - hashSymmetric(defs, hashDefinition) - } - - /** - * Hashes a sequence of definitions by combining each definition's own - * hash with extra one supplied as first element of a pair. - * - * It's useful when one wants to influence hash of a definition by some - * external (to definition) factor (e.g. location of definition). - * - * NOTE: This method doesn't perform any filtering of passed definitions. - */ - def hashDefinitionsWithExtraHashes(ds: Seq[(Definition, Hash)]): Unit = - { - def hashDefinitionCombined(d: Definition, extraHash: Hash): Unit = { - hashDefinition(d) - extend(extraHash) - } - hashSymmetric(ds, (hashDefinitionCombined _).tupled) - } - def hashDefinition(d: Definition): Unit = { - hashString(d.name) - hashAnnotations(d.annotations) - hashModifiers(d.modifiers) - hashAccess(d.access) - d match { - case c: ClassLike => hashClass(c) - case f: FieldLike => hashField(f) - case d: Def => hashDef(d) - case t: TypeDeclaration => hashTypeDeclaration(t) - case t: TypeAlias => hashTypeAlias(t) - } - } - final def hashClass(c: ClassLike): Unit = visit(visitedClassLike, c)(hashClass0) - def hashClass0(c: ClassLike): Unit = { - extend(ClassHash) - hashParameterizedDefinition(c) - hashType(c.selfType) - hashStructure(c.structure, includeDefinitions, isTrait(c)) - } - def hashField(f: FieldLike): Unit = { - f match { - case v: Var => extend(VarHash) - case v: Val => extend(ValHash) - } - hashType(f.tpe) - } - def hashDef(d: Def): Unit = { - extend(DefHash) - hashParameterizedDefinition(d) - hashValueParameters(d.valueParameters) - hashType(d.returnType) - } - def hashAccess(a: Access): Unit = - a match { - case pub: Public => extend(PublicHash) - case qual: Qualified => hashQualified(qual) - } - def hashQualified(qual: Qualified): Unit = - { - qual match { - case p: Protected => extend(ProtectedHash) - case p: Private => extend(PrivateHash) - } - hashQualifier(qual.qualifier) - } - def hashQualifier(qual: Qualifier): Unit = - qual match { - case _: Unqualified => extend(UnqualifiedHash) - case _: ThisQualifier => extend(ThisQualifierHash) - case id: IdQualifier => - extend(IdQualifierHash) - hashString(id.value) - } - - def hashValueParameters(valueParameters: Seq[ParameterList]) = hashSeq(valueParameters, hashValueParameterList) - def hashValueParameterList(list: ParameterList) = - { - extend(ValueParamsHash) - hashBoolean(list.isImplicit) - hashSeq(list.parameters, hashValueParameter) - } - def hashValueParameter(parameter: MethodParameter) = - { - hashString(parameter.name) - hashType(parameter.tpe) - extend(parameter.modifier.ordinal) - hashBoolean(parameter.hasDefault) - } - - def hashParameterizedDefinition[T <: ParameterizedDefinition](d: T): Unit = { - hashTypeParameters(d.typeParameters) - } - def hashTypeDeclaration(d: TypeDeclaration): Unit = { - extend(TypeDeclHash) - hashParameterizedDefinition(d) - hashType(d.lowerBound) - hashType(d.upperBound) - } - def hashTypeAlias(d: TypeAlias): Unit = { - extend(TypeAliasHash) - hashParameterizedDefinition(d) - hashType(d.tpe) - } - - def hashTypeParameters(parameters: Seq[TypeParameter]) = hashSeq(parameters, hashTypeParameter) - def hashTypeParameter(parameter: TypeParameter): Unit = { - hashString(parameter.id) - extend(parameter.variance.ordinal) - hashTypeParameters(parameter.typeParameters) - hashType(parameter.lowerBound) - hashType(parameter.upperBound) - hashAnnotations(parameter.annotations) - } - def hashAnnotations(annotations: Seq[Annotation]) = hashSeq(annotations, hashAnnotation) - def hashAnnotation(annotation: Annotation) = - { - hashType(annotation.base) - hashAnnotationArguments(annotation.arguments) - } - def hashAnnotationArguments(args: Seq[AnnotationArgument]) = hashSeq(args, hashAnnotationArgument) - def hashAnnotationArgument(arg: AnnotationArgument): Unit = { - hashString(arg.name) - hashString(arg.value) - } - - def hashTypes(ts: Seq[Type], includeDefinitions: Boolean = true) = - hashSeq(ts, (t: Type) => hashType(t, includeDefinitions)) - def hashType(t: Type, includeDefinitions: Boolean = true): Unit = - t match { - case s: Structure => hashStructure(s, includeDefinitions, isTrait = false) - case e: Existential => hashExistential(e) - case c: Constant => hashConstant(c) - case p: Polymorphic => hashPolymorphic(p) - case a: Annotated => hashAnnotated(a) - case p: Parameterized => hashParameterized(p) - case p: Projection => hashProjection(p) - case _: EmptyType => extend(EmptyTypeHash) - case s: Singleton => hashSingleton(s) - case pr: ParameterRef => hashParameterRef(pr) - } - - def hashParameterRef(p: ParameterRef): Unit = { - extend(ParameterRefHash) - hashString(p.id) - } - def hashSingleton(s: Singleton): Unit = { - extend(SingletonHash) - hashPath(s.path) - } - def hashPath(path: Path) = hashSeq(path.components, hashPathComponent) - def hashPathComponent(pc: PathComponent) = pc match { - case _: This => extend(ThisPathHash) - case s: Super => hashSuperPath(s) - case id: Id => hashIdPath(id) - } - def hashSuperPath(s: Super): Unit = { - extend(SuperHash) - hashPath(s.qualifier) - } - def hashIdPath(id: Id): Unit = { - extend(IdPathHash) - hashString(id.id) - } - - def hashConstant(c: Constant) = - { - extend(ConstantHash) - hashString(c.value) - hashType(c.baseType) - } - def hashExistential(e: Existential) = - { - extend(ExistentialHash) - hashParameters(e.clause, e.baseType) - } - def hashPolymorphic(p: Polymorphic) = - { - extend(PolymorphicHash) - hashParameters(p.parameters, p.baseType) - } - def hashProjection(p: Projection) = - { - extend(ProjectionHash) - hashString(p.id) - hashType(p.prefix) - } - def hashParameterized(p: Parameterized): Unit = { - extend(ParameterizedHash) - hashType(p.baseType) - hashTypes(p.typeArguments) - } - def hashAnnotated(a: Annotated): Unit = { - extend(AnnotatedHash) - hashType(a.baseType) - hashAnnotations(a.annotations) - } - @deprecated("Use the overload that indicates if the definition is a trait.", "0.14") - final def hashStructure(structure: Structure, includeDefinitions: Boolean): Unit = - hashStructure(structure, includeDefinitions, isTrait = false) - final def hashStructure(structure: Structure, includeDefinitions: Boolean, isTrait: Boolean = false): Unit = - visit(visitedStructures, structure)(structure => hashStructure0(structure, includeDefinitions, isTrait)) - @deprecated("Use the overload that indicates if the definition is a trait.", "0.14") - def hashStructure0(structure: Structure, includeDefinitions: Boolean): Unit = - hashStructure0(structure, includeDefinitions, isTrait = false) - def hashStructure0(structure: Structure, includeDefinitions: Boolean, isTrait: Boolean = false): Unit = { - extend(StructureHash) - hashTypes(structure.parents, includeDefinitions) - if (includeDefinitions || isTrait) { - hashDefinitions(structure.declared, isTrait) - hashDefinitions(structure.inherited, isTrait) - } - } - def hashParameters(parameters: Seq[TypeParameter], base: Type): Unit = - { - hashTypeParameters(parameters) - hashType(base) - } -} diff --git a/compile/api/src/main/scala/xsbt/api/NameHashing.scala b/compile/api/src/main/scala/xsbt/api/NameHashing.scala deleted file mode 100644 index 60221e22f..000000000 --- a/compile/api/src/main/scala/xsbt/api/NameHashing.scala +++ /dev/null @@ -1,147 +0,0 @@ -package xsbt.api - -import xsbti.api.SourceAPI -import xsbti.api.Definition -import xsbti.api.DefinitionType -import xsbti.api.ClassLike -import xsbti.api._internalOnly_NameHash -import xsbti.api._internalOnly_NameHashes -import xsbti.api.DefinitionType.ClassDef -import xsbti.api.DefinitionType.Module -import xsbti.api.DefinitionType.PackageModule -import xsbti.api.DefinitionType.Trait - -/** - * A class that computes hashes for each group of definitions grouped by a simple name. - * - * See `nameHashes` method for details. - */ -class NameHashing { - - import NameHashing._ - - /** - * This method takes an API representation and extracts a flat collection of all - * definitions contained in that API representation. Then it groups definition - * by a simple name. Lastly, it computes a hash sum of all definitions in a single - * group. - * - * NOTE: The hashing sum used for hashing a group of definition is insensitive - * to order of definitions. - */ - def nameHashes(source: SourceAPI): _internalOnly_NameHashes = { - val apiPublicDefs = publicDefs(source) - val (regularDefs, implicitDefs) = apiPublicDefs.partition(locDef => !locDef.definition.modifiers.isImplicit) - val regularNameHashes = nameHashesForLocatedDefinitions(regularDefs) - val implicitNameHashes = nameHashesForLocatedDefinitions(implicitDefs) - new _internalOnly_NameHashes(regularNameHashes.toArray, implicitNameHashes.toArray) - } - - private def nameHashesForLocatedDefinitions(locatedDefs: Iterable[LocatedDefinition]): Iterable[_internalOnly_NameHash] = { - val groupedBySimpleName = locatedDefs.groupBy(locatedDef => localName(locatedDef.definition.name)) - val hashes = groupedBySimpleName.mapValues(hashLocatedDefinitions) - hashes.toIterable.map({ case (name: String, hash: Int) => new _internalOnly_NameHash(name, hash) }) - } - - private def hashLocatedDefinitions(locatedDefs: Iterable[LocatedDefinition]): Int = { - val defsWithExtraHashes = locatedDefs.toSeq.map(ld => ld.definition -> ld.location.hashCode) - xsbt.api.HashAPI.hashDefinitionsWithExtraHashes(defsWithExtraHashes) - } - - /** - * A visitor that visits given API object and extracts all nested public - * definitions it finds. The extracted definitions have Location attached - * to them which identifies API object's location. - * - * The returned location is basically a path to a definition that contains - * the located definition. For example, if we have: - * - * object Foo { - * class Bar { def abc: Int } - * } - * - * then location of `abc` is Seq((TermName, Foo), (TypeName, Bar)) - */ - private class ExtractPublicDefinitions extends Visit { - val locatedDefs = scala.collection.mutable.Buffer[LocatedDefinition]() - private var currentLocation: Location = Location() - override def visitAPI(s: SourceAPI): Unit = { - s.packages foreach visitPackage - s.definitions foreach { - case topLevelDef: ClassLike => - val packageName = { - val fullName = topLevelDef.name() - val lastDotIndex = fullName.lastIndexOf('.') - if (lastDotIndex <= 0) "" else fullName.substring(0, lastDotIndex - 1) - } - currentLocation = packageAsLocation(packageName) - visitDefinition(topLevelDef) - } - } - override def visitDefinition(d: Definition): Unit = { - val locatedDef = LocatedDefinition(currentLocation, d) - locatedDefs += locatedDef - d match { - case cl: xsbti.api.ClassLike => - val savedLocation = currentLocation - currentLocation = classLikeAsLocation(currentLocation, cl) - super.visitDefinition(d) - currentLocation = savedLocation - case _ => - super.visitDefinition(d) - } - } - } - - private def publicDefs(source: SourceAPI): Iterable[LocatedDefinition] = { - val visitor = new ExtractPublicDefinitions - visitor.visitAPI(source) - visitor.locatedDefs - } - - private def localName(name: String): String = { - // when there's no dot in name `lastIndexOf` returns -1 so we handle - // that case properly - val index = name.lastIndexOf('.') + 1 - name.substring(index) - } - - private def packageAsLocation(pkg: String): Location = if (pkg != "") { - val selectors = pkg.split('.').map(name => Selector(name, TermName)).toSeq - Location(selectors: _*) - } else Location.Empty - - private def classLikeAsLocation(prefix: Location, cl: ClassLike): Location = { - val selector = { - val clNameType = NameType(cl.definitionType) - Selector(localName(cl.name), clNameType) - } - Location((prefix.selectors :+ selector): _*) - } -} - -object NameHashing { - private case class LocatedDefinition(location: Location, definition: Definition) - /** - * Location is expressed as sequence of annotated names. The annotation denotes - * a type of a name, i.e. whether it's a term name or type name. - * - * Using Scala compiler terminology, location is defined as a sequence of member - * selections that uniquely identify a given Symbol. - */ - private case class Location(selectors: Selector*) - private object Location { - val Empty = Location(Seq.empty: _*) - } - private case class Selector(name: String, nameType: NameType) - private sealed trait NameType - private object NameType { - import DefinitionType._ - def apply(dt: DefinitionType): NameType = dt match { - case Trait | ClassDef => TypeName - case Module | PackageModule => TermName - } - } - private case object TermName extends NameType - private case object TypeName extends NameType -} diff --git a/compile/api/src/main/scala/xsbt/api/SameAPI.scala b/compile/api/src/main/scala/xsbt/api/SameAPI.scala deleted file mode 100644 index ebf03f5cd..000000000 --- a/compile/api/src/main/scala/xsbt/api/SameAPI.scala +++ /dev/null @@ -1,391 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010, 2011 Mark Harrah - */ -package xsbt.api - -import xsbti.api._ - -import Function.tupled -import scala.collection.{ immutable, mutable } - -@deprecated("This class is not used in incremental compiler and will be removed in next major version.", "0.13.2") -class NameChanges(val newTypes: Set[String], val removedTypes: Set[String], val newTerms: Set[String], val removedTerms: Set[String]) { - override def toString = - (("New types", newTypes) :: ("Removed types", removedTypes) :: ("New terms", newTerms) :: ("Removed terms", removedTerms) :: Nil).map { - case (label, set) => label + ":\n\t" + set.mkString("\n\t") - }.mkString("Name changes:\n ", "\n ", "\n") - -} - -object TopLevel { - @deprecated("The NameChanges class is deprecated and will be removed in next major version.", "0.13.2") - def nameChanges(a: Iterable[Source], b: Iterable[Source]): NameChanges = { - val api = (_: Source).api - apiNameChanges(a map api, b map api) - } - /** Identifies removed and new top-level definitions by name. */ - @deprecated("The NameChanges class is deprecated and will be removed in next major version.", "0.13.2") - def apiNameChanges(a: Iterable[SourceAPI], b: Iterable[SourceAPI]): NameChanges = - { - def changes(s: Set[String], t: Set[String]) = (s -- t, t -- s) - - val (avalues, atypes) = definitions(a) - val (bvalues, btypes) = definitions(b) - - val (newTypes, removedTypes) = changes(names(atypes), names(btypes)) - val (newTerms, removedTerms) = changes(names(avalues), names(bvalues)) - - new NameChanges(newTypes, removedTypes, newTerms, removedTerms) - } - def definitions(i: Iterable[SourceAPI]) = SameAPI.separateDefinitions(i.toSeq.flatMap(_.definitions)) - def names(s: Iterable[Definition]): Set[String] = Set() ++ s.map(_.name) -} - -/** Checks the API of two source files for equality.*/ -object SameAPI { - def apply(a: Source, b: Source): Boolean = - a.apiHash == b.apiHash && (a.hash.nonEmpty && b.hash.nonEmpty) && apply(a.api, b.api) - - def apply(a: Def, b: Def): Boolean = - (new SameAPI(false, true)).sameDefinitions(List(a), List(b), true) - - def apply(a: SourceAPI, b: SourceAPI): Boolean = - { - val start = System.currentTimeMillis - - /*println("\n=========== API #1 ================") - import DefaultShowAPI._ - println(ShowAPI.show(a)) - println("\n=========== API #2 ================") - println(ShowAPI.show(b))*/ - - val result = (new SameAPI(false, true)).check(a, b) - val end = System.currentTimeMillis - //println(" API comparison took: " + (end - start) / 1000.0 + " s") - result - } - - def separateDefinitions(s: Seq[Definition]): (Seq[Definition], Seq[Definition]) = - s.partition(isValueDefinition) - def isValueDefinition(d: Definition): Boolean = - d match { - case _: FieldLike | _: Def => true - case c: ClassLike => isValue(c.definitionType) - case _ => false - } - def isValue(d: DefinitionType): Boolean = - d == DefinitionType.Module || d == DefinitionType.PackageModule - /** Puts the given definitions in a map according to their names.*/ - def byName(s: Seq[Definition]): Map[String, List[Definition]] = - { - var map = Map[String, List[Definition]]() - for (d <- s; name = d.name) - map = map.updated(name, d :: map.getOrElse(name, Nil)) - map - } - - /** - * Removes definitions that should not be considered for API equality. - * All top-level definitions are always considered: 'private' only means package-private. - * Other definitions are considered if they are not qualified with 'private[this]' or 'private'. - */ - def filterDefinitions(d: Seq[Definition], topLevel: Boolean, includePrivate: Boolean) = if (topLevel || includePrivate) d else d.filter(isNonPrivate) - def isNonPrivate(d: Definition): Boolean = isNonPrivate(d.access) - /** Returns false if the `access` is `Private` and qualified, true otherwise.*/ - def isNonPrivate(access: Access): Boolean = - access match { - case p: Private if !p.qualifier.isInstanceOf[IdQualifier] => false - case _ => true - } -} -/** - * Used to implement API equality. - * - * If `includePrivate` is true, `private` and `private[this]` members are included in the comparison. Otherwise, those members are excluded. - */ -class SameAPI(includePrivate: Boolean, includeParamNames: Boolean) { - import SameAPI._ - - private val pending = new mutable.HashSet[AnyRef] - private[this] val debugEnabled = java.lang.Boolean.getBoolean("xsbt.api.debug") - def debug(flag: Boolean, msg: => String): Boolean = - { - if (debugEnabled && !flag) println(msg) - flag - } - - /** Returns true if source `a` has the same API as source `b`.*/ - def check(a: SourceAPI, b: SourceAPI): Boolean = - { - samePackages(a, b) && - debug(sameDefinitions(a, b), "Definitions differed") - } - - def samePackages(a: SourceAPI, b: SourceAPI): Boolean = - sameStrings(packages(a), packages(b)) - def packages(s: SourceAPI): Set[String] = - Set() ++ s.packages.map(_.name) - - def sameDefinitions(a: SourceAPI, b: SourceAPI): Boolean = - sameDefinitions(a.definitions, b.definitions, true) - def sameDefinitions(a: Seq[Definition], b: Seq[Definition], topLevel: Boolean): Boolean = - { - val (avalues, atypes) = separateDefinitions(filterDefinitions(a, topLevel, includePrivate)) - val (bvalues, btypes) = separateDefinitions(filterDefinitions(b, topLevel, includePrivate)) - debug(sameDefinitions(byName(avalues), byName(bvalues)), "Value definitions differed") && - debug(sameDefinitions(byName(atypes), byName(btypes)), "Type definitions differed") - } - def sameDefinitions(a: scala.collection.Map[String, List[Definition]], b: scala.collection.Map[String, List[Definition]]): Boolean = - debug(sameStrings(a.keySet, b.keySet), "\tDefinition strings differed (a: " + (a.keySet -- b.keySet) + ", b: " + (b.keySet -- a.keySet) + ")") && - zippedEntries(a, b).forall(tupled(sameNamedDefinitions)) - - /** - * Checks that the definitions in `a` are the same as those in `b`, ignoring order. - * Each list is assumed to have already been checked to have the same names (by `sameDefinitions`, for example). - */ - def sameNamedDefinitions(a: List[Definition], b: List[Definition]): Boolean = - { - def sameDefs(a: List[Definition], b: List[Definition]): Boolean = - { - a match { - case adef :: atail => - def sameDef(seen: List[Definition], remaining: List[Definition]): Boolean = - remaining match { - case Nil => debug(false, "Definition different in new API: \n" + adef.name) - case bdef :: btail => - val eq = sameDefinitionContent(adef, bdef) - if (eq) sameDefs(atail, seen ::: btail) else sameDef(bdef :: seen, btail) - } - sameDef(Nil, b) - case Nil => true - } - } - debug((a.length == b.length), "\t\tLength differed for " + a.headOption.map(_.name).getOrElse("empty")) && sameDefs(a, b) - } - - /** Checks that the two definitions are the same, other than their name.*/ - def sameDefinitionContent(a: Definition, b: Definition): Boolean = - samePending(a, b)(sameDefinitionContentDirect) - def sameDefinitionContentDirect(a: Definition, b: Definition): Boolean = - { - //a.name == b.name && - debug(sameAccess(a.access, b.access), "Access differed") && - debug(sameModifiers(a.modifiers, b.modifiers), "Modifiers differed") && - debug(sameAnnotations(a.annotations, b.annotations), "Annotations differed") && - debug(sameDefinitionSpecificAPI(a, b), "Definition-specific differed") - } - - def sameAccess(a: Access, b: Access): Boolean = - (a, b) match { - case (_: Public, _: Public) => true - case (qa: Protected, qb: Protected) => sameQualifier(qa, qb) - case (qa: Private, qb: Private) => sameQualifier(qa, qb) - case _ => debug(false, "Different access categories") - } - def sameQualifier(a: Qualified, b: Qualified): Boolean = - sameQualifier(a.qualifier, b.qualifier) - def sameQualifier(a: Qualifier, b: Qualifier): Boolean = - (a, b) match { - case (_: Unqualified, _: Unqualified) => true - case (_: ThisQualifier, _: ThisQualifier) => true - case (ia: IdQualifier, ib: IdQualifier) => debug(ia.value == ib.value, "Different qualifiers") - case _ => debug(false, "Different qualifier categories: " + a.getClass.getName + " -- " + b.getClass.getName) - } - - def sameModifiers(a: Modifiers, b: Modifiers): Boolean = - bitSet(a) == bitSet(b) - - def bitSet(m: Modifiers): immutable.BitSet = - { - import m._ - val bs = new mutable.BitSet - setIf(bs, isAbstract, 0) - setIf(bs, isOverride, 1) - setIf(bs, isFinal, 2) - setIf(bs, isSealed, 3) - setIf(bs, isImplicit, 4) - setIf(bs, isLazy, 5) - setIf(bs, isMacro, 6) - bs.toImmutable - } - def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit = - if (flag) bs += i - - def sameAnnotations(a: Seq[Annotation], b: Seq[Annotation]): Boolean = - sameSeq(a, b)(sameAnnotation) - def sameAnnotation(a: Annotation, b: Annotation): Boolean = - debug(sameType(a.base, b.base), "Annotation base type differed") && - debug(sameAnnotationArguments(a.arguments, b.arguments), "Annotation arguments differed (" + a + ") and (" + b + ")") - def sameAnnotationArguments(a: Seq[AnnotationArgument], b: Seq[AnnotationArgument]): Boolean = - argumentMap(a) == argumentMap(b) - def argumentMap(a: Seq[AnnotationArgument]): Map[String, String] = - Map() ++ a.map(arg => (arg.name, arg.value)) - - def sameDefinitionSpecificAPI(a: Definition, b: Definition): Boolean = - (a, b) match { - case (fa: FieldLike, fb: FieldLike) => sameFieldSpecificAPI(fa, fb) - case (pa: ParameterizedDefinition, pb: ParameterizedDefinition) => sameParameterizedDefinition(pa, pb) - case _ => false - } - - def sameParameterizedDefinition(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean = - debug(sameTypeParameters(a.typeParameters, b.typeParameters), "Different type parameters for " + a.name) && - sameParameterizedSpecificAPI(a, b) - - def sameParameterizedSpecificAPI(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean = - (a, b) match { - case (da: Def, db: Def) => sameDefSpecificAPI(da, db) - case (ca: ClassLike, cb: ClassLike) => sameClassLikeSpecificAPI(ca, cb) - case (ta: TypeAlias, tb: TypeAlias) => sameAliasSpecificAPI(ta, tb) - case (ta: TypeDeclaration, tb: TypeDeclaration) => sameDeclarationSpecificAPI(ta, tb) - case _ => false - } - - def sameDefSpecificAPI(a: Def, b: Def): Boolean = - debug(sameValueParameters(a.valueParameters, b.valueParameters), "Different def value parameters for " + a.name) && - debug(sameType(a.returnType, b.returnType), "Different def return type for " + a.name) - def sameAliasSpecificAPI(a: TypeAlias, b: TypeAlias): Boolean = - debug(sameType(a.tpe, b.tpe), "Different alias type for " + a.name) - def sameDeclarationSpecificAPI(a: TypeDeclaration, b: TypeDeclaration): Boolean = - debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound for declaration " + a.name) && - debug(sameType(a.upperBound, b.upperBound), "Different upper bound for declaration " + a.name) - def sameFieldSpecificAPI(a: FieldLike, b: FieldLike): Boolean = - debug(sameFieldCategory(a, b), "Different field categories (" + a.name + "=" + a.getClass.getName + " -- " + a.name + "=" + a.getClass.getName + ")") && - debug(sameType(a.tpe, b.tpe), "Different field type for " + a.name) - - def sameFieldCategory(a: FieldLike, b: FieldLike): Boolean = - (a, b) match { - case (_: Val, _: Val) => true - case (_: Var, _: Var) => true - case _ => false - } - - def sameClassLikeSpecificAPI(a: ClassLike, b: ClassLike): Boolean = - sameDefinitionType(a.definitionType, b.definitionType) && - sameType(a.selfType, b.selfType) && - sameStructure(a.structure, b.structure) - - def sameValueParameters(a: Seq[ParameterList], b: Seq[ParameterList]): Boolean = - sameSeq(a, b)(sameParameterList) - - def sameParameterList(a: ParameterList, b: ParameterList): Boolean = - (a.isImplicit == b.isImplicit) && - sameParameters(a.parameters, b.parameters) - def sameParameters(a: Seq[MethodParameter], b: Seq[MethodParameter]): Boolean = - sameSeq(a, b)(sameMethodParameter) - def sameMethodParameter(a: MethodParameter, b: MethodParameter): Boolean = - (!includeParamNames || a.name == b.name) && - sameType(a.tpe, b.tpe) && - (a.hasDefault == b.hasDefault) && - sameParameterModifier(a.modifier, b.modifier) - def sameParameterModifier(a: ParameterModifier, b: ParameterModifier) = - a == b - def sameDefinitionType(a: DefinitionType, b: DefinitionType): Boolean = - a == b - def sameVariance(a: Variance, b: Variance): Boolean = - a == b - - def sameTypeParameters(a: Seq[TypeParameter], b: Seq[TypeParameter]): Boolean = - debug(sameSeq(a, b)(sameTypeParameter), "Different type parameters") - def sameTypeParameter(a: TypeParameter, b: TypeParameter): Boolean = - { - sameTypeParameters(a.typeParameters, b.typeParameters) && - debug(sameAnnotations(a.annotations, b.annotations), "Different type parameter annotations") && - debug(sameVariance(a.variance, b.variance), "Different variance") && - debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound") && - debug(sameType(a.upperBound, b.upperBound), "Different upper bound") && - sameTags(a.id, b.id) - } - def sameTags(a: String, b: String): Boolean = - debug(a == b, "Different type parameter bindings: " + a + ", " + b) - - def sameType(a: Type, b: Type): Boolean = - samePending(a, b)(sameTypeDirect) - def sameTypeDirect(a: Type, b: Type): Boolean = - (a, b) match { - case (sa: SimpleType, sb: SimpleType) => debug(sameSimpleTypeDirect(sa, sb), "Different simple types: " + DefaultShowAPI(sa) + " and " + DefaultShowAPI(sb)) - case (ca: Constant, cb: Constant) => debug(sameConstantType(ca, cb), "Different constant types: " + DefaultShowAPI(ca) + " and " + DefaultShowAPI(cb)) - case (aa: Annotated, ab: Annotated) => debug(sameAnnotatedType(aa, ab), "Different annotated types") - case (sa: Structure, sb: Structure) => debug(sameStructureDirect(sa, sb), "Different structure type") - case (ea: Existential, eb: Existential) => debug(sameExistentialType(ea, eb), "Different existential type") - case (pa: Polymorphic, pb: Polymorphic) => debug(samePolymorphicType(pa, pb), "Different polymorphic type") - case _ => differentCategory("type", a, b) - } - - def sameConstantType(ca: Constant, cb: Constant): Boolean = - sameType(ca.baseType, cb.baseType) && - ca.value == cb.value - def sameExistentialType(a: Existential, b: Existential): Boolean = - sameTypeParameters(a.clause, b.clause) && - sameType(a.baseType, b.baseType) - def samePolymorphicType(a: Polymorphic, b: Polymorphic): Boolean = - sameTypeParameters(a.parameters, b.parameters) && - sameType(a.baseType, b.baseType) - def sameAnnotatedType(a: Annotated, b: Annotated): Boolean = - sameType(a.baseType, b.baseType) && - sameAnnotations(a.annotations, b.annotations) - def sameStructure(a: Structure, b: Structure): Boolean = - samePending(a, b)(sameStructureDirect) - - private[this] def samePending[T](a: T, b: T)(f: (T, T) => Boolean): Boolean = - if (pending add ((a, b))) f(a, b) else true - - def sameStructureDirect(a: Structure, b: Structure): Boolean = - { - sameSeq(a.parents, b.parents)(sameType) && - sameMembers(a.declared, b.declared) && - sameMembers(a.inherited, b.inherited) - } - - def sameMembers(a: Seq[Definition], b: Seq[Definition]): Boolean = - sameDefinitions(a, b, false) - - def sameSimpleType(a: SimpleType, b: SimpleType): Boolean = - samePending(a, b)(sameSimpleTypeDirect) - def sameSimpleTypeDirect(a: SimpleType, b: SimpleType): Boolean = - (a, b) match { - case (pa: Projection, pb: Projection) => debug(sameProjection(pa, pb), "Different projection") - case (pa: ParameterRef, pb: ParameterRef) => debug(sameParameterRef(pa, pb), "Different parameter ref") - case (sa: Singleton, sb: Singleton) => debug(sameSingleton(sa, sb), "Different singleton") - case (_: EmptyType, _: EmptyType) => true - case (pa: Parameterized, pb: Parameterized) => debug(sameParameterized(pa, pb), "Different parameterized") - case _ => differentCategory("simple type", a, b) - } - def differentCategory(label: String, a: AnyRef, b: AnyRef): Boolean = - debug(false, "Different category of " + label + " (" + a.getClass.getName + " and " + b.getClass.getName + ") for (" + a + " and " + b + ")") - - def sameParameterized(a: Parameterized, b: Parameterized): Boolean = - sameSimpleType(a.baseType, b.baseType) && - sameSeq(a.typeArguments, b.typeArguments)(sameType) - def sameParameterRef(a: ParameterRef, b: ParameterRef): Boolean = sameTags(a.id, b.id) - def sameSingleton(a: Singleton, b: Singleton): Boolean = - samePath(a.path, b.path) - def sameProjection(a: Projection, b: Projection): Boolean = - sameSimpleType(a.prefix, b.prefix) && - (a.id == b.id) - - def samePath(a: Path, b: Path): Boolean = - samePathComponents(a.components, b.components) - def samePathComponents(a: Seq[PathComponent], b: Seq[PathComponent]): Boolean = - sameSeq(a, b)(samePathComponent) - def samePathComponent(a: PathComponent, b: PathComponent): Boolean = - (a, b) match { - case (_: This, _: This) => true - case (sa: Super, sb: Super) => samePathSuper(sa, sb) - case (ia: Id, ib: Id) => samePathId(ia, ib) - case _ => false - } - def samePathSuper(a: Super, b: Super): Boolean = - samePath(a.qualifier, b.qualifier) - def samePathId(a: Id, b: Id): Boolean = - a.id == b.id - - // precondition: a.keySet == b.keySet - protected def zippedEntries[A, B](a: scala.collection.Map[A, B], b: scala.collection.Map[A, B]): Iterable[(B, B)] = - for ((key, avalue) <- a) yield (avalue, b(key)) - - def sameStrings(a: scala.collection.Set[String], b: scala.collection.Set[String]): Boolean = - a == b - final def sameSeq[T](a: Seq[T], b: Seq[T])(eq: (T, T) => Boolean): Boolean = - (a.length == b.length) && (a zip b).forall(tupled(eq)) -} \ No newline at end of file diff --git a/compile/api/src/main/scala/xsbt/api/ShowAPI.scala b/compile/api/src/main/scala/xsbt/api/ShowAPI.scala deleted file mode 100644 index 4412be75d..000000000 --- a/compile/api/src/main/scala/xsbt/api/ShowAPI.scala +++ /dev/null @@ -1,288 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - -import xsbti.api._ - -trait Show[A] { - def show(a: A): String -} - -final class ShowLazy[A](delegate: => Show[A]) extends Show[A] { - private lazy val s = delegate - def show(a: A) = s.show(a) -} - -import ShowAPI._ - -object ShowAPI { - def Show[T](implicit s: Show[T]): Show[T] = s - def show[T](t: T)(implicit s: Show[T]): String = s.show(t) - - def bounds(lower: Type, upper: Type)(implicit t: Show[Type]): String = - ">: " + t.show(lower) + " <: " + t.show(upper) - - import ParameterModifier._ - def parameterModifier(base: String, pm: ParameterModifier): String = - pm match { - case Plain => base - case Repeated => base + "*" - case ByName => "=> " + base - } - - def concat[A](list: Seq[A], as: Show[A], sep: String): String = mapSeq(list, as).mkString(sep) - def commas[A](list: Seq[A], as: Show[A]): String = concat(list, as, ", ") - def spaced[A](list: Seq[A], as: Show[A]): String = concat(list, as, " ") - def lines[A](list: Seq[A], as: Show[A]): String = mapSeq(list, as).mkString("\n") - def mapSeq[A](list: Seq[A], as: Show[A]): Seq[String] = list.map(as.show) -} - -trait ShowBase { - implicit def showAnnotation(implicit as: Show[AnnotationArgument], t: Show[Type]): Show[Annotation] = - new Show[Annotation] { def show(a: Annotation) = "@" + t.show(a.base) + (if (a.arguments.isEmpty) "" else "(" + commas(a.arguments, as) + ")") } - - implicit def showAnnotationArgument: Show[AnnotationArgument] = - new Show[AnnotationArgument] { def show(a: AnnotationArgument) = a.name + " = " + a.value } - - import Variance._ - implicit def showVariance: Show[Variance] = - new Show[Variance] { def show(v: Variance) = v match { case Invariant => ""; case Covariant => "+"; case Contravariant => "-" } } - - implicit def showSource(implicit ps: Show[Package], ds: Show[Definition]): Show[SourceAPI] = - new Show[SourceAPI] { def show(a: SourceAPI) = lines(a.packages, ps) + "\n" + lines(a.definitions, ds) } - - implicit def showPackage: Show[Package] = - new Show[Package] { def show(pkg: Package) = "package " + pkg.name } - - implicit def showAccess(implicit sq: Show[Qualified]): Show[Access] = - new Show[Access] { - def show(a: Access) = - a match { - case p: Public => "" - case q: Qualified => sq.show(q) - } - } - implicit def showQualified(implicit sq: Show[Qualifier]): Show[Qualified] = - new Show[Qualified] { - def show(q: Qualified) = - ((q match { - case p: Protected => "protected" - case p: Private => "private" - }) - + sq.show(q.qualifier)) - } - implicit def showQualifier: Show[Qualifier] = - new Show[Qualifier] { - def show(q: Qualifier) = - q match { - case _: Unqualified => "" - case _: ThisQualifier => "[this]" - case i: IdQualifier => "[" + i.value + "]" - } - } - implicit def showModifiers: Show[Modifiers] = - new Show[Modifiers] { - def show(m: Modifiers) = - { - val mods = - (m.isOverride, "override") :: - (m.isFinal, "final") :: - (m.isSealed, "sealed") :: - (m.isImplicit, "implicit") :: - (m.isAbstract, "abstract") :: - (m.isLazy, "lazy") :: - Nil - mods.filter(_._1).map(_._2).mkString(" ") - } - } - - implicit def showDefinitionType: Show[DefinitionType] = - new Show[DefinitionType] { - import DefinitionType._ - def show(dt: DefinitionType) = - dt match { - case Trait => "trait" - case ClassDef => "class" - case Module => "object" - case PackageModule => "package object" - } - } -} -trait ShowDefinitions { - implicit def showVal(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Val] = - new Show[Val] { def show(v: Val) = definitionBase(v, "val")(acs, ms, ans) + ": " + t.show(v.tpe) } - - implicit def showVar(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Var] = - new Show[Var] { def show(v: Var) = definitionBase(v, "var")(acs, ms, ans) + ": " + t.show(v.tpe) } - - implicit def showDef(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], vp: Show[Seq[ParameterList]], t: Show[Type]): Show[Def] = - new Show[Def] { def show(d: Def) = parameterizedDef(d, "def")(acs, ms, ans, tp) + vp.show(d.valueParameters) + ": " + t.show(d.returnType) } - - implicit def showClassLike(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType], s: Show[Structure], t: Show[Type]): Show[ClassLike] = - new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) + " requires " + t.show(cl.selfType) + " extends " + s.show(cl.structure) } - - implicit def showTypeAlias(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeAlias] = - new Show[TypeAlias] { def show(ta: TypeAlias) = parameterizedDef(ta, "type")(acs, ms, ans, tp) + " = " + t.show(ta.tpe) } - - implicit def showTypeDeclaration(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeDeclaration] = - new Show[TypeDeclaration] { def show(td: TypeDeclaration) = parameterizedDef(td, "type")(acs, ms, ans, tp) + bounds(td.lowerBound, td.upperBound) } - def showClassLikeSimple(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType]): Show[ClassLike] = - new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) } - - def parameterizedDef(d: ParameterizedDefinition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]]): String = - definitionBase(d, label)(acs, ms, ans) + tp.show(d.typeParameters) - def definitionBase(d: Definition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation]): String = - space(spaced(d.annotations, ans)) + space(acs.show(d.access)) + space(ms.show(d.modifiers)) + space(label) + d.name - def space(s: String) = if (s.isEmpty) s else s + " " -} -trait ShowDefinition { - implicit def showDefinition(implicit vl: Show[Val], vr: Show[Var], ds: Show[Def], cl: Show[ClassLike], ta: Show[TypeAlias], td: Show[TypeDeclaration]): Show[Definition] = - new Show[Definition] { - def show(d: Definition) = - d match { - case v: Val => vl.show(v) - case v: Var => vr.show(v) - case d: Def => ds.show(d) - case c: ClassLike => cl.show(c) - case t: TypeAlias => ta.show(t) - case t: TypeDeclaration => td.show(t) - } - } -} -trait ShowType { - implicit def showType(implicit s: Show[SimpleType], a: Show[Annotated], st: Show[Structure], c: Show[Constant], e: Show[Existential], po: Show[Polymorphic]): Show[Type] = - new Show[Type] { - def show(t: Type) = - t match { - case q: SimpleType => s.show(q) - case q: Constant => c.show(q) - case q: Annotated => a.show(q) - case q: Structure => st.show(q) - case q: Existential => e.show(q) - case q: Polymorphic => po.show(q) - } - } - - implicit def showSimpleType(implicit pr: Show[Projection], pa: Show[ParameterRef], si: Show[Singleton], et: Show[EmptyType], p: Show[Parameterized]): Show[SimpleType] = - new Show[SimpleType] { - def show(t: SimpleType) = - t match { - case q: Projection => pr.show(q) - case q: ParameterRef => pa.show(q) - case q: Singleton => si.show(q) - case q: EmptyType => et.show(q) - case q: Parameterized => p.show(q) - } - } -} -trait ShowBasicTypes { - implicit def showSingleton(implicit p: Show[Path]): Show[Singleton] = - new Show[Singleton] { def show(s: Singleton) = p.show(s.path) } - implicit def showEmptyType: Show[EmptyType] = - new Show[EmptyType] { def show(e: EmptyType) = "" } - implicit def showParameterRef: Show[ParameterRef] = - new Show[ParameterRef] { def show(p: ParameterRef) = "<" + p.id + ">" } -} -trait ShowTypes { - implicit def showStructure(implicit t: Show[Type], d: Show[Definition]): Show[Structure] = - new Show[Structure] { - def show(s: Structure) = { - // don't show inherited class like definitions to avoid dealing with cycles - val safeInherited = s.inherited.filterNot(_.isInstanceOf[ClassLike]) - val showInherited: Show[Definition] = new Show[Definition] { - def show(deff: Definition): String = "^inherited^ " + d.show(deff) - } - concat(s.parents, t, " with ") + "\n{\n" + lines(safeInherited, showInherited) + "\n" + lines(s.declared, d) + "\n}" - } - } - implicit def showAnnotated(implicit as: Show[Annotation], t: Show[Type]): Show[Annotated] = - new Show[Annotated] { def show(a: Annotated) = spaced(a.annotations, as) + " " + t.show(a.baseType) } - implicit def showProjection(implicit t: Show[SimpleType]): Show[Projection] = - new Show[Projection] { def show(p: Projection) = t.show(p.prefix) + "#" + p.id } - implicit def showParameterized(implicit t: Show[Type]): Show[Parameterized] = - new Show[Parameterized] { def show(p: Parameterized) = t.show(p.baseType) + mapSeq(p.typeArguments, t).mkString("[", ", ", "]") } - implicit def showConstant(implicit t: Show[Type]): Show[Constant] = - new Show[Constant] { def show(c: Constant) = t.show(c.baseType) + "(" + c.value + ")" } - implicit def showExistential(implicit t: Show[Type], tp: Show[TypeParameter]): Show[Existential] = - new Show[Existential] { - def show(e: Existential) = - t.show(e.baseType) + e.clause.map(t => "type " + tp.show(t)).mkString(" forSome { ", "; ", "}") - } - implicit def showPolymorphic(implicit t: Show[Type], tps: Show[Seq[TypeParameter]]): Show[Polymorphic] = - new Show[Polymorphic] { def show(p: Polymorphic) = t.show(p.baseType) + tps.show(p.parameters) } - -} - -trait ShowPath { - implicit def showPath(implicit pc: Show[PathComponent]): Show[Path] = - new Show[Path] { def show(p: Path) = mapSeq(p.components, pc).mkString(".") } - - implicit def showPathComponent(implicit sp: Show[Path]): Show[PathComponent] = - new Show[PathComponent] { - def show(p: PathComponent) = - p match { - case s: Super => "super[" + sp.show(s.qualifier) + "]" - case _: This => "this" - case i: Id => i.id - } - } -} - -trait ShowValueParameters { - implicit def showParameterLists(implicit pl: Show[ParameterList]): Show[Seq[ParameterList]] = - new Show[Seq[ParameterList]] { def show(p: Seq[ParameterList]) = concat(p, pl, "") } - implicit def showParameterList(implicit mp: Show[MethodParameter]): Show[ParameterList] = - new Show[ParameterList] { def show(pl: ParameterList) = "(" + (if (pl.isImplicit) "implicit " else "") + commas(pl.parameters, mp) + ")" } - - implicit def showMethodParameter(implicit t: Show[Type]): Show[MethodParameter] = - new Show[MethodParameter] { - def show(mp: MethodParameter) = - mp.name + ": " + parameterModifier(t.show(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "") - } -} -trait ShowTypeParameters { - implicit def showTypeParameters(implicit as: Show[TypeParameter]): Show[Seq[TypeParameter]] = - new Show[Seq[TypeParameter]] { def show(tps: Seq[TypeParameter]) = if (tps.isEmpty) "" else mapSeq(tps, as).mkString("[", ",", "]") } - implicit def showTypeParameter(implicit as: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type], v: Show[Variance]): Show[TypeParameter] = - new Show[TypeParameter] { - def show(tps: TypeParameter) = - spaced(tps.annotations, as) + " " + v.show(tps.variance) + tps.id + tp.show(tps.typeParameters) + " " + bounds(tps.lowerBound, tps.upperBound) - } -} - -// this class is a hack to resolve some diverging implicit errors. -// I'm pretty sure the cause is the Show[Seq[T]] dominating Show[X] issue. -// It could probably be reduced a bit if that is the case (below was trial and error) -object DefaultShowAPI extends ShowBase with ShowBasicTypes with ShowValueParameters { - def apply(d: Definition) = ShowAPI.show(d) - def apply(d: Type) = ShowAPI.show(d) - - implicit lazy val showVal: Show[Val] = Cyclic.showVal - implicit lazy val showVar: Show[Var] = Cyclic.showVar - implicit lazy val showClassLike: Show[ClassLike] = Cyclic.showClassLike - implicit lazy val showTypeDeclaration: Show[TypeDeclaration] = Cyclic.showTypeDeclaration - implicit lazy val showTypeAlias: Show[TypeAlias] = Cyclic.showTypeAlias - implicit lazy val showDef: Show[Def] = Cyclic.showDef - - implicit lazy val showProj: Show[Projection] = Cyclic.showProjection - implicit lazy val showPoly: Show[Polymorphic] = Cyclic.showPolymorphic - - implicit lazy val showSimple: Show[SimpleType] = new ShowLazy(Cyclic.showSimpleType) - implicit lazy val showAnnotated: Show[Annotated] = Cyclic.showAnnotated - implicit lazy val showExistential: Show[Existential] = Cyclic.showExistential - implicit lazy val showConstant: Show[Constant] = Cyclic.showConstant - implicit lazy val showParameterized: Show[Parameterized] = Cyclic.showParameterized - - implicit lazy val showTypeParameters: Show[Seq[TypeParameter]] = new ShowLazy(Cyclic.showTypeParameters) - implicit lazy val showTypeParameter: Show[TypeParameter] = Cyclic.showTypeParameter - - implicit lazy val showDefinition: Show[Definition] = new ShowLazy(Cyclic.showDefinition) - implicit lazy val showType: Show[Type] = new ShowLazy(Cyclic.showType) - implicit lazy val showStructure: Show[Structure] = new ShowLazy(Cyclic.showStructure) - - implicit lazy val showPath: Show[Path] = new ShowLazy(Cyclic.showPath) - implicit lazy val showPathComponent: Show[PathComponent] = Cyclic.showPathComponent - - private object Cyclic extends ShowTypes with ShowType with ShowPath with ShowDefinition with ShowDefinitions with ShowTypeParameters -} \ No newline at end of file diff --git a/compile/api/src/main/scala/xsbt/api/Visit.scala b/compile/api/src/main/scala/xsbt/api/Visit.scala deleted file mode 100644 index f6e2d2058..000000000 --- a/compile/api/src/main/scala/xsbt/api/Visit.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - -import xsbti.api._ -import scala.collection.mutable - -class Visit { - private[this] val visitedStructures = new mutable.HashSet[Structure] - private[this] val visitedClassLike = new mutable.HashSet[ClassLike] - - def visit(s: Source): Unit = visitAPI(s.api) - def visitAPI(s: SourceAPI): Unit = - { - s.packages foreach visitPackage - s.definitions foreach visitDefinition - } - - def visitPackage(p: Package): Unit = { - visitString(p.name) - } - - def visitDefinitions(ds: Seq[Definition]) = ds foreach visitDefinition - def visitDefinition(d: Definition): Unit = { - visitString(d.name) - visitAnnotations(d.annotations) - visitModifiers(d.modifiers) - visitAccess(d.access) - d match { - case c: ClassLike => visitClass(c) - case f: FieldLike => visitField(f) - case d: Def => visitDef(d) - case t: TypeDeclaration => visitTypeDeclaration(t) - case t: TypeAlias => visitTypeAlias(t) - } - } - final def visitClass(c: ClassLike): Unit = if (visitedClassLike add c) visitClass0(c) - def visitClass0(c: ClassLike): Unit = { - visitParameterizedDefinition(c) - visitType(c.selfType) - visitStructure(c.structure) - } - def visitField(f: FieldLike): Unit = { - visitType(f.tpe) - f match { - case v: Var => visitVar(v) - case v: Val => visitVal(v) - } - } - def visitVar(v: Var): Unit = () - def visitVal(v: Val): Unit = () - def visitDef(d: Def): Unit = { - visitParameterizedDefinition(d) - visitValueParameters(d.valueParameters) - visitType(d.returnType) - } - def visitAccess(a: Access): Unit = - a match { - case pub: Public => visitPublic(pub) - case qual: Qualified => visitQualified(qual) - } - def visitQualified(qual: Qualified): Unit = - qual match { - case p: Protected => visitProtected(p) - case p: Private => visitPrivate(p) - } - def visitQualifier(qual: Qualifier): Unit = - qual match { - case unq: Unqualified => visitUnqualified(unq) - case thisq: ThisQualifier => visitThisQualifier(thisq) - case id: IdQualifier => visitIdQualifier(id) - } - def visitIdQualifier(id: IdQualifier): Unit = { - visitString(id.value) - } - def visitUnqualified(unq: Unqualified): Unit = () - def visitThisQualifier(thisq: ThisQualifier): Unit = () - def visitPublic(pub: Public): Unit = () - def visitPrivate(p: Private): Unit = visitQualifier(p.qualifier) - def visitProtected(p: Protected): Unit = visitQualifier(p.qualifier) - def visitModifiers(m: Modifiers): Unit = () - - def visitValueParameters(valueParameters: Seq[ParameterList]) = valueParameters foreach visitValueParameterList - def visitValueParameterList(list: ParameterList) = list.parameters foreach visitValueParameter - def visitValueParameter(parameter: MethodParameter) = - { - visitString(parameter.name) - visitType(parameter.tpe) - } - - def visitParameterizedDefinition[T <: ParameterizedDefinition](d: T): Unit = visitTypeParameters(d.typeParameters) - - def visitTypeDeclaration(d: TypeDeclaration): Unit = { - visitParameterizedDefinition(d) - visitType(d.lowerBound) - visitType(d.upperBound) - } - def visitTypeAlias(d: TypeAlias): Unit = { - visitParameterizedDefinition(d) - visitType(d.tpe) - } - - def visitTypeParameters(parameters: Seq[TypeParameter]) = parameters foreach visitTypeParameter - def visitTypeParameter(parameter: TypeParameter): Unit = { - visitTypeParameters(parameter.typeParameters) - visitType(parameter.lowerBound) - visitType(parameter.upperBound) - visitAnnotations(parameter.annotations) - } - def visitAnnotations(annotations: Seq[Annotation]) = annotations foreach visitAnnotation - def visitAnnotation(annotation: Annotation) = - { - visitType(annotation.base) - visitAnnotationArguments(annotation.arguments) - } - def visitAnnotationArguments(args: Seq[AnnotationArgument]) = args foreach visitAnnotationArgument - def visitAnnotationArgument(arg: AnnotationArgument): Unit = { - visitString(arg.name) - visitString(arg.value) - } - - def visitTypes(ts: Seq[Type]) = ts.foreach(visitType) - def visitType(t: Type): Unit = { - t match { - case s: Structure => visitStructure(s) - case e: Existential => visitExistential(e) - case c: Constant => visitConstant(c) - case p: Polymorphic => visitPolymorphic(p) - case a: Annotated => visitAnnotated(a) - case p: Parameterized => visitParameterized(p) - case p: Projection => visitProjection(p) - case _: EmptyType => visitEmptyType() - case s: Singleton => visitSingleton(s) - case pr: ParameterRef => visitParameterRef(pr) - } - } - - def visitEmptyType(): Unit = () - def visitParameterRef(p: ParameterRef): Unit = () - def visitSingleton(s: Singleton): Unit = visitPath(s.path) - def visitPath(path: Path) = path.components foreach visitPathComponent - def visitPathComponent(pc: PathComponent) = pc match { - case t: This => visitThisPath(t) - case s: Super => visitSuperPath(s) - case id: Id => visitIdPath(id) - } - def visitThisPath(t: This): Unit = () - def visitSuperPath(s: Super): Unit = visitPath(s.qualifier) - def visitIdPath(id: Id): Unit = visitString(id.id) - - def visitConstant(c: Constant) = - { - visitString(c.value) - visitType(c.baseType) - } - def visitExistential(e: Existential) = visitParameters(e.clause, e.baseType) - def visitPolymorphic(p: Polymorphic) = visitParameters(p.parameters, p.baseType) - def visitProjection(p: Projection) = - { - visitString(p.id) - visitType(p.prefix) - } - def visitParameterized(p: Parameterized): Unit = { - visitType(p.baseType) - visitTypes(p.typeArguments) - } - def visitAnnotated(a: Annotated): Unit = { - visitType(a.baseType) - visitAnnotations(a.annotations) - } - final def visitStructure(structure: Structure) = if (visitedStructures add structure) visitStructure0(structure) - def visitStructure0(structure: Structure): Unit = { - visitTypes(structure.parents) - visitDefinitions(structure.declared) - visitDefinitions(structure.inherited) - } - def visitParameters(parameters: Seq[TypeParameter], base: Type): Unit = - { - visitTypeParameters(parameters) - visitType(base) - } - def visitString(s: String): Unit = () -} diff --git a/compile/api/src/main/scala/xsbti/SafeLazy.scala b/compile/api/src/main/scala/xsbti/SafeLazy.scala deleted file mode 100644 index 6809e9402..000000000 --- a/compile/api/src/main/scala/xsbti/SafeLazy.scala +++ /dev/null @@ -1,24 +0,0 @@ -// needs to be in xsbti package (or a subpackage) to pass through the filter in DualLoader -// and be accessible to the compiler-side interface -package xsbti - -object SafeLazy { - def apply[T <: AnyRef](eval: xsbti.F0[T]): xsbti.api.Lazy[T] = - apply(eval()) - def apply[T <: AnyRef](eval: => T): xsbti.api.Lazy[T] = - fromFunction0(eval _) - def fromFunction0[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = - new Impl(eval) - - def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = apply(value) - - private[this] final class Impl[T <: AnyRef](private[this] var eval: () => T) extends xsbti.api.AbstractLazy[T] { - private[this] lazy val _t = - { - val t = eval() - eval = null // clear the reference, ensuring the only memory we hold onto is the result - t - } - def get: T = _t - } -} \ No newline at end of file diff --git a/compile/api/src/test/scala/xsbt/api/NameHashingSpecification.scala b/compile/api/src/test/scala/xsbt/api/NameHashingSpecification.scala deleted file mode 100644 index 6014a1e52..000000000 --- a/compile/api/src/test/scala/xsbt/api/NameHashingSpecification.scala +++ /dev/null @@ -1,327 +0,0 @@ -package xsbt.api - -import org.junit.runner.RunWith -import xsbti.api._ -import org.specs2.mutable.Specification -import org.specs2.runner.JUnitRunner - -@RunWith(classOf[JUnitRunner]) -class NameHashingSpecification extends Specification { - - /** - * Very basic test which checks whether a name hash is insensitive to - * definition order (across the whole compilation unit). - */ - "new member" in { - val nameHashing = new NameHashing - val def1 = new Def(Array.empty, strTpe, Array.empty, "foo", publicAccess, defaultModifiers, Array.empty) - val def2 = new Def(Array.empty, intTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - val classBar1 = simpleClass("Bar", def1) - val classBar2 = simpleClass("Bar", def1, def2) - val api1 = new SourceAPI(Array.empty, Array(classBar1)) - val api2 = new SourceAPI(Array.empty, Array(classBar2)) - val nameHashes1 = nameHashing.nameHashes(api1) - val nameHashes2 = nameHashing.nameHashes(api2) - assertNameHashEqualForRegularName("Bar", nameHashes1, nameHashes2) - assertNameHashEqualForRegularName("foo", nameHashes1, nameHashes2) - nameHashes1.regularMembers.map(_.name).toSeq must not contain ("bar") - nameHashes2.regularMembers.map(_.name).toSeq must contain("bar") - } - - /** - * Very basic test which checks whether a name hash is insensitive to - * definition order (across the whole compilation unit). - */ - "definition order" in { - val nameHashing = new NameHashing - val def1 = new Def(Array.empty, intTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - val def2 = new Def(Array.empty, strTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - val nestedBar1 = simpleClass("Bar1", def1) - val nestedBar2 = simpleClass("Bar2", def2) - val classA = simpleClass("Foo", nestedBar1, nestedBar2) - val classB = simpleClass("Foo", nestedBar2, nestedBar1) - val api1 = new SourceAPI(Array.empty, Array(classA)) - val api2 = new SourceAPI(Array.empty, Array(classB)) - val nameHashes1 = nameHashing.nameHashes(api1) - val nameHashes2 = nameHashing.nameHashes(api2) - val def1Hash = HashAPI(def1) - val def2Hash = HashAPI(def2) - def1Hash !=== def2Hash - nameHashes1 === nameHashes2 - } - - /** - * Very basic test which asserts that a name hash is sensitive to definition location. - * - * For example, if we have: - * // Foo1.scala - * class Foo { def xyz: Int = ... } - * object Foo - * - * and: - * // Foo2.scala - * class Foo - * object Foo { def xyz: Int = ... } - * - * then hash for `xyz` name should differ in those two cases - * because method `xyz` was moved from class to an object. - */ - "definition location" in { - val nameHashing = new NameHashing - val deff = new Def(Array.empty, intTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - val classA = { - val nestedBar1 = simpleClass("Bar1", deff) - val nestedBar2 = simpleClass("Bar2") - simpleClass("Foo", nestedBar1, nestedBar2) - } - val classB = { - val nestedBar1 = simpleClass("Bar1") - val nestedBar2 = simpleClass("Bar2", deff) - simpleClass("Foo", nestedBar1, nestedBar2) - } - val api1 = new SourceAPI(Array.empty, Array(classA)) - val api2 = new SourceAPI(Array.empty, Array(classB)) - val nameHashes1 = nameHashing.nameHashes(api1) - val nameHashes2 = nameHashing.nameHashes(api2) - nameHashes1 !=== nameHashes2 - } - - /** - * Test if members introduced in parent class affect hash of a name - * of a child class. - * - * For example, if we have: - * // Test1.scala - * class Parent - * class Child extends Parent - * - * and: - * // Test2.scala - * class Parent { def bar: Int = ... } - * class Child extends Parent - * - * then hash for `Child` name should be the same in both - * cases. - */ - "definition in parent class" in { - val parentA = simpleClass("Parent") - val barMethod = new Def(Array.empty, intTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - val parentB = simpleClass("Parent", barMethod) - val childA = { - val structure = new Structure(lzy(Array[Type](parentA.structure)), lzy(Array.empty[Definition]), lzy(Array.empty[Definition])) - simpleClass("Child", structure) - } - val childB = { - val structure = new Structure(lzy(Array[Type](parentB.structure)), lzy(Array.empty[Definition]), lzy(Array[Definition](barMethod))) - simpleClass("Child", structure) - } - val parentANameHashes = nameHashesForClass(parentA) - val parentBNameHashes = nameHashesForClass(parentB) - Seq("Parent") === parentANameHashes.regularMembers.map(_.name).toSeq - Seq("Parent", "bar") === parentBNameHashes.regularMembers.map(_.name).toSeq - parentANameHashes !=== parentBNameHashes - val childANameHashes = nameHashesForClass(childA) - val childBNameHashes = nameHashesForClass(childB) - assertNameHashEqualForRegularName("Child", childANameHashes, childBNameHashes) - } - - /** - * Checks if changes to structural types that appear in method signature - * affect name hash of the method. For example, if we have: - * - * // Test1.scala - * class A { - * def foo: { bar: Int } - * } - * - * // Test2.scala - * class A { - * def foo: { bar: String } - * } - * - * then name hash for "foo" should be different in those two cases. - */ - "structural type in definition" in { - /** def foo: { bar: Int } */ - val fooMethod1 = { - val barMethod1 = new Def(Array.empty, intTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - new Def(Array.empty, simpleStructure(barMethod1), Array.empty, "foo", publicAccess, defaultModifiers, Array.empty) - } - /** def foo: { bar: String } */ - val fooMethod2 = { - val barMethod2 = new Def(Array.empty, strTpe, Array.empty, "bar", publicAccess, defaultModifiers, Array.empty) - new Def(Array.empty, simpleStructure(barMethod2), Array.empty, "foo", publicAccess, defaultModifiers, Array.empty) - } - val aClass1 = simpleClass("A", fooMethod1) - val aClass2 = simpleClass("A", fooMethod2) - val nameHashes1 = nameHashesForClass(aClass1) - val nameHashes2 = nameHashesForClass(aClass2) - // note that `bar` does appear here - Seq("A", "foo", "bar") === nameHashes1.regularMembers.map(_.name).toSeq - Seq("A", "foo", "bar") === nameHashes2.regularMembers.map(_.name).toSeq - assertNameHashEqualForRegularName("A", nameHashes1, nameHashes2) - assertNameHashNotEqualForRegularName("foo", nameHashes1, nameHashes2) - assertNameHashNotEqualForRegularName("bar", nameHashes1, nameHashes2) - } - - /** - * Checks that private members are included in the hash of the public API of traits. - * Including the private members of traits is required because classes that implement a trait - * have to define the private members of the trait. Therefore, if a private member of a trait is added, - * modified or removed we need to recompile the classes that implement this trait. - * For instance, if trait Foo is initially defined as: - * trait Foo { private val x = new A } - * changing it to - * trait Foo { private val x = new B } - * requires us to recompile all implementors of trait Foo, because scalac generates setters and getters - * for the private fields of trait Foo in its implementor. If the clients of trait Foo are not recompiled, - * we get abstract method errors at runtime, because the types expected by the setter (for instance) does not - * match. - */ - "private members in traits" in { - /* trait Foo { private val x } */ - val fooTrait1 = - simpleTrait("Foo", - simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)), - publicAccess) - - /* trait Foo */ - val fooTrait2 = - simpleTrait("Foo", - simpleStructure(), - publicAccess) - - val api1 = new SourceAPI(Array.empty, Array(fooTrait1)) - val api2 = new SourceAPI(Array.empty, Array(fooTrait2)) - - HashAPI(api1) !== HashAPI(api2) - - } - - /** - * Checks that private members in non-top-level traits are included as well. - */ - "private members in nested traits" in { - /* class A { trait Foo { private val x } } */ - val classA1 = - simpleClass("A", - simpleTrait("Foo", - simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)), - publicAccess)) - - /* class A { trait Foo } */ - val classA2 = - simpleClass("A", - simpleTrait("Foo", - simpleStructure(), - publicAccess)) - - val api1 = new SourceAPI(Array.empty, Array(classA1)) - val api2 = new SourceAPI(Array.empty, Array(classA2)) - - HashAPI(api1) !== HashAPI(api2) - - } - - /** - * Checks that private traits are NOT included in the hash. - */ - "private traits" in { - /* class Foo { private trait T { private val x } } */ - val classFoo1 = - simpleClass("Foo", - simpleTrait("T", - simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)), - privateAccess)) - - /** class Foo { private trait T } */ - val classFoo2 = - simpleClass("Foo", - simpleTrait("T", - simpleStructure(), - privateAccess)) - - /** class Foo */ - val classFoo3 = - simpleClass("Foo") - - val api1 = new SourceAPI(Array.empty, Array(classFoo1)) - val api2 = new SourceAPI(Array.empty, Array(classFoo2)) - val api3 = new SourceAPI(Array.empty, Array(classFoo3)) - - HashAPI(api1) === HashAPI(api2) && HashAPI(api2) === HashAPI(api3) - } - - /** - * Checks that private members are NOT included in the hash of the public API of classes. - */ - "private members in classes" in { - /* class Foo { private val x } */ - val classFoo1 = - simpleClass("Foo", - simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty))) - - /* class Foo */ - val classFoo2 = - simpleClass("Foo", - simpleStructure()) - - val api1 = new SourceAPI(Array.empty, Array(classFoo1)) - val api2 = new SourceAPI(Array.empty, Array(classFoo2)) - - HashAPI(api1) === HashAPI(api2) - - } - - private def assertNameHashEqualForRegularName(name: String, nameHashes1: _internalOnly_NameHashes, - nameHashes2: _internalOnly_NameHashes) = { - val nameHash1 = nameHashForRegularName(nameHashes1, name) - val nameHash2 = nameHashForRegularName(nameHashes1, name) - nameHash1 === nameHash2 - } - - private def assertNameHashNotEqualForRegularName(name: String, nameHashes1: _internalOnly_NameHashes, - nameHashes2: _internalOnly_NameHashes) = { - val nameHash1 = nameHashForRegularName(nameHashes1, name) - val nameHash2 = nameHashForRegularName(nameHashes2, name) - nameHash1 !=== nameHash2 - } - - private def nameHashForRegularName(nameHashes: _internalOnly_NameHashes, name: String): _internalOnly_NameHash = - try { - nameHashes.regularMembers.find(_.name == name).get - } catch { - case e: NoSuchElementException => throw new RuntimeException(s"Couldn't find $name in $nameHashes", e) - } - - private def nameHashesForClass(cl: ClassLike): _internalOnly_NameHashes = { - val sourceAPI = new SourceAPI(Array.empty, Array(cl)) - val nameHashing = new NameHashing - nameHashing.nameHashes(sourceAPI) - } - - private def lzy[T](x: T): Lazy[T] = new Lazy[T] { def get: T = x } - - private def simpleStructure(defs: Definition*) = new Structure(lzy(Array.empty[Type]), lzy(defs.toArray), lzy(Array.empty[Definition])) - - private def simpleClass(name: String, defs: Definition*): ClassLike = { - val structure = simpleStructure(defs: _*) - simpleClass(name, structure) - } - - private def simpleClass(name: String, structure: Structure): ClassLike = { - new ClassLike(DefinitionType.ClassDef, lzy(emptyType), lzy(structure), Array.empty, Array.empty, name, publicAccess, defaultModifiers, Array.empty) - } - - private def simpleTrait(name: String, structure: Structure, access: Access): ClassLike = { - new ClassLike(DefinitionType.Trait, lzy(emptyType), lzy(structure), Array.empty, Array.empty, name, access, defaultModifiers, Array.empty) - } - - private val emptyType = new EmptyType - private val intTpe = new Projection(emptyType, "Int") - private val strTpe = new Projection(emptyType, "String") - private val publicAccess = new Public - private val privateAccess = new Private(new Unqualified) - private val defaultModifiers = new Modifiers(false, false, false, false, false, false, false) - -} diff --git a/compile/inc/NOTICE b/compile/inc/NOTICE deleted file mode 100644 index 28a4004a0..000000000 --- a/compile/inc/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Incremental Logic Component -Copyright 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/compile/inc/notes b/compile/inc/notes deleted file mode 100644 index 11d7537f5..000000000 --- a/compile/inc/notes +++ /dev/null @@ -1,21 +0,0 @@ -each compilation group gets an Analysis -an sbt-style project could have multiple compilation groups or there could be multiple projects per compilation group. -Traditionally, there has been a main group and a test group. - -Each Analysis is associated with one or more classpath entries. Typically, it will be associated with the output directory and/or any artifacts produced from that output directory. - -For Java sources, need to write a (File, Set[File]) => Source function that reads an API from a class file. The compile function passed to IncrementalCompile needs to handle compiling Java sources in the proper order - -Need to handle entries removed from classpath. Could be done similarly to how Locate is used for getting the API for a dependency. In this case, we'd get the Stamp for a binary dependency. - -Post-analysis - - need to handle inherited definitions - -Need builder component: - Processor[D] - def process(command: String, arg: String, current: State[D]): Result[State[D]] - Initial[D] - def init: State[D] - State[D] - value: D - processors: Map[String, Processor[D]] \ No newline at end of file diff --git a/compile/inc/src/main/scala/sbt/CompileSetup.scala b/compile/inc/src/main/scala/sbt/CompileSetup.scala deleted file mode 100644 index 5d5a0823b..000000000 --- a/compile/inc/src/main/scala/sbt/CompileSetup.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import xsbti.compile.{ CompileOrder, Output => APIOutput, SingleOutput, MultipleOutput } -import java.io.File - -// this class exists because of Scala's restriction on implicit parameter search. -// We cannot require an implicit parameter Equiv[Seq[String]] to construct Equiv[CompileSetup] -// because complexity(Equiv[Seq[String]]) > complexity(Equiv[CompileSetup]) -// (6 > 4) -final class CompileOptions(val options: Seq[String], val javacOptions: Seq[String]) { - override def toString = s"CompileOptions(scalac=$options, javac=$javacOptions)" -} -final class CompileSetup(val output: APIOutput, val options: CompileOptions, val compilerVersion: String, - val order: CompileOrder, val nameHashing: Boolean) { - @deprecated("Use the other overloaded variant of the constructor that takes `nameHashing` value, instead.", "0.13.2") - def this(output: APIOutput, options: CompileOptions, compilerVersion: String, order: CompileOrder) = { - this(output, options, compilerVersion, order, false) - } - override def toString = s"""CompileSetup( - | options = $options - | compilerVersion = $compilerVersion - | order = $order - | nameHashing = $nameHashing - | output = $output - |)""".stripMargin -} - -object CompileSetup { - // Equiv[CompileOrder.Value] dominates Equiv[CompileSetup] - implicit def equivCompileSetup(implicit equivOutput: Equiv[APIOutput], equivOpts: Equiv[CompileOptions], equivComp: Equiv[String] /*, equivOrder: Equiv[CompileOrder]*/ ): Equiv[CompileSetup] = new Equiv[CompileSetup] { - def equiv(a: CompileSetup, b: CompileSetup) = { - // For some reason, an Equiv[Nothing] or some such is getting injected into here now, and borking all our results. - // We hardcode these to use the Equiv defined in this class. - def sameOutput = CompileSetup.equivOutput.equiv(a.output, b.output) - def sameOptions = CompileSetup.equivOpts.equiv(a.options, b.options) - def sameCompiler = equivComp.equiv(a.compilerVersion, b.compilerVersion) - def sameOrder = a.order == b.order - def sameNameHasher = a.nameHashing == b.nameHashing - sameOutput && - sameOptions && - sameCompiler && - sameOrder && // equivOrder.equiv(a.order, b.order) - sameNameHasher - } - } - implicit val equivFile: Equiv[File] = new Equiv[File] { - def equiv(a: File, b: File) = a.getAbsoluteFile == b.getAbsoluteFile - } - implicit val equivOutput: Equiv[APIOutput] = new Equiv[APIOutput] { - implicit val outputGroupsOrdering = Ordering.by((og: MultipleOutput.OutputGroup) => og.sourceDirectory) - def equiv(out1: APIOutput, out2: APIOutput) = (out1, out2) match { - case (m1: MultipleOutput, m2: MultipleOutput) => - (m1.outputGroups.length == m2.outputGroups.length) && - (m1.outputGroups.sorted zip m2.outputGroups.sorted forall { - case (a, b) => - equivFile.equiv(a.sourceDirectory, b.sourceDirectory) && equivFile.equiv(a.outputDirectory, b.outputDirectory) - }) - case (s1: SingleOutput, s2: SingleOutput) => - equivFile.equiv(s1.outputDirectory, s2.outputDirectory) - case _ => - false - } - } - implicit val equivOpts: Equiv[CompileOptions] = new Equiv[CompileOptions] { - def equiv(a: CompileOptions, b: CompileOptions) = { - (a.options sameElements b.options) && - (a.javacOptions sameElements b.javacOptions) - } - } - implicit val equivCompilerVersion: Equiv[String] = new Equiv[String] { - def equiv(a: String, b: String) = a == b - } - - implicit val equivOrder: Equiv[CompileOrder] = new Equiv[CompileOrder] { - def equiv(a: CompileOrder, b: CompileOrder) = a == b - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/APIDiff.scala b/compile/inc/src/main/scala/sbt/inc/APIDiff.scala deleted file mode 100644 index 48fa0968f..000000000 --- a/compile/inc/src/main/scala/sbt/inc/APIDiff.scala +++ /dev/null @@ -1,67 +0,0 @@ -package sbt.inc - -import xsbti.api.SourceAPI -import xsbt.api.ShowAPI -import xsbt.api.DefaultShowAPI._ -import java.lang.reflect.Method -import java.util.{ List => JList } - -/** - * A class which computes diffs (unified diffs) between two textual representations of an API. - * - * Internally, it uses java-diff-utils library but it calls it through reflection so there's - * no hard dependency on java-diff-utils. - * - * The reflective lookup of java-diff-utils library is performed in the constructor. Exceptions - * thrown by reflection are passed as-is to the caller of the constructor. - * - * @throws ClassNotFoundException if difflib.DiffUtils class cannot be located - * @throws LinkageError - * @throws ExceptionInInitializerError - */ -private[inc] class APIDiff { - - import APIDiff._ - - private val diffUtilsClass = Class.forName(diffUtilsClassName) - // method signature: diff(List, List) - private val diffMethod: Method = - diffUtilsClass.getMethod(diffMethodName, classOf[JList[_]], classOf[JList[_]]) - - private val generateUnifiedDiffMethod: Method = { - val patchClass = Class.forName(patchClassName) - // method signature: generateUnifiedDiff(String, String, List, Patch, int) - diffUtilsClass.getMethod(generateUnifiedDiffMethodName, classOf[String], - classOf[String], classOf[JList[String]], patchClass, classOf[Int]) - } - - /** - * Generates an unified diff between textual representations of `api1` and `api2`. - */ - def generateApiDiff(fileName: String, api1: SourceAPI, api2: SourceAPI, contextSize: Int): String = { - val api1Str = ShowAPI.show(api1) - val api2Str = ShowAPI.show(api2) - generateApiDiff(fileName, api1Str, api2Str, contextSize) - } - - private def generateApiDiff(fileName: String, f1: String, f2: String, contextSize: Int): String = { - assert((diffMethod != null) && (generateUnifiedDiffMethod != null), "APIDiff isn't properly initialized.") - import scala.collection.JavaConverters._ - def asJavaList[T](it: Iterator[T]): java.util.List[T] = it.toSeq.asJava - val f1Lines = asJavaList(f1.lines) - val f2Lines = asJavaList(f2.lines) - //val diff = DiffUtils.diff(f1Lines, f2Lines) - val diff /*: Patch*/ = diffMethod.invoke(null, f1Lines, f2Lines) - val unifiedPatch: JList[String] = generateUnifiedDiffMethod.invoke(null, fileName, fileName, f1Lines, diff, - (contextSize: java.lang.Integer)).asInstanceOf[JList[String]] - unifiedPatch.asScala.mkString("\n") - } - -} - -private[inc] object APIDiff { - private val diffUtilsClassName = "difflib.DiffUtils" - private val patchClassName = "difflib.Patch" - private val diffMethodName = "diff" - private val generateUnifiedDiffMethodName = "generateUnifiedDiff" -} diff --git a/compile/inc/src/main/scala/sbt/inc/APIs.scala b/compile/inc/src/main/scala/sbt/inc/APIs.scala deleted file mode 100644 index 08de5b88d..000000000 --- a/compile/inc/src/main/scala/sbt/inc/APIs.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbti.api.Source -import java.io.File -import APIs.getAPI -import xsbti.api._internalOnly_NameHashes -import scala.util.Sorting -import xsbt.api.SameAPI - -trait APIs { - /** - * The API for the source file `src` at the time represented by this instance. - * This method returns an empty API if the file had no API or is not known to this instance. - */ - def internalAPI(src: File): Source - /** - * The API for the external class `ext` at the time represented by this instance. - * This method returns an empty API if the file had no API or is not known to this instance. - */ - def externalAPI(ext: String): Source - - def allExternals: collection.Set[String] - def allInternalSources: collection.Set[File] - - def ++(o: APIs): APIs - - def markInternalSource(src: File, api: Source): APIs - def markExternalAPI(ext: String, api: Source): APIs - - def removeInternal(remove: Iterable[File]): APIs - def filterExt(keep: String => Boolean): APIs - @deprecated("OK to remove in 0.14", "0.13.1") - def groupBy[K](internal: (File) => K, keepExternal: Map[K, String => Boolean]): Map[K, APIs] - - def internal: Map[File, Source] - def external: Map[String, Source] -} -object APIs { - def apply(internal: Map[File, Source], external: Map[String, Source]): APIs = new MAPIs(internal, external) - def empty: APIs = apply(Map.empty, Map.empty) - - val emptyAPI = new xsbti.api.SourceAPI(Array(), Array()) - val emptyCompilation = new xsbti.api.Compilation(-1, Array()) - val emptyNameHashes = new xsbti.api._internalOnly_NameHashes(Array.empty, Array.empty) - val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, emptyNameHashes, false) - def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource) -} - -private class MAPIs(val internal: Map[File, Source], val external: Map[String, Source]) extends APIs { - def allInternalSources: collection.Set[File] = internal.keySet - def allExternals: collection.Set[String] = external.keySet - - def ++(o: APIs): APIs = new MAPIs(internal ++ o.internal, external ++ o.external) - - def markInternalSource(src: File, api: Source): APIs = - new MAPIs(internal.updated(src, api), external) - - def markExternalAPI(ext: String, api: Source): APIs = - new MAPIs(internal, external.updated(ext, api)) - - def removeInternal(remove: Iterable[File]): APIs = new MAPIs(internal -- remove, external) - def filterExt(keep: String => Boolean): APIs = new MAPIs(internal, external.filterKeys(keep)) - @deprecated("Broken implementation. OK to remove in 0.14", "0.13.1") - def groupBy[K](f: (File) => K, keepExternal: Map[K, String => Boolean]): Map[K, APIs] = - internal.groupBy(item => f(item._1)) map { group => (group._1, new MAPIs(group._2, external).filterExt(keepExternal.getOrElse(group._1, _ => false))) } - - def internalAPI(src: File) = getAPI(internal, src) - def externalAPI(ext: String) = getAPI(external, ext) - - override def equals(other: Any): Boolean = other match { - case o: MAPIs => { - def areEqual[T](x: Map[T, Source], y: Map[T, Source])(implicit ord: math.Ordering[T]) = { - x.size == y.size && (sorted(x) zip sorted(y) forall { z => z._1._1 == z._2._1 && SameAPI(z._1._2, z._2._2) }) - } - areEqual(internal, o.internal) && areEqual(external, o.external) - } - case _ => false - } - - override lazy val hashCode: Int = { - def hash[T](m: Map[T, Source])(implicit ord: math.Ordering[T]) = sorted(m).map(x => (x._1, x._2.apiHash).hashCode).hashCode - (hash(internal), hash(external)).hashCode - } - - override def toString: String = "API(internal: %d, external: %d)".format(internal.size, external.size) - - private[this] def sorted[T](m: Map[T, Source])(implicit ord: math.Ordering[T]): Seq[(T, Source)] = m.toSeq.sortBy(_._1) -} diff --git a/compile/inc/src/main/scala/sbt/inc/Analysis.scala b/compile/inc/src/main/scala/sbt/inc/Analysis.scala deleted file mode 100644 index b692d9a31..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Analysis.scala +++ /dev/null @@ -1,307 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbti.api.Source -import xsbti.DependencyContext._ -import java.io.File - -/** - * The merge/groupBy functionality requires understanding of the concepts of internalizing/externalizing dependencies: - * - * Say we have source files X, Y. And say we have some analysis A_X containing X as a source, and likewise for A_Y and Y. - * If X depends on Y then A_X contains an external dependency X -> Y. - * - * However if we merge A_X and A_Y into a combined analysis A_XY, then A_XY contains X and Y as sources, and therefore - * X -> Y must be converted to an internal dependency in A_XY. We refer to this as "internalizing" the dependency. - * - * The reverse transformation must occur if we group an analysis A_XY into A_X and A_Y, so that the dependency X->Y - * crosses the boundary. We refer to this as "externalizing" the dependency. - * - * These transformations are complicated by the fact that internal dependencies are expressed as source file -> source file, - * but external dependencies are expressed as source file -> fully-qualified class name. - */ -trait Analysis { - val stamps: Stamps - val apis: APIs - /** Mappings between sources, classes, and binaries. */ - val relations: Relations - val infos: SourceInfos - /** - * Information about compiler runs accumulated since `clean` command has been run. - * - * The main use-case for using `compilations` field is to determine how - * many iterations it took to compilen give code. The `Compilation` object - * are also stored in `Source` objects so there's an indirect way to recover - * information about files being recompiled in every iteration. - * - * The incremental compilation algorithm doesn't use information stored in - * `compilations`. It's safe to prune contents of that field without breaking - * internal consistency of the entire Analysis object. - */ - val compilations: Compilations - - /** Concatenates Analysis objects naively, i.e., doesn't internalize external deps on added files. See `Analysis.merge`. */ - def ++(other: Analysis): Analysis - - /** Drops all analysis information for `sources` naively, i.e., doesn't externalize internal deps on removed files. */ - def --(sources: Iterable[File]): Analysis - - def copy(stamps: Stamps = stamps, apis: APIs = apis, relations: Relations = relations, infos: SourceInfos = infos, - compilations: Compilations = compilations): Analysis - - def addSource(src: File, api: Source, stamp: Stamp, info: SourceInfo, - products: Iterable[(File, String, Stamp)], - internalDeps: Iterable[InternalDependency], - externalDeps: Iterable[ExternalDependency], - binaryDeps: Iterable[(File, String, Stamp)]): Analysis - - @deprecated("Register all products and dependencies in addSource.", "0.13.8") - def addSource(src: File, api: Source, stamp: Stamp, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis - @deprecated("Register all products and dependencies in addSource.", "0.13.8") - def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis - @deprecated("Register all products and dependencies in addSource.", "0.13.8") - def addExternalDep(src: File, dep: String, api: Source, inherited: Boolean): Analysis - @deprecated("Register all products and dependencies in addSource.", "0.13.8") - def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis - - /** Partitions this Analysis using the discriminator function. Externalizes internal deps that cross partitions. */ - def groupBy[K](discriminator: (File => K)): Map[K, Analysis] - - override lazy val toString = Analysis.summary(this) -} - -object Analysis { - lazy val Empty: Analysis = new MAnalysis(Stamps.empty, APIs.empty, Relations.empty, SourceInfos.empty, Compilations.empty) - private[sbt] def empty(nameHashing: Boolean): Analysis = new MAnalysis(Stamps.empty, APIs.empty, - Relations.empty(nameHashing = nameHashing), SourceInfos.empty, Compilations.empty) - - /** Merge multiple analysis objects into one. Deps will be internalized as needed. */ - def merge(analyses: Traversable[Analysis]): Analysis = { - if (analyses.exists(_.relations.nameHashing)) - throw new IllegalArgumentException("Merging of Analyses that have" + - "`relations.memberRefAndInheritanceDeps` set to `true` is not supported.") - - // Merge the Relations, internalizing deps as needed. - val mergedSrcProd = Relation.merge(analyses map { _.relations.srcProd }) - val mergedBinaryDep = Relation.merge(analyses map { _.relations.binaryDep }) - val mergedClasses = Relation.merge(analyses map { _.relations.classes }) - - val stillInternal = Relation.merge(analyses map { _.relations.direct.internal }) - val (internalized, stillExternal) = Relation.merge(analyses map { _.relations.direct.external }) partition { case (a, b) => mergedClasses._2s.contains(b) } - val internalizedFiles = Relation.reconstruct(internalized.forwardMap mapValues { _ flatMap mergedClasses.reverse }) - val mergedInternal = stillInternal ++ internalizedFiles - - val stillInternalPI = Relation.merge(analyses map { _.relations.publicInherited.internal }) - val (internalizedPI, stillExternalPI) = Relation.merge(analyses map { _.relations.publicInherited.external }) partition { case (a, b) => mergedClasses._2s.contains(b) } - val internalizedFilesPI = Relation.reconstruct(internalizedPI.forwardMap mapValues { _ flatMap mergedClasses.reverse }) - val mergedInternalPI = stillInternalPI ++ internalizedFilesPI - - val mergedRelations = Relations.make( - mergedSrcProd, - mergedBinaryDep, - Relations.makeSource(mergedInternal, stillExternal), - Relations.makeSource(mergedInternalPI, stillExternalPI), - mergedClasses - ) - - // Merge the APIs, internalizing APIs for targets of dependencies we internalized above. - val concatenatedAPIs = (APIs.empty /: (analyses map { _.apis }))(_ ++ _) - val stillInternalAPIs = concatenatedAPIs.internal - val (internalizedAPIs, stillExternalAPIs) = concatenatedAPIs.external partition { x: (String, Source) => internalized._2s.contains(x._1) } - val internalizedFilesAPIs = internalizedAPIs flatMap { - case (cls: String, source: Source) => mergedRelations.definesClass(cls) map { file: File => (file, concatenatedAPIs.internalAPI(file)) } - } - val mergedAPIs = APIs(stillInternalAPIs ++ internalizedFilesAPIs, stillExternalAPIs) - - val mergedStamps = Stamps.merge(analyses map { _.stamps }) - val mergedInfos = SourceInfos.merge(analyses map { _.infos }) - val mergedCompilations = Compilations.merge(analyses map { _.compilations }) - - new MAnalysis(mergedStamps, mergedAPIs, mergedRelations, mergedInfos, mergedCompilations) - } - - def summary(a: Analysis): String = - { - val (j, s) = a.apis.allInternalSources.partition(_.getName.endsWith(".java")) - val c = a.stamps.allProducts - val ext = a.apis.allExternals - val jars = a.relations.allBinaryDeps.filter(_.getName.endsWith(".jar")) - val unreportedCount = a.infos.allInfos.values.map(_.unreportedProblems.size).sum - val sections = - counted("Scala source", "", "s", s.size) ++ - counted("Java source", "", "s", j.size) ++ - counted("class", "", "es", c.size) ++ - counted("external source dependenc", "y", "ies", ext.size) ++ - counted("binary dependenc", "y", "ies", jars.size) ++ - counted("unreported warning", "", "s", unreportedCount) - sections.mkString("Analysis: ", ", ", "") - } - - def counted(prefix: String, single: String, plural: String, count: Int): Option[String] = - count match { - case 0 => None - case 1 => Some("1 " + prefix + single) - case x => Some(x.toString + " " + prefix + plural) - } - -} -private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relations, val infos: SourceInfos, val compilations: Compilations) extends Analysis { - def ++(o: Analysis): Analysis = new MAnalysis(stamps ++ o.stamps, apis ++ o.apis, relations ++ o.relations, - infos ++ o.infos, compilations ++ o.compilations) - - def --(sources: Iterable[File]): Analysis = - { - val newRelations = relations -- sources - def keep[T](f: (Relations, T) => Set[_]): T => Boolean = f(newRelations, _).nonEmpty - - val newAPIs = apis.removeInternal(sources).filterExt(keep(_ usesExternal _)) - val newStamps = stamps.filter(keep(_ produced _), sources, keep(_ usesBinary _)) - val newInfos = infos -- sources - new MAnalysis(newStamps, newAPIs, newRelations, newInfos, compilations) - } - - def copy(stamps: Stamps, apis: APIs, relations: Relations, infos: SourceInfos, compilations: Compilations = compilations): Analysis = - new MAnalysis(stamps, apis, relations, infos, compilations) - - def addSource(src: File, api: Source, stamp: Stamp, info: SourceInfo, - products: Iterable[(File, String, Stamp)], - internalDeps: Iterable[InternalDependency], - externalDeps: Iterable[ExternalDependency], - binaryDeps: Iterable[(File, String, Stamp)]): Analysis = { - - val newStamps = { - val productStamps = products.foldLeft(stamps.markInternalSource(src, stamp)) { - case (tmpStamps, (toProduct, _, prodStamp)) => tmpStamps.markProduct(toProduct, prodStamp) - } - - binaryDeps.foldLeft(productStamps) { - case (tmpStamps, (toBinary, className, binStamp)) => tmpStamps.markBinary(toBinary, className, binStamp) - } - } - - val newAPIs = externalDeps.foldLeft(apis.markInternalSource(src, api)) { - case (tmpApis, ExternalDependency(_, toClassName, classApi, _)) => tmpApis.markExternalAPI(toClassName, classApi) - } - - val newRelations = relations.addSource(src, products map (p => (p._1, p._2)), internalDeps, externalDeps, binaryDeps) - - copy(newStamps, newAPIs, newRelations, infos.add(src, info)) - } - - def addSource(src: File, api: Source, stamp: Stamp, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis = { - - val directDeps = directInternal.map(InternalDependency(src, _, DependencyByMemberRef)) - val inheritedDeps = inheritedInternal.map(InternalDependency(src, _, DependencyByInheritance)) - - addSource(src, api, stamp, info, products = Nil, directDeps ++ inheritedDeps, Nil, Nil) - } - - def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis = - copy(stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDeps(src, (dep, className, stamp) :: Nil), infos) - - def addExternalDep(src: File, dep: String, depAPI: Source, inherited: Boolean): Analysis = { - val context = if (inherited) DependencyByInheritance else DependencyByMemberRef - copy(stamps, apis.markExternalAPI(dep, depAPI), relations.addExternalDeps(src, ExternalDependency(src, dep, depAPI, context) :: Nil), infos) - } - - def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis = - copy(stamps.markProduct(product, stamp), apis, relations.addProducts(src, (product, name) :: Nil), infos) - - def groupBy[K](discriminator: File => K): Map[K, Analysis] = { - if (relations.nameHashing) - throw new UnsupportedOperationException("Grouping of Analyses that have" + - "`relations.memberRefAndInheritanceDeps` set to `true` is not supported.") - - def discriminator1(x: (File, _)) = discriminator(x._1) // Apply the discriminator to the first coordinate. - - val kSrcProd = relations.srcProd.groupBy(discriminator1) - val kBinaryDep = relations.binaryDep.groupBy(discriminator1) - val kClasses = relations.classes.groupBy(discriminator1) - val kSourceInfos = infos.allInfos.groupBy(discriminator1) - - val (kStillInternal, kExternalized) = relations.direct.internal partition { case (a, b) => discriminator(a) == discriminator(b) } match { - case (i, e) => (i.groupBy(discriminator1), e.groupBy(discriminator1)) - } - val kStillExternal = relations.direct.external.groupBy(discriminator1) - - // Find all possible groups. - val allMaps = kSrcProd :: kBinaryDep :: kStillInternal :: kExternalized :: kStillExternal :: kClasses :: kSourceInfos :: Nil - val allKeys: Set[K] = (Set.empty[K] /: (allMaps map { _.keySet }))(_ ++ _) - - // Map from file to a single representative class defined in that file. - // This is correct (for now): currently all classes in an external dep share the same Source object, - // and a change to any of them will act like a change to all of them. - // We don't use all the top-level classes in source.api.definitions, even though that's more intuitively - // correct, because this can cause huge bloat of the analysis file. - def getRepresentativeClass(file: File): Option[String] = apis.internalAPI(file).api.definitions.headOption map { _.name } - - // Create an Analysis for each group. - (for (k <- allKeys) yield { - def getFrom[A, B](m: Map[K, Relation[A, B]]): Relation[A, B] = m.getOrElse(k, Relation.empty) - - // Products and binary deps. - val srcProd = getFrom(kSrcProd) - val binaryDep = getFrom(kBinaryDep) - - // Direct Sources. - val stillInternal = getFrom(kStillInternal) - val stillExternal = getFrom(kStillExternal) - val externalized = getFrom(kExternalized) - val externalizedClasses = Relation.reconstruct(externalized.forwardMap mapValues { _ flatMap getRepresentativeClass }) - val newExternal = stillExternal ++ externalizedClasses - - // Public inherited sources. - val stillInternalPI = stillInternal filter relations.publicInherited.internal.contains - val stillExternalPI = stillExternal filter relations.publicInherited.external.contains - val externalizedPI = externalized filter relations.publicInherited.internal.contains - val externalizedClassesPI = Relation.reconstruct(externalizedPI.forwardMap mapValues { _ flatMap getRepresentativeClass }) - val newExternalPI = stillExternalPI ++ externalizedClassesPI - - // Class names. - val classes = getFrom(kClasses) - - // Create new relations for this group. - val newRelations = Relations.make( - srcProd, - binaryDep, - Relations.makeSource(stillInternal, newExternal), - Relations.makeSource(stillInternalPI, newExternalPI), - classes - ) - - // Compute new API mappings. - def apisFor[T](m: Map[T, Source], x: Traversable[T]): Map[T, Source] = - (x map { e: T => (e, m.get(e)) } collect { case (t, Some(source)) => (t, source) }).toMap - val stillInternalAPIs = apisFor(apis.internal, srcProd._1s) - val stillExternalAPIs = apisFor(apis.external, stillExternal._2s) - val externalizedAPIs = apisFor(apis.internal, externalized._2s) - val externalizedClassesAPIs = externalizedAPIs flatMap { - case (file: File, source: Source) => getRepresentativeClass(file) map { cls: String => (cls, source) } - } - val newAPIs = APIs(stillInternalAPIs, stillExternalAPIs ++ externalizedClassesAPIs) - - // New stamps. - val newStamps = Stamps( - stamps.products.filterKeys(srcProd._2s.contains), - stamps.sources.filterKeys({ discriminator(_) == k }), - stamps.binaries.filterKeys(binaryDep._2s.contains), - stamps.classNames.filterKeys(binaryDep._2s.contains)) - - // New infos. - val newSourceInfos = SourceInfos.make(kSourceInfos.getOrElse(k, Map.empty)) - - (k, new MAnalysis(newStamps, newAPIs, newRelations, newSourceInfos, compilations)) - }).toMap - } - - override def equals(other: Any) = other match { - // Note: Equality doesn't consider source infos or compilations. - case o: MAnalysis => stamps == o.stamps && apis == o.apis && relations == o.relations - case _ => false - } - - override lazy val hashCode = (stamps :: apis :: relations :: Nil).hashCode -} diff --git a/compile/inc/src/main/scala/sbt/inc/AnalysisStore.scala b/compile/inc/src/main/scala/sbt/inc/AnalysisStore.scala deleted file mode 100644 index 4ecede85a..000000000 --- a/compile/inc/src/main/scala/sbt/inc/AnalysisStore.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -trait AnalysisStore { - def set(analysis: Analysis, setup: CompileSetup): Unit - def get(): Option[(Analysis, CompileSetup)] -} - -object AnalysisStore { - def cached(backing: AnalysisStore): AnalysisStore = new AnalysisStore { - private var last: Option[(Analysis, CompileSetup)] = None - def set(analysis: Analysis, setup: CompileSetup): Unit = { - backing.set(analysis, setup) - last = Some((analysis, setup)) - } - def get(): Option[(Analysis, CompileSetup)] = - { - if (last.isEmpty) - last = backing.get() - last - } - } - def sync(backing: AnalysisStore): AnalysisStore = new AnalysisStore { - def set(analysis: Analysis, setup: CompileSetup): Unit = synchronized { backing.set(analysis, setup) } - def get(): Option[(Analysis, CompileSetup)] = synchronized { backing.get() } - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/Changes.scala b/compile/inc/src/main/scala/sbt/inc/Changes.scala deleted file mode 100644 index 8e21f0614..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Changes.scala +++ /dev/null @@ -1,67 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbt.api.NameChanges -import java.io.File -import xsbti.api.{ _internalOnly_NameHashes => NameHashes } -import xsbti.api.{ _internalOnly_NameHash => NameHash } - -final case class InitialChanges(internalSrc: Changes[File], removedProducts: Set[File], binaryDeps: Set[File], external: APIChanges[String]) -final class APIChanges[T](val apiChanges: Iterable[APIChange[T]]) { - override def toString = "API Changes: " + apiChanges - def allModified: Iterable[T] = apiChanges.map(_.modified) -} - -sealed abstract class APIChange[T](val modified: T) -/** - * If we recompile a source file that contains a macro definition then we always assume that it's - * api has changed. The reason is that there's no way to determine if changes to macros implementation - * are affecting its users or not. Therefore we err on the side of caution. - */ -final case class APIChangeDueToMacroDefinition[T](modified0: T) extends APIChange(modified0) -final case class SourceAPIChange[T](modified0: T) extends APIChange(modified0) -/** - * An APIChange that carries information about modified names. - * - * This class is used only when name hashing algorithm is enabled. - */ -final case class NamesChange[T](modified0: T, modifiedNames: ModifiedNames) extends APIChange(modified0) - -/** - * ModifiedNames are determined by comparing name hashes in two versions of an API representation. - * - * Note that we distinguish between sets of regular (non-implicit) and implicit modified names. - * This distinction is needed because the name hashing algorithm makes different decisions based - * on whether modified name is implicit or not. Implicit names are much more difficult to handle - * due to difficulty of reasoning about the implicit scope. - */ -final case class ModifiedNames(regularNames: Set[String], implicitNames: Set[String]) { - override def toString: String = - s"ModifiedNames(regularNames = ${regularNames mkString ", "}, implicitNames = ${implicitNames mkString ", "})" -} -object ModifiedNames { - def compareTwoNameHashes(a: NameHashes, b: NameHashes): ModifiedNames = { - val modifiedRegularNames = calculateModifiedNames(a.regularMembers.toSet, b.regularMembers.toSet) - val modifiedImplicitNames = calculateModifiedNames(a.implicitMembers.toSet, b.implicitMembers.toSet) - ModifiedNames(modifiedRegularNames, modifiedImplicitNames) - } - private def calculateModifiedNames(xs: Set[NameHash], ys: Set[NameHash]): Set[String] = { - val differentNameHashes = (xs union ys) diff (xs intersect ys) - differentNameHashes.map(_.name) - } -} - -trait Changes[A] { - def added: Set[A] - def removed: Set[A] - def changed: Set[A] - def unmodified: Set[A] -} - -sealed abstract class Change(val file: File) -final class Removed(f: File) extends Change(f) -final class Added(f: File, newStamp: Stamp) extends Change(f) -final class Modified(f: File, oldStamp: Stamp, newStamp: Stamp) extends Change(f) diff --git a/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala b/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala deleted file mode 100644 index 507e6ae95..000000000 --- a/compile/inc/src/main/scala/sbt/inc/ClassfileManager.scala +++ /dev/null @@ -1,79 +0,0 @@ -package sbt.inc - -import sbt.IO -import java.io.File -import collection.mutable - -/** - * During an incremental compilation run, a ClassfileManager deletes class files and is notified of generated class files. - * A ClassfileManager can be used only once. - */ -trait ClassfileManager { - /** - * Called once per compilation step with the class files to delete prior to that step's compilation. - * The files in `classes` must not exist if this method returns normally. - * Any empty ancestor directories of deleted files must not exist either. - */ - def delete(classes: Iterable[File]): Unit - - /** Called once per compilation step with the class files generated during that step.*/ - def generated(classes: Iterable[File]): Unit - - /** Called once at the end of the whole compilation run, with `success` indicating whether compilation succeeded (true) or not (false).*/ - def complete(success: Boolean): Unit -} - -object ClassfileManager { - /** Constructs a minimal ClassfileManager implementation that immediately deletes class files when requested. */ - val deleteImmediately: () => ClassfileManager = () => new ClassfileManager { - def delete(classes: Iterable[File]): Unit = IO.deleteFilesEmptyDirs(classes) - def generated(classes: Iterable[File]): Unit = () - def complete(success: Boolean): Unit = () - } - @deprecated("Use overloaded variant that takes additional logger argument, instead.", "0.13.5") - def transactional(tempDir0: File): () => ClassfileManager = - transactional(tempDir0, sbt.Logger.Null) - /** When compilation fails, this ClassfileManager restores class files to the way they were before compilation.*/ - def transactional(tempDir0: File, logger: sbt.Logger): () => ClassfileManager = () => new ClassfileManager { - val tempDir = tempDir0.getCanonicalFile - IO.delete(tempDir) - IO.createDirectory(tempDir) - logger.debug(s"Created transactional ClassfileManager with tempDir = $tempDir") - - private[this] val generatedClasses = new mutable.HashSet[File] - private[this] val movedClasses = new mutable.HashMap[File, File] - - private def showFiles(files: Iterable[File]): String = files.map(f => s"\t$f").mkString("\n") - def delete(classes: Iterable[File]): Unit = { - logger.debug(s"About to delete class files:\n${showFiles(classes)}") - val toBeBackedUp = classes.filter(c => c.exists && !movedClasses.contains(c) && !generatedClasses(c)) - logger.debug(s"We backup classs files:\n${showFiles(toBeBackedUp)}") - for (c <- toBeBackedUp) { - movedClasses.put(c, move(c)) - } - IO.deleteFilesEmptyDirs(classes) - } - def generated(classes: Iterable[File]): Unit = { - logger.debug(s"Registering generated classes:\n${showFiles(classes)}") - generatedClasses ++= classes - } - def complete(success: Boolean): Unit = { - if (!success) { - logger.debug("Rolling back changes to class files.") - logger.debug(s"Removing generated classes:\n${showFiles(generatedClasses)}") - IO.deleteFilesEmptyDirs(generatedClasses) - logger.debug(s"Restoring class files: \n${showFiles(movedClasses.keys)}") - for ((orig, tmp) <- movedClasses) IO.move(tmp, orig) - } - logger.debug(s"Removing the temporary directory used for backing up class files: $tempDir") - IO.delete(tempDir) - } - - def move(c: File): File = - { - val target = File.createTempFile("sbt", ".class", tempDir) - IO.move(c, target) - target - } - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/Compilations.scala b/compile/inc/src/main/scala/sbt/inc/Compilations.scala deleted file mode 100644 index 2f6c2f003..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Compilations.scala +++ /dev/null @@ -1,21 +0,0 @@ -package sbt.inc - -import xsbti.api.Compilation - -/** Information about compiler runs accumulated since `clean` command has been run. */ -trait Compilations { - def allCompilations: Seq[Compilation] - def ++(o: Compilations): Compilations - def add(c: Compilation): Compilations -} - -object Compilations { - val empty: Compilations = new MCompilations(Seq.empty) - def make(s: Seq[Compilation]): Compilations = new MCompilations(s) - def merge(s: Traversable[Compilations]): Compilations = make((s flatMap { _.allCompilations }).toSeq.distinct) -} - -private final class MCompilations(val allCompilations: Seq[Compilation]) extends Compilations { - def ++(o: Compilations): Compilations = new MCompilations(allCompilations ++ o.allCompilations) - def add(c: Compilation): Compilations = new MCompilations(allCompilations :+ c) -} diff --git a/compile/inc/src/main/scala/sbt/inc/Compile.scala b/compile/inc/src/main/scala/sbt/inc/Compile.scala deleted file mode 100644 index 41338e7bd..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Compile.scala +++ /dev/null @@ -1,234 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbti.api.{ Source, SourceAPI, Compilation, OutputSetting, _internalOnly_NameHashes } -import xsbti.compile.{ DependencyChanges, Output, SingleOutput, MultipleOutput } -import xsbti.{ Position, Problem, Severity } -import Logger.{ m2o, problem } -import java.io.File -import xsbti.api.Definition -import xsbti.DependencyContext -import xsbti.DependencyContext.{ DependencyByInheritance, DependencyByMemberRef } - -/** - * Helper methods for running incremental compilation. All this is responsible for is - * adapting any xsbti.AnalysisCallback into one compatible with the [[sbt.inc.Incremental]] class. - */ -object IncrementalCompile { - /** - * Runs the incremental compilation algorithm. - * @param sources - * The full set of input sources - * @param entry - * A className -> source file lookup function. - * @param compile - * The mechanism to run a single 'step' of compile, for ALL source files involved. - * @param previous - * The previous dependency Analysis (or an empty one). - * @param forEntry - * The dependency Analysis associated with a given file - * @param output - * The configured output directory/directory mapping for source files. - * @param log - * Where all log messages should go - * @param options - * Incremental compiler options (like name hashing vs. not). - * @return - * A flag of whether or not compilation completed succesfully, and the resulting dependency analysis object. - * - */ - def apply(sources: Set[File], entry: String => Option[File], - compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, - previous: Analysis, - forEntry: File => Option[Analysis], - output: Output, log: Logger, - options: IncOptions): (Boolean, Analysis) = - { - val current = Stamps.initial(Stamp.lastModified, Stamp.hash, Stamp.lastModified) - val internalMap = (f: File) => previous.relations.produced(f).headOption - val externalAPI = getExternalAPI(entry, forEntry) - try { - Incremental.compile(sources, entry, previous, current, forEntry, doCompile(compile, internalMap, externalAPI, current, output, options), log, options) - } catch { - case e: xsbti.CompileCancelled => - log.info("Compilation has been cancelled") - // in case compilation got cancelled potential partial compilation results (e.g. produced classs files) got rolled back - // and we can report back as there was no change (false) and return a previous Analysis which is still up-to-date - (false, previous) - } - } - def doCompile(compile: (Set[File], DependencyChanges, xsbti.AnalysisCallback) => Unit, internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, output: Output, options: IncOptions) = - (srcs: Set[File], changes: DependencyChanges) => { - val callback = new AnalysisCallback(internalMap, externalAPI, current, output, options) - compile(srcs, changes, callback) - callback.get - } - def getExternalAPI(entry: String => Option[File], forEntry: File => Option[Analysis]): (File, String) => Option[Source] = - (file: File, className: String) => - entry(className) flatMap { defines => - if (file != Locate.resolve(defines, className)) - None - else - forEntry(defines) flatMap { analysis => - analysis.relations.definesClass(className).headOption flatMap { src => - analysis.apis.internal get src - } - } - } -} -private final class AnalysisCallback(internalMap: File => Option[File], externalAPI: (File, String) => Option[Source], current: ReadStamps, output: Output, options: IncOptions) extends xsbti.AnalysisCallback { - val compilation = { - val outputSettings = output match { - case single: SingleOutput => Array(new OutputSetting("/", single.outputDirectory.getAbsolutePath)) - case multi: MultipleOutput => - multi.outputGroups.map(out => new OutputSetting(out.sourceDirectory.getAbsolutePath, out.outputDirectory.getAbsolutePath)) - } - new Compilation(System.currentTimeMillis, outputSettings) - } - - override def toString = (List("APIs", "Binary deps", "Products", "Source deps") zip List(apis, binaryDeps, classes, intSrcDeps)).map { case (label, map) => label + "\n\t" + map.mkString("\n\t") }.mkString("\n") - - import collection.mutable.{ HashMap, HashSet, ListBuffer, Map, Set } - - private[this] val apis = new HashMap[File, (Int, SourceAPI)] - private[this] val usedNames = new HashMap[File, Set[String]] - private[this] val publicNameHashes = new HashMap[File, _internalOnly_NameHashes] - private[this] val unreporteds = new HashMap[File, ListBuffer[Problem]] - private[this] val reporteds = new HashMap[File, ListBuffer[Problem]] - private[this] val binaryDeps = new HashMap[File, Set[File]] - // source file to set of generated (class file, class name) - private[this] val classes = new HashMap[File, Set[(File, String)]] - // generated class file to its source file - private[this] val classToSource = new HashMap[File, File] - // internal source dependencies - private[this] val intSrcDeps = new HashMap[File, Set[InternalDependency]] - // external source dependencies - private[this] val extSrcDeps = new HashMap[File, Set[ExternalDependency]] - private[this] val binaryClassName = new HashMap[File, String] - // source files containing a macro def. - private[this] val macroSources = Set[File]() - - private def add[A, B](map: Map[A, Set[B]], a: A, b: B): Unit = - map.getOrElseUpdate(a, new HashSet[B]) += b - - def problem(category: String, pos: Position, msg: String, severity: Severity, reported: Boolean): Unit = - { - for (source <- m2o(pos.sourceFile)) { - val map = if (reported) reporteds else unreporteds - map.getOrElseUpdate(source, ListBuffer.empty) += Logger.problem(category, pos, msg, severity) - } - } - - def sourceDependency(dependsOn: File, source: File, context: DependencyContext) = { - add(intSrcDeps, source, InternalDependency(source, dependsOn, context)) - } - - @deprecated("Use `sourceDependency(File, File, DependencyContext)`.", "0.13.8") - def sourceDependency(dependsOn: File, source: File, inherited: Boolean) = - { - val context = if (inherited) DependencyByInheritance else DependencyByMemberRef - sourceDependency(dependsOn, source, context) - } - - private[this] def externalBinaryDependency(binary: File, className: String, source: File, context: DependencyContext) = { - binaryClassName.put(binary, className) - add(binaryDeps, source, binary) - } - - private[this] def externalSourceDependency(sourceFile: File, dependsOn: String, source: Source, context: DependencyContext) = { - val dependency = ExternalDependency(sourceFile, dependsOn, source, context) - add(extSrcDeps, sourceFile, dependency) - } - - def binaryDependency(classFile: File, name: String, source: File, context: DependencyContext) = - internalMap(classFile) match { - case Some(dependsOn) => - // dependency is a product of a source not included in this compilation - sourceDependency(dependsOn, source, context) - case None => - classToSource.get(classFile) match { - case Some(dependsOn) => - // dependency is a product of a source in this compilation step, - // but not in the same compiler run (as in javac v. scalac) - sourceDependency(dependsOn, source, context) - case None => - externalDependency(classFile, name, source, context) - } - } - - @deprecated("Use `binaryDependency(File, String, File, DependencyContext)`.", "0.13.8") - def binaryDependency(classFile: File, name: String, source: File, inherited: Boolean) = { - val context = if (inherited) DependencyByInheritance else DependencyByMemberRef - binaryDependency(classFile, name, source, context) - } - - private[this] def externalDependency(classFile: File, name: String, source: File, context: DependencyContext): Unit = - externalAPI(classFile, name) match { - case Some(api) => - // dependency is a product of a source in another project - externalSourceDependency(source, name, api, context) - case None => - // dependency is some other binary on the classpath - externalBinaryDependency(classFile, name, source, context) - } - - def generatedClass(source: File, module: File, name: String) = - { - add(classes, source, (module, name)) - classToSource.put(module, source) - } - - // empty value used when name hashing algorithm is disabled - private val emptyNameHashes = new xsbti.api._internalOnly_NameHashes(Array.empty, Array.empty) - - def api(sourceFile: File, source: SourceAPI): Unit = { - import xsbt.api.{ APIUtil, HashAPI } - if (APIUtil.isScalaSourceName(sourceFile.getName) && APIUtil.hasMacro(source)) macroSources += sourceFile - publicNameHashes(sourceFile) = { - if (nameHashing) - (new xsbt.api.NameHashing).nameHashes(source) - else - emptyNameHashes - } - val shouldMinimize = !Incremental.apiDebug(options) - val savedSource = if (shouldMinimize) APIUtil.minimize(source) else source - apis(sourceFile) = (HashAPI(source), savedSource) - } - - def usedName(sourceFile: File, name: String) = add(usedNames, sourceFile, name) - - def nameHashing: Boolean = options.nameHashing - - def get: Analysis = addUsedNames(addCompilation(addProductsAndDeps(Analysis.empty(nameHashing = nameHashing)))) - - def getOrNil[A, B](m: collection.Map[A, Seq[B]], a: A): Seq[B] = m.get(a).toList.flatten - def addCompilation(base: Analysis): Analysis = base.copy(compilations = base.compilations.add(compilation)) - def addUsedNames(base: Analysis): Analysis = (base /: usedNames) { - case (a, (src, names)) => - (a /: names) { case (a, name) => a.copy(relations = a.relations.addUsedName(src, name)) } - } - - def addProductsAndDeps(base: Analysis): Analysis = - (base /: apis) { - case (a, (src, api)) => - val stamp = current.internalSource(src) - val hash = stamp match { case h: Hash => h.value; case _ => new Array[Byte](0) } - // TODO store this in Relations, rather than Source. - val hasMacro: Boolean = macroSources.contains(src) - val s = new xsbti.api.Source(compilation, hash, api._2, api._1, publicNameHashes(src), hasMacro) - val info = SourceInfos.makeInfo(getOrNil(reporteds, src), getOrNil(unreporteds, src)) - val binaries = binaryDeps.getOrElse(src, Nil: Iterable[File]) - val prods = classes.getOrElse(src, Nil: Iterable[(File, String)]) - - val products = prods.map { case (prod, name) => (prod, name, current product prod) } - val internalDeps = intSrcDeps.getOrElse(src, Set.empty) - val externalDeps = extSrcDeps.getOrElse(src, Set.empty) - val binDeps = binaries.map(d => (d, binaryClassName(d), current binary d)) - - a.addSource(src, s, stamp, info, products, internalDeps, externalDeps, binDeps) - - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/Dependency.scala b/compile/inc/src/main/scala/sbt/inc/Dependency.scala deleted file mode 100644 index 668c687fd..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Dependency.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sbt.inc - -import java.io.File -import xsbti.api.Source -import xsbti.DependencyContext - -/** - * Represents the kind of dependency that exists between `sourceFile` and either `targetFile` - * or `targetClassName`. - * - * `InternalDependency` represent dependencies that exist between the files of a same project, - * while `ExternalDependency` represent cross-project dependencies. - */ -private[inc] final case class InternalDependency(sourceFile: File, targetFile: File, context: DependencyContext) -private[inc] final case class ExternalDependency(sourceFile: File, targetClassName: String, targetSource: Source, context: DependencyContext) diff --git a/compile/inc/src/main/scala/sbt/inc/FileValueCache.scala b/compile/inc/src/main/scala/sbt/inc/FileValueCache.scala deleted file mode 100644 index bd236bf9d..000000000 --- a/compile/inc/src/main/scala/sbt/inc/FileValueCache.scala +++ /dev/null @@ -1,43 +0,0 @@ -package sbt -package inc - -import java.io.File -import java.util.concurrent.ConcurrentHashMap - -sealed trait FileValueCache[T] { - def clear(): Unit - def get: File => T -} - -private[this] final class FileValueCache0[T](getStamp: File => Stamp, make: File => T)(implicit equiv: Equiv[Stamp]) extends FileValueCache[T] { - private[this] val backing = new ConcurrentHashMap[File, FileCache] - - def clear(): Unit = backing.clear() - def get = file => { - val ifAbsent = new FileCache(file) - val cache = backing.putIfAbsent(file, ifAbsent) - (if (cache eq null) ifAbsent else cache).get() - } - - private[this] final class FileCache(file: File) { - private[this] var stampedValue: Option[(Stamp, T)] = None - def get(): T = synchronized { - val latest = getStamp(file) - stampedValue match { - case Some((stamp, value)) if (equiv.equiv(latest, stamp)) => value - case _ => update(latest) - } - } - - private[this] def update(stamp: Stamp): T = - { - val value = make(file) - stampedValue = Some((stamp, value)) - value - } - } -} -object FileValueCache { - def apply[T](f: File => T): FileValueCache[T] = make(Stamp.lastModified)(f) - def make[T](stamp: File => Stamp)(f: File => T): FileValueCache[T] = new FileValueCache0[T](stamp, f) -} \ No newline at end of file diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala deleted file mode 100644 index b38fed952..000000000 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ /dev/null @@ -1,338 +0,0 @@ -package sbt.inc - -import java.io.File - -/** - * Represents all configuration options for the incremental compiler itself and - * not the underlying Java/Scala compiler. - * - * NOTE: This class used to be a case class but due to problems with retaining - * binary compatibility while new fields are added it has been expanded to a - * regular class. All compiler-generated methods for a case class has been - * defined explicitly. - */ -final class IncOptions( - /** After which step include whole transitive closure of invalidated source files. */ - val transitiveStep: Int, - /** - * What's the fraction of invalidated source files when we switch to recompiling - * all files and giving up incremental compilation altogether. That's useful in - * cases when probability that we end up recompiling most of source files but - * in multiple steps is high. Multi-step incremental recompilation is slower - * than recompiling everything in one step. - */ - val recompileAllFraction: Double, - /** Print very detailed information about relations, such as dependencies between source files. */ - val relationsDebug: Boolean, - /** - * Enable tools for debugging API changes. At the moment this option is unused but in the - * future it will enable for example: - * - disabling API hashing and API minimization (potentially very memory consuming) - * - diffing textual API representation which helps understanding what kind of changes - * to APIs are visible to the incremental compiler - */ - val apiDebug: Boolean, - /** - * Controls context size (in lines) displayed when diffs are produced for textual API - * representation. - * - * This option is used only when `apiDebug == true`. - */ - val apiDiffContextSize: Int, - /** - * The directory where we dump textual representation of APIs. This method might be called - * only if apiDebug returns true. This is unused option at the moment as the needed functionality - * is not implemented yet. - */ - val apiDumpDirectory: Option[java.io.File], - /** Creates a new ClassfileManager that will handle class file deletion and addition during a single incremental compilation run. */ - val newClassfileManager: () => ClassfileManager, - /** - * Determines whether incremental compiler should recompile all dependencies of a file - * that contains a macro definition. - */ - val recompileOnMacroDef: Boolean, - /** - * Determines whether incremental compiler uses the new algorithm known as name hashing. - * - * This flag is disabled by default so incremental compiler's behavior is the same as in sbt 0.13.0. - * - * IMPLEMENTATION NOTE: - * Enabling this flag enables a few additional functionalities that are needed by the name hashing algorithm: - * - * 1. New dependency source tracking is used. See `sbt.inc.Relations` for details. - * 2. Used names extraction and tracking is enabled. See `sbt.inc.Relations` for details as well. - * 3. Hashing of public names is enabled. See `sbt.inc.AnalysisCallback` for details. - * - */ - val nameHashing: Boolean, - /** - * THE `antStyle` OPTION IS UNSUPPORTED, MAY GO AWAY AT ANY POINT. - * - * Enables "ant-style" mode of incremental compilation. This mode emulates what Ant's scalac command does. - * The idea is to recompile just changed source files and not perform any invalidation of dependencies. This - * is a very naive mode of incremental compilation that very often leads to broken binaries. - * - * The Ant-style mode has been introduced because Scala team needs it for migration of Scala compiler to sbt. - * The name hashing algorithm doesn't work well with Scala compiler sources due to deep inheritance chains. - * There's a plan to refactor compiler's code to use more composition instead of inheritance. - * - * Once Scala compiler sources are refactored to work well with name hashing algorithm this option will be - * deleted immediately. - */ - val antStyle: Boolean) extends Product with Serializable { - - /** - * Secondary constructor introduced to make IncOptions to be binary compatible with version that didn't have - * `recompileOnMacroDef` and `nameHashing` fields defined. - */ - def this(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean, - apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], newClassfileManager: () => ClassfileManager) = { - this(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, IncOptions.recompileOnMacroDefDefault, IncOptions.nameHashingDefault, - IncOptions.antStyleDefault) - } - - assert(!(antStyle && nameHashing), "Name hashing and Ant-style cannot be enabled at the same time.") - - def withTransitiveStep(transitiveStep: Int): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withRelationsDebug(relationsDebug: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withApiDebug(apiDebug: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withRecompileOnMacroDef(recompileOnMacroDef: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withNameHashing(nameHashing: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - def withAntStyle(antStyle: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - //- EXPANDED CASE CLASS METHOD BEGIN -// - @deprecated("Use `with$nameOfTheField` copying methods instead.", "0.13.2") - def copy(transitiveStep: Int = this.transitiveStep, recompileAllFraction: Double = this.recompileAllFraction, - relationsDebug: Boolean = this.relationsDebug, apiDebug: Boolean = this.apiDebug, - apiDiffContextSize: Int = this.apiDiffContextSize, - apiDumpDirectory: Option[java.io.File] = this.apiDumpDirectory, - newClassfileManager: () => ClassfileManager = this.newClassfileManager): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle) - } - - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - override def productPrefix: String = "IncOptions" - - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - def productArity: Int = 10 - - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - def productElement(x$1: Int): Any = x$1 match { - case 0 => IncOptions.this.transitiveStep - case 1 => IncOptions.this.recompileAllFraction - case 2 => IncOptions.this.relationsDebug - case 3 => IncOptions.this.apiDebug - case 4 => IncOptions.this.apiDiffContextSize - case 5 => IncOptions.this.apiDumpDirectory - case 6 => IncOptions.this.newClassfileManager - case 7 => IncOptions.this.recompileOnMacroDef - case 8 => IncOptions.this.nameHashing - case 9 => IncOptions.this.antStyle - case _ => throw new IndexOutOfBoundsException(x$1.toString()) - } - - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - override def productIterator: Iterator[Any] = scala.runtime.ScalaRunTime.typedProductIterator[Any](IncOptions.this) - - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - def canEqual(x$1: Any): Boolean = x$1.isInstanceOf[IncOptions] - - override def hashCode(): Int = { - import scala.runtime.Statics - var acc: Int = -889275714 - acc = Statics.mix(acc, transitiveStep) - acc = Statics.mix(acc, Statics.doubleHash(recompileAllFraction)) - acc = Statics.mix(acc, if (relationsDebug) 1231 else 1237) - acc = Statics.mix(acc, if (apiDebug) 1231 else 1237) - acc = Statics.mix(acc, apiDiffContextSize) - acc = Statics.mix(acc, Statics.anyHash(apiDumpDirectory)) - acc = Statics.mix(acc, Statics.anyHash(newClassfileManager)) - acc = Statics.mix(acc, if (recompileOnMacroDef) 1231 else 1237) - acc = Statics.mix(acc, if (nameHashing) 1231 else 1237) - acc = Statics.mix(acc, if (antStyle) 1231 else 1237) - Statics.finalizeHash(acc, 9) - } - - override def toString(): String = scala.runtime.ScalaRunTime._toString(IncOptions.this) - - override def equals(x$1: Any): Boolean = { - this.eq(x$1.asInstanceOf[Object]) || (x$1.isInstanceOf[IncOptions] && ({ - val IncOptions$1: IncOptions = x$1.asInstanceOf[IncOptions] - transitiveStep == IncOptions$1.transitiveStep && recompileAllFraction == IncOptions$1.recompileAllFraction && - relationsDebug == IncOptions$1.relationsDebug && apiDebug == IncOptions$1.apiDebug && - apiDiffContextSize == IncOptions$1.apiDiffContextSize && apiDumpDirectory == IncOptions$1.apiDumpDirectory && - newClassfileManager == IncOptions$1.newClassfileManager && - recompileOnMacroDef == IncOptions$1.recompileOnMacroDef && nameHashing == IncOptions$1.nameHashing && - antStyle == IncOptions$1.antStyle - })) - } - //- EXPANDED CASE CLASS METHOD END -// -} - -object IncOptions extends Serializable { - private val recompileOnMacroDefDefault: Boolean = true - private[sbt] val nameHashingDefault: Boolean = true - private val antStyleDefault: Boolean = false - val Default = IncOptions( - // 1. recompile changed sources - // 2(3). recompile direct dependencies and transitive public inheritance dependencies of sources with API changes in 1(2). - // 4. further changes invalidate all dependencies transitively to avoid too many steps - transitiveStep = 3, - recompileAllFraction = 0.5, - relationsDebug = false, - apiDebug = false, - apiDiffContextSize = 5, - apiDumpDirectory = None, - newClassfileManager = ClassfileManager.deleteImmediately, - recompileOnMacroDef = recompileOnMacroDefDefault, - nameHashing = nameHashingDefault - ) - //- EXPANDED CASE CLASS METHOD BEGIN -// - final override def toString(): String = "IncOptions" - @deprecated("Use overloaded variant of `apply` with complete list of arguments instead.", "0.13.2") - def apply(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean, - apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], - newClassfileManager: () => ClassfileManager): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) - } - def apply(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean, - apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], - newClassfileManager: () => ClassfileManager, recompileOnMacroDef: Boolean, - nameHashing: Boolean): IncOptions = { - new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyleDefault) - } - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") - def unapply(x$0: IncOptions): Option[(Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef)] = { - if (x$0 == null) None - else Some.apply[(Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef)]( - Tuple7.apply[Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef]( - x$0.transitiveStep, x$0.recompileAllFraction, x$0.relationsDebug, x$0.apiDebug, x$0.apiDiffContextSize, - x$0.apiDumpDirectory, x$0.newClassfileManager)) - } - private def readResolve(): Object = IncOptions - //- EXPANDED CASE CLASS METHOD END -// - - @deprecated("Use IncOptions.Default.withNewClassfileManager(ClassfileManager.transactional(tempDir)), instead.", "0.13.5") - def defaultTransactional(tempDir: File): IncOptions = - setTransactional(Default, tempDir) - @deprecated("Use opts.withNewClassfileManager(ClassfileManager.transactional(tempDir)), instead.", "0.13.5") - def setTransactional(opts: IncOptions, tempDir: File): IncOptions = - opts.withNewClassfileManager(ClassfileManager.transactional(tempDir, sbt.Logger.Null)) - - private val transitiveStepKey = "transitiveStep" - private val recompileAllFractionKey = "recompileAllFraction" - private val relationsDebugKey = "relationsDebug" - private val apiDebugKey = "apiDebug" - private val apiDumpDirectoryKey = "apiDumpDirectory" - private val apiDiffContextSizeKey = "apiDiffContextSize" - private val recompileOnMacroDefKey = "recompileOnMacroDef" - private val nameHashingKey = "nameHashing" - private val antStyleKey = "antStyle" - - def fromStringMap(m: java.util.Map[String, String]): IncOptions = { - // all the code below doesn't look like idiomatic Scala for a good reason: we are working with Java API - def getTransitiveStep: Int = { - val k = transitiveStepKey - if (m.containsKey(k)) m.get(k).toInt else Default.transitiveStep - } - def getRecompileAllFraction: Double = { - val k = recompileAllFractionKey - if (m.containsKey(k)) m.get(k).toDouble else Default.recompileAllFraction - } - def getRelationsDebug: Boolean = { - val k = relationsDebugKey - if (m.containsKey(k)) m.get(k).toBoolean else Default.relationsDebug - } - def getApiDebug: Boolean = { - val k = apiDebugKey - if (m.containsKey(k)) m.get(k).toBoolean else Default.apiDebug - } - def getApiDiffContextSize: Int = { - val k = apiDiffContextSizeKey - if (m.containsKey(k)) m.get(k).toInt else Default.apiDiffContextSize - } - def getApiDumpDirectory: Option[java.io.File] = { - val k = apiDumpDirectoryKey - if (m.containsKey(k)) - Some(new java.io.File(m.get(k))) - else None - } - def getRecompileOnMacroDef: Boolean = { - val k = recompileOnMacroDefKey - if (m.containsKey(k)) m.get(k).toBoolean else Default.recompileOnMacroDef - } - def getNameHashing: Boolean = { - val k = nameHashingKey - if (m.containsKey(k)) m.get(k).toBoolean else Default.nameHashing - } - - def getAntStyle: Boolean = { - val k = antStyleKey - if (m.containsKey(k)) m.get(k).toBoolean else Default.antStyle - } - - new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize, - getApiDumpDirectory, ClassfileManager.deleteImmediately, getRecompileOnMacroDef, getNameHashing, getAntStyle) - } - - def toStringMap(o: IncOptions): java.util.Map[String, String] = { - val m = new java.util.HashMap[String, String] - m.put(transitiveStepKey, o.transitiveStep.toString) - m.put(recompileAllFractionKey, o.recompileAllFraction.toString) - m.put(relationsDebugKey, o.relationsDebug.toString) - m.put(apiDebugKey, o.apiDebug.toString) - o.apiDumpDirectory.foreach(f => m.put(apiDumpDirectoryKey, f.toString)) - m.put(apiDiffContextSizeKey, o.apiDiffContextSize.toString) - m.put(recompileOnMacroDefKey, o.recompileOnMacroDef.toString) - m.put(nameHashingKey, o.nameHashing.toString) - m - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/Incremental.scala b/compile/inc/src/main/scala/sbt/inc/Incremental.scala deleted file mode 100644 index 5966ea52d..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Incremental.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbt.api.{ NameChanges, SameAPI, TopLevel } -import annotation.tailrec -import xsbti.api.{ Compilation, Source } -import xsbti.compile.DependencyChanges -import java.io.File - -/** - * Helper class to run incremental compilation algorithm. - * - * - * This class delegates down to - * - IncrementalNameHashing - * - IncrementalDefault - * - IncrementalAnyStyle - */ -object Incremental { - /** - * Runs the incremental compiler algorithm. - * - * @param sources The sources to compile - * @param entry The means of looking up a class on the classpath. - * @param previous The previously detected source dependencies. - * @param current A mechanism for generating stamps (timestamps, hashes, etc). - * @param doCompile The function which can run one level of compile. - * @param log The log where we write debugging information - * @param options Incremental compilation options - * @param equivS The means of testing whether two "Stamps" are the same. - * @return - * A flag of whether or not compilation completed succesfully, and the resulting dependency analysis object. - */ - def compile(sources: Set[File], - entry: String => Option[File], - previous: Analysis, - current: ReadStamps, - forEntry: File => Option[Analysis], - doCompile: (Set[File], DependencyChanges) => Analysis, - log: Logger, - options: IncOptions)(implicit equivS: Equiv[Stamp]): (Boolean, Analysis) = - { - val incremental: IncrementalCommon = - if (options.nameHashing) - new IncrementalNameHashing(log, options) - else if (options.antStyle) - new IncrementalAntStyle(log, options) - else - new IncrementalDefaultImpl(log, options) - val initialChanges = incremental.changedInitial(entry, sources, previous, current, forEntry) - val binaryChanges = new DependencyChanges { - val modifiedBinaries = initialChanges.binaryDeps.toArray - val modifiedClasses = initialChanges.external.allModified.toArray - def isEmpty = modifiedBinaries.isEmpty && modifiedClasses.isEmpty - } - val initialInv = incremental.invalidateInitial(previous.relations, initialChanges) - log.debug("All initially invalidated sources: " + initialInv + "\n") - val analysis = manageClassfiles(options) { classfileManager => - incremental.cycle(initialInv, sources, binaryChanges, previous, doCompile, classfileManager, 1) - } - (initialInv.nonEmpty, analysis) - } - - // the name of system property that was meant to enable debugging mode of incremental compiler but - // it ended up being used just to enable debugging of relations. That's why if you migrate to new - // API for configuring incremental compiler (IncOptions) it's enough to control value of `relationsDebug` - // flag to achieve the same effect as using `incDebugProp`. - @deprecated("Use `IncOptions.relationsDebug` flag to enable debugging of relations.", "0.13.2") - val incDebugProp = "xsbt.inc.debug" - - private[inc] val apiDebugProp = "xsbt.api.debug" - private[inc] def apiDebug(options: IncOptions): Boolean = options.apiDebug || java.lang.Boolean.getBoolean(apiDebugProp) - - private[sbt] def prune(invalidatedSrcs: Set[File], previous: Analysis): Analysis = - prune(invalidatedSrcs, previous, ClassfileManager.deleteImmediately()) - - private[sbt] def prune(invalidatedSrcs: Set[File], previous: Analysis, classfileManager: ClassfileManager): Analysis = - { - classfileManager.delete(invalidatedSrcs.flatMap(previous.relations.products)) - previous -- invalidatedSrcs - } - - private[this] def manageClassfiles[T](options: IncOptions)(run: ClassfileManager => T): T = - { - val classfileManager = options.newClassfileManager() - val result = try run(classfileManager) catch { - case e: Exception => - classfileManager.complete(success = false) - throw e - } - classfileManager.complete(success = true) - result - } - -} diff --git a/compile/inc/src/main/scala/sbt/inc/IncrementalAntStyle.scala b/compile/inc/src/main/scala/sbt/inc/IncrementalAntStyle.scala deleted file mode 100644 index c1d392c47..000000000 --- a/compile/inc/src/main/scala/sbt/inc/IncrementalAntStyle.scala +++ /dev/null @@ -1,24 +0,0 @@ -package sbt -package inc - -import java.io.File -import xsbti.api.Source - -private final class IncrementalAntStyle(log: Logger, options: IncOptions) extends IncrementalCommon(log, options) { - - /** Ant-style mode doesn't do anything special with package objects */ - override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] = Set.empty - - /** In Ant-style mode we don't need to compare APIs because we don't perform any invalidation */ - override protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] = None - - /** In Ant-style mode we don't perform any invalidation */ - override protected def invalidateByExternal(relations: Relations, externalAPIChange: APIChange[String]): Set[File] = Set.empty - - /** In Ant-style mode we don't perform any invalidation */ - override protected def invalidateSource(relations: Relations, change: APIChange[File]): Set[File] = Set.empty - - /** In Ant-style mode we don't need to perform any dependency analysis hence we can always return an empty set. */ - override protected def allDeps(relations: Relations): File => Set[File] = _ => Set.empty - -} diff --git a/compile/inc/src/main/scala/sbt/inc/IncrementalCommon.scala b/compile/inc/src/main/scala/sbt/inc/IncrementalCommon.scala deleted file mode 100644 index e11939285..000000000 --- a/compile/inc/src/main/scala/sbt/inc/IncrementalCommon.scala +++ /dev/null @@ -1,338 +0,0 @@ -package sbt -package inc - -import scala.annotation.tailrec -import xsbti.compile.DependencyChanges -import xsbti.api.{ Compilation, Source } -import java.io.File - -private[inc] abstract class IncrementalCommon(log: Logger, options: IncOptions) { - - private def incDebug(options: IncOptions): Boolean = options.relationsDebug || java.lang.Boolean.getBoolean(Incremental.incDebugProp) - - // setting the related system property to true will skip checking that the class name - // still comes from the same classpath entry. This can workaround bugs in classpath construction, - // such as the currently problematic -javabootclasspath. This is subject to removal at any time. - private[this] def skipClasspathLookup = java.lang.Boolean.getBoolean("xsbt.skip.cp.lookup") - - // TODO: the Analysis for the last successful compilation should get returned + Boolean indicating success - // TODO: full external name changes, scopeInvalidations - @tailrec final def cycle(invalidatedRaw: Set[File], allSources: Set[File], binaryChanges: DependencyChanges, previous: Analysis, - doCompile: (Set[File], DependencyChanges) => Analysis, classfileManager: ClassfileManager, cycleNum: Int): Analysis = - if (invalidatedRaw.isEmpty) - previous - else { - def debug(s: => String) = if (incDebug(options)) log.debug(s) else () - val withPackageObjects = invalidatedRaw ++ invalidatedPackageObjects(invalidatedRaw, previous.relations) - val invalidated = expand(withPackageObjects, allSources) - val pruned = Incremental.prune(invalidated, previous, classfileManager) - debug("********* Pruned: \n" + pruned.relations + "\n*********") - - val fresh = doCompile(invalidated, binaryChanges) - classfileManager.generated(fresh.relations.allProducts) - debug("********* Fresh: \n" + fresh.relations + "\n*********") - val merged = pruned ++ fresh //.copy(relations = pruned.relations ++ fresh.relations, apis = pruned.apis ++ fresh.apis) - debug("********* Merged: \n" + merged.relations + "\n*********") - - val incChanges = changedIncremental(invalidated, previous.apis.internalAPI _, merged.apis.internalAPI _) - debug("\nChanges:\n" + incChanges) - val transitiveStep = options.transitiveStep - val incInv = invalidateIncremental(merged.relations, merged.apis, incChanges, invalidated, cycleNum >= transitiveStep) - cycle(incInv, allSources, emptyChanges, merged, doCompile, classfileManager, cycleNum + 1) - } - private[this] def emptyChanges: DependencyChanges = new DependencyChanges { - val modifiedBinaries = new Array[File](0) - val modifiedClasses = new Array[String](0) - def isEmpty = true - } - private[this] def expand(invalidated: Set[File], all: Set[File]): Set[File] = { - val recompileAllFraction = options.recompileAllFraction - if (invalidated.size > all.size * recompileAllFraction) { - log.debug("Recompiling all " + all.size + " sources: invalidated sources (" + invalidated.size + ") exceeded " + (recompileAllFraction * 100.0) + "% of all sources") - all ++ invalidated // need the union because all doesn't contain removed sources - } else invalidated - } - - protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] - - /** - * Logs API changes using debug-level logging. The API are obtained using the APIDiff class. - * - * NOTE: This method creates a new APIDiff instance on every invocation. - */ - private def logApiChanges[T](apiChanges: Iterable[APIChange[T]], oldAPIMapping: T => Source, - newAPIMapping: T => Source): Unit = { - val contextSize = options.apiDiffContextSize - try { - val apiDiff = new APIDiff - apiChanges foreach { - case APIChangeDueToMacroDefinition(src) => - log.debug(s"Public API is considered to be changed because $src contains a macro definition.") - case apiChange @ (_: SourceAPIChange[T] | _: NamesChange[T]) => - val src = apiChange.modified - val oldApi = oldAPIMapping(src) - val newApi = newAPIMapping(src) - val apiUnifiedPatch = apiDiff.generateApiDiff(src.toString, oldApi.api, newApi.api, contextSize) - log.debug(s"Detected a change in a public API (${src.toString}):\n" - + apiUnifiedPatch) - } - } catch { - case e: ClassNotFoundException => - log.error("You have api debugging enabled but DiffUtils library cannot be found on sbt's classpath") - case e: LinkageError => - log.error("Encoutared linkage error while trying to load DiffUtils library.") - log.trace(e) - case e: Exception => - log.error("An exception has been thrown while trying to dump an api diff.") - log.trace(e) - } - } - - /** - * Accepts the sources that were recompiled during the last step and functions - * providing the API before and after the last step. The functions should return - * an empty API if the file did not/does not exist. - */ - def changedIncremental[T](lastSources: collection.Set[T], oldAPI: T => Source, newAPI: T => Source): APIChanges[T] = - { - val oldApis = lastSources.toSeq map oldAPI - val newApis = lastSources.toSeq map newAPI - val apiChanges = (lastSources, oldApis, newApis).zipped.flatMap { (src, oldApi, newApi) => sameSource(src, oldApi, newApi) } - - if (Incremental.apiDebug(options) && apiChanges.nonEmpty) { - logApiChanges(apiChanges, oldAPI, newAPI) - } - - new APIChanges(apiChanges) - } - def sameSource[T](src: T, a: Source, b: Source): Option[APIChange[T]] = { - // Clients of a modified source file (ie, one that doesn't satisfy `shortcutSameSource`) containing macros must be recompiled. - val hasMacro = a.hasMacro || b.hasMacro - if (shortcutSameSource(a, b)) { - None - } else { - if (hasMacro && options.recompileOnMacroDef) { - Some(APIChangeDueToMacroDefinition(src)) - } else sameAPI(src, a, b) - } - } - - protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] - - def shortcutSameSource(a: Source, b: Source): Boolean = a.hash.nonEmpty && b.hash.nonEmpty && sameCompilation(a.compilation, b.compilation) && (a.hash.deep equals b.hash.deep) - def sameCompilation(a: Compilation, b: Compilation): Boolean = a.startTime == b.startTime && a.outputs.corresponds(b.outputs) { - case (co1, co2) => co1.sourceDirectory == co2.sourceDirectory && co1.outputDirectory == co2.outputDirectory - } - - def changedInitial(entry: String => Option[File], sources: Set[File], previousAnalysis: Analysis, current: ReadStamps, - forEntry: File => Option[Analysis])(implicit equivS: Equiv[Stamp]): InitialChanges = - { - val previous = previousAnalysis.stamps - val previousAPIs = previousAnalysis.apis - - val srcChanges = changes(previous.allInternalSources.toSet, sources, f => !equivS.equiv(previous.internalSource(f), current.internalSource(f))) - val removedProducts = previous.allProducts.filter(p => !equivS.equiv(previous.product(p), current.product(p))).toSet - val binaryDepChanges = previous.allBinaries.filter(externalBinaryModified(entry, forEntry, previous, current)).toSet - val extChanges = changedIncremental(previousAPIs.allExternals, previousAPIs.externalAPI _, currentExternalAPI(entry, forEntry)) - - InitialChanges(srcChanges, removedProducts, binaryDepChanges, extChanges) - } - - def changes(previous: Set[File], current: Set[File], existingModified: File => Boolean): Changes[File] = - new Changes[File] { - private val inBoth = previous & current - val removed = previous -- inBoth - val added = current -- inBoth - val (changed, unmodified) = inBoth.partition(existingModified) - } - - def invalidateIncremental(previous: Relations, apis: APIs, changes: APIChanges[File], recompiledSources: Set[File], transitive: Boolean): Set[File] = - { - val dependsOnSrc = previous.usesInternalSrc _ - val propagated = - if (transitive) - transitiveDependencies(dependsOnSrc, changes.allModified.toSet) - else - invalidateIntermediate(previous, changes) - - val dups = invalidateDuplicates(previous) - if (dups.nonEmpty) - log.debug("Invalidated due to generated class file collision: " + dups) - - val inv = propagated ++ dups // ++ scopeInvalidations(previous.extAPI _, changes.modified, changes.names) - val newlyInvalidated = inv -- recompiledSources - log.debug("All newly invalidated sources after taking into account (previously) recompiled sources:" + newlyInvalidated) - if (newlyInvalidated.isEmpty) Set.empty else inv - } - - /** Invalidate all sources that claim to produce the same class file as another source file. */ - def invalidateDuplicates(merged: Relations): Set[File] = - merged.srcProd.reverseMap.flatMap { - case (classFile, sources) => - if (sources.size > 1) sources else Nil - } toSet; - - /** - * Returns the transitive source dependencies of `initial`. - * Because the intermediate steps do not pull in cycles, this result includes the initial files - * if they are part of a cycle containing newly invalidated files . - */ - def transitiveDependencies(dependsOnSrc: File => Set[File], initial: Set[File]): Set[File] = - { - val transitiveWithInitial = transitiveDeps(initial)(dependsOnSrc) - val transitivePartial = includeInitialCond(initial, transitiveWithInitial, dependsOnSrc) - log.debug("Final step, transitive dependencies:\n\t" + transitivePartial) - transitivePartial - } - - /** Invalidates sources based on initially detected 'changes' to the sources, products, and dependencies.*/ - def invalidateInitial(previous: Relations, changes: InitialChanges): Set[File] = - { - val srcChanges = changes.internalSrc - val srcDirect = srcChanges.removed ++ srcChanges.removed.flatMap(previous.usesInternalSrc) ++ srcChanges.added ++ srcChanges.changed - val byProduct = changes.removedProducts.flatMap(previous.produced) - val byBinaryDep = changes.binaryDeps.flatMap(previous.usesBinary) - val byExtSrcDep = invalidateByAllExternal(previous, changes.external) //changes.external.modified.flatMap(previous.usesExternal) // ++ scopeInvalidations - checkAbsolute(srcChanges.added.toList) - log.debug( - "\nInitial source changes: \n\tremoved:" + srcChanges.removed + "\n\tadded: " + srcChanges.added + "\n\tmodified: " + srcChanges.changed + - "\nInvalidated products: " + changes.removedProducts + - "\nExternal API changes: " + changes.external + - "\nModified binary dependencies: " + changes.binaryDeps + - "\nInitial directly invalidated sources: " + srcDirect + - "\n\nSources indirectly invalidated by:" + - "\n\tproduct: " + byProduct + - "\n\tbinary dep: " + byBinaryDep + - "\n\texternal source: " + byExtSrcDep - ) - - srcDirect ++ byProduct ++ byBinaryDep ++ byExtSrcDep - } - private[this] def checkAbsolute(addedSources: List[File]): Unit = - if (addedSources.nonEmpty) { - addedSources.filterNot(_.isAbsolute) match { - case first :: more => - val fileStrings = more match { - case Nil => first.toString - case x :: Nil => s"$first and $x" - case _ => s"$first and ${more.size} others" - } - sys.error(s"The incremental compiler requires absolute sources, but some were relative: $fileStrings") - case Nil => - } - } - - def invalidateByAllExternal(relations: Relations, externalAPIChanges: APIChanges[String]): Set[File] = { - (externalAPIChanges.apiChanges.flatMap { externalAPIChange => - invalidateByExternal(relations, externalAPIChange) - }).toSet - } - - /** Sources invalidated by `external` sources in other projects according to the previous `relations`. */ - protected def invalidateByExternal(relations: Relations, externalAPIChange: APIChange[String]): Set[File] - - /** Intermediate invalidation step: steps after the initial invalidation, but before the final transitive invalidation. */ - def invalidateIntermediate(relations: Relations, changes: APIChanges[File]): Set[File] = - { - invalidateSources(relations, changes) - } - /** - * Invalidates inheritance dependencies, transitively. Then, invalidates direct dependencies. Finally, excludes initial dependencies not - * included in a cycle with newly invalidated sources. - */ - private[this] def invalidateSources(relations: Relations, changes: APIChanges[File]): Set[File] = - { - val initial = changes.allModified.toSet - val all = (changes.apiChanges flatMap { change => - invalidateSource(relations, change) - }).toSet - includeInitialCond(initial, all, allDeps(relations)) - } - - protected def allDeps(relations: Relations): File => Set[File] - - protected def invalidateSource(relations: Relations, change: APIChange[File]): Set[File] - - /** - * Conditionally include initial sources that are dependencies of newly invalidated sources. - * * Initial sources included in this step can be because of a cycle, but not always. - */ - private[this] def includeInitialCond(initial: Set[File], currentInvalidations: Set[File], allDeps: File => Set[File]): Set[File] = - { - val newInv = currentInvalidations -- initial - log.debug("New invalidations:\n\t" + newInv) - val transitiveOfNew = transitiveDeps(newInv)(allDeps) - val initialDependsOnNew = transitiveOfNew & initial - log.debug("Previously invalidated, but (transitively) depend on new invalidations:\n\t" + initialDependsOnNew) - newInv ++ initialDependsOnNew - } - - def externalBinaryModified(entry: String => Option[File], analysis: File => Option[Analysis], previous: Stamps, current: ReadStamps)(implicit equivS: Equiv[Stamp]): File => Boolean = - dependsOn => - { - def inv(reason: String): Boolean = { - log.debug("Invalidating " + dependsOn + ": " + reason) - true - } - def entryModified(className: String, classpathEntry: File): Boolean = - { - val resolved = Locate.resolve(classpathEntry, className) - if (resolved.getCanonicalPath != dependsOn.getCanonicalPath) - inv("class " + className + " now provided by " + resolved.getCanonicalPath) - else - fileModified(dependsOn, resolved) - } - def fileModified(previousFile: File, currentFile: File): Boolean = - { - val previousStamp = previous.binary(previousFile) - val currentStamp = current.binary(currentFile) - if (equivS.equiv(previousStamp, currentStamp)) - false - else - inv("stamp changed from " + previousStamp + " to " + currentStamp) - } - def dependencyModified(file: File): Boolean = - previous.className(file) match { - case None => inv("no class name was mapped for it.") - case Some(name) => entry(name) match { - case None => inv("could not find class " + name + " on the classpath.") - case Some(e) => entryModified(name, e) - } - } - - analysis(dependsOn).isEmpty && - (if (skipClasspathLookup) fileModified(dependsOn, dependsOn) else dependencyModified(dependsOn)) - - } - - def currentExternalAPI(entry: String => Option[File], forEntry: File => Option[Analysis]): String => Source = - className => - orEmpty( - for { - e <- entry(className) - analysis <- forEntry(e) - src <- analysis.relations.definesClass(className).headOption - } yield analysis.apis.internalAPI(src) - ) - - def orEmpty(o: Option[Source]): Source = o getOrElse APIs.emptySource - def orTrue(o: Option[Boolean]): Boolean = o getOrElse true - - protected def transitiveDeps[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): Set[T] = - { - val xs = new collection.mutable.HashSet[T] - def all(from: T, tos: Iterable[T]): Unit = tos.foreach(to => visit(from, to)) - def visit(from: T, to: T): Unit = - if (!xs.contains(to)) { - log.debug(s"Including $to by $from") - xs += to - all(to, dependencies(to)) - } - log.debug("Initial set of included nodes: " + nodes) - nodes foreach { start => - xs += start - all(start, dependencies(start)) - } - xs.toSet - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/IncrementalDefaultImpl.scala b/compile/inc/src/main/scala/sbt/inc/IncrementalDefaultImpl.scala deleted file mode 100644 index c43fd5b90..000000000 --- a/compile/inc/src/main/scala/sbt/inc/IncrementalDefaultImpl.scala +++ /dev/null @@ -1,56 +0,0 @@ -package sbt -package inc - -import xsbti.api.Source -import xsbt.api.SameAPI -import java.io.File - -private final class IncrementalDefaultImpl(log: Logger, options: IncOptions) extends IncrementalCommon(log, options) { - - // Package objects are fragile: if they inherit from an invalidated source, get "class file needed by package is missing" error - // This might be too conservative: we probably only need package objects for packages of invalidated sources. - override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] = - invalidated flatMap relations.publicInherited.internal.reverse filter { _.getName == "package.scala" } - - override protected def sameAPI[T](src: T, a: Source, b: Source): Option[SourceAPIChange[T]] = { - if (SameAPI(a, b)) - None - else { - val sourceApiChange = SourceAPIChange(src) - Some(sourceApiChange) - } - } - - /** Invalidates sources based on initially detected 'changes' to the sources, products, and dependencies.*/ - override protected def invalidateByExternal(relations: Relations, externalAPIChange: APIChange[String]): Set[File] = { - val modified = externalAPIChange.modified - // Propagate public inheritance dependencies transitively. - // This differs from normal because we need the initial crossing from externals to sources in this project. - val externalInheritedR = relations.publicInherited.external - val byExternalInherited = externalInheritedR.reverse(modified) - val internalInheritedR = relations.publicInherited.internal - val transitiveInherited = transitiveDeps(byExternalInherited)(internalInheritedR.reverse _) - - // Get the direct dependencies of all sources transitively invalidated by inheritance - val directA = transitiveInherited flatMap relations.direct.internal.reverse - // Get the sources that directly depend on externals. This includes non-inheritance dependencies and is not transitive. - val directB = relations.direct.external.reverse(modified) - transitiveInherited ++ directA ++ directB - } - - override protected def invalidateSource(relations: Relations, change: APIChange[File]): Set[File] = { - def reverse(r: Relations.Source) = r.internal.reverse _ - val directDeps: File => Set[File] = reverse(relations.direct) - val publicInherited: File => Set[File] = reverse(relations.publicInherited) - log.debug("Invalidating by inheritance (transitively)...") - val transitiveInherited = transitiveDeps(Set(change.modified))(publicInherited) - log.debug("Invalidated by transitive public inheritance: " + transitiveInherited) - val direct = transitiveInherited flatMap directDeps - log.debug("Invalidated by direct dependency: " + direct) - transitiveInherited ++ direct - } - - override protected def allDeps(relations: Relations): File => Set[File] = - f => relations.direct.internal.reverse(f) - -} \ No newline at end of file diff --git a/compile/inc/src/main/scala/sbt/inc/IncrementalNameHashing.scala b/compile/inc/src/main/scala/sbt/inc/IncrementalNameHashing.scala deleted file mode 100644 index 1aaff67b4..000000000 --- a/compile/inc/src/main/scala/sbt/inc/IncrementalNameHashing.scala +++ /dev/null @@ -1,86 +0,0 @@ -package sbt -package inc - -import xsbti.api.Source -import xsbt.api.SameAPI -import java.io.File - -/** - * Implementation of incremental algorithm known as "name hashing". It differs from the default implementation - * by applying pruning (filter) of member reference dependencies based on used and modified simple names. - * - * See MemberReferenceInvalidationStrategy for some more information. - */ -private final class IncrementalNameHashing(log: Logger, options: IncOptions) extends IncrementalCommon(log, options) { - - private val memberRefInvalidator = new MemberRefInvalidator(log) - - // Package objects are fragile: if they inherit from an invalidated source, get "class file needed by package is missing" error - // This might be too conservative: we probably only need package objects for packages of invalidated sources. - override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] = - invalidated flatMap relations.inheritance.internal.reverse filter { _.getName == "package.scala" } - - override protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] = { - if (SameAPI(a, b)) - None - else { - val aNameHashes = a._internalOnly_nameHashes - val bNameHashes = b._internalOnly_nameHashes - val modifiedNames = ModifiedNames.compareTwoNameHashes(aNameHashes, bNameHashes) - val apiChange = NamesChange(src, modifiedNames) - Some(apiChange) - } - } - - /** Invalidates sources based on initially detected 'changes' to the sources, products, and dependencies.*/ - override protected def invalidateByExternal(relations: Relations, externalAPIChange: APIChange[String]): Set[File] = { - val modified = externalAPIChange.modified - val invalidationReason = memberRefInvalidator.invalidationReason(externalAPIChange) - log.debug(s"$invalidationReason\nAll member reference dependencies will be considered within this context.") - // Propagate inheritance dependencies transitively. - // This differs from normal because we need the initial crossing from externals to sources in this project. - val externalInheritanceR = relations.inheritance.external - val byExternalInheritance = externalInheritanceR.reverse(modified) - log.debug(s"Files invalidated by inheriting from (external) $modified: $byExternalInheritance; now invalidating by inheritance (internally).") - val transitiveInheritance = byExternalInheritance flatMap { file => - invalidateByInheritance(relations, file) - } - val memberRefInvalidationInternal = memberRefInvalidator.get(relations.memberRef.internal, - relations.names, externalAPIChange) - val memberRefInvalidationExternal = memberRefInvalidator.get(relations.memberRef.external, - relations.names, externalAPIChange) - - // Get the member reference dependencies of all sources transitively invalidated by inheritance - log.debug("Getting direct dependencies of all sources transitively invalidated by inheritance.") - val memberRefA = transitiveInheritance flatMap memberRefInvalidationInternal - // Get the sources that depend on externals by member reference. - // This includes non-inheritance dependencies and is not transitive. - log.debug(s"Getting sources that directly depend on (external) $modified.") - val memberRefB = memberRefInvalidationExternal(modified) - transitiveInheritance ++ memberRefA ++ memberRefB - } - - private def invalidateByInheritance(relations: Relations, modified: File): Set[File] = { - val inheritanceDeps = relations.inheritance.internal.reverse _ - log.debug(s"Invalidating (transitively) by inheritance from $modified...") - val transitiveInheritance = transitiveDeps(Set(modified))(inheritanceDeps) - log.debug("Invalidated by transitive inheritance dependency: " + transitiveInheritance) - transitiveInheritance - } - - override protected def invalidateSource(relations: Relations, change: APIChange[File]): Set[File] = { - log.debug(s"Invalidating ${change.modified}...") - val transitiveInheritance = invalidateByInheritance(relations, change.modified) - val reasonForInvalidation = memberRefInvalidator.invalidationReason(change) - log.debug(s"$reasonForInvalidation\nAll member reference dependencies will be considered within this context.") - val memberRefInvalidation = memberRefInvalidator.get(relations.memberRef.internal, - relations.names, change) - val memberRef = transitiveInheritance flatMap memberRefInvalidation - val all = transitiveInheritance ++ memberRef - all - } - - override protected def allDeps(relations: Relations): File => Set[File] = - f => relations.memberRef.internal.reverse(f) - -} diff --git a/compile/inc/src/main/scala/sbt/inc/Locate.scala b/compile/inc/src/main/scala/sbt/inc/Locate.scala deleted file mode 100644 index c6762d8a0..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Locate.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import java.io.File -import java.util.zip.{ ZipException, ZipFile } -import Function.const - -object Locate { - type DefinesClass = File => String => Boolean - - /** - * Right(src) provides the value for the found class - * Left(true) means that the class was found, but it had no associated value - * Left(false) means that the class was not found - */ - def value[S](classpath: Seq[File], get: File => String => Option[S]): String => Either[Boolean, S] = - { - val gets = classpath.toStream.map(getValue(get)) - className => find(className, gets) - } - - def find[S](name: String, gets: Stream[String => Either[Boolean, S]]): Either[Boolean, S] = - if (gets.isEmpty) - Left(false) - else - gets.head(name) match { - case Left(false) => find(name, gets.tail) - case x => x - } - - /** - * Returns a function that searches the provided class path for - * a class name and returns the entry that defines that class. - */ - def entry(classpath: Seq[File], f: DefinesClass): String => Option[File] = - { - val entries = classpath.toStream.map { entry => (entry, f(entry)) } - className => entries collect { case (entry, defines) if defines(className) => entry } headOption; - } - def resolve(f: File, className: String): File = if (f.isDirectory) classFile(f, className) else f - - def getValue[S](get: File => String => Option[S])(entry: File): String => Either[Boolean, S] = - { - val defClass = definesClass(entry) - val getF = get(entry) - className => if (defClass(className)) getF(className).toRight(true) else Left(false) - } - - def definesClass(entry: File): String => Boolean = - if (entry.isDirectory) - directoryDefinesClass(entry) - else if (entry.exists && classpath.ClasspathUtilities.isArchive(entry, contentFallback = true)) - jarDefinesClass(entry) - else - const(false) - - def jarDefinesClass(entry: File): String => Boolean = - { - import collection.JavaConversions._ - val jar = try { new ZipFile(entry, ZipFile.OPEN_READ) } catch { - // ZipException doesn't include the file name :( - case e: ZipException => throw new RuntimeException("Error opening zip file: " + entry.getName, e) - } - val entries = try { jar.entries.map(e => toClassName(e.getName)).toSet } finally { jar.close() } - entries.contains _ - } - - def toClassName(entry: String): String = - entry.stripSuffix(ClassExt).replace('/', '.') - - val ClassExt = ".class" - - def directoryDefinesClass(entry: File): String => Boolean = - className => classFile(entry, className).isFile - - def classFile(baseDir: File, className: String): File = - { - val (pkg, name) = components(className) - val dir = subDirectory(baseDir, pkg) - new File(dir, name + ClassExt) - } - - def subDirectory(base: File, parts: Seq[String]): File = - (base /: parts)((b, p) => new File(b, p)) - - def components(className: String): (Seq[String], String) = - { - assume(!className.isEmpty) - val parts = className.split("\\.") - if (parts.length == 1) (Nil, parts(0)) else (parts.init, parts.last) - } -} \ No newline at end of file diff --git a/compile/inc/src/main/scala/sbt/inc/MemberRefInvalidator.scala b/compile/inc/src/main/scala/sbt/inc/MemberRefInvalidator.scala deleted file mode 100644 index 28ac92515..000000000 --- a/compile/inc/src/main/scala/sbt/inc/MemberRefInvalidator.scala +++ /dev/null @@ -1,123 +0,0 @@ -package sbt.inc - -import sbt.Relation -import java.io.File -import sbt.Logger -import xsbt.api.APIUtil - -/** - * Implements various strategies for invalidating dependencies introduced by member reference. - * - * The strategy is represented as function T => Set[File] where T is a source file that other - * source files depend on. When you apply that function to given element `src` you get set of - * files that depend on `src` by member reference and should be invalidated due to api change - * that was passed to a method constructing that function. There are two questions that arise: - * - * 1. Why is signature T => Set[File] and not T => Set[T] or File => Set[File]? - * 2. Why would we apply that function to any other `src` that then one that got modified - * and the modification is described by APIChange? - * - * Let's address the second question with the following example of source code structure: - * - * // A.scala - * class A - * - * // B.scala - * class B extends A - * - * // C.scala - * class C { def foo(a: A) = ??? } - * - * // D.scala - * class D { def bar(b: B) = ??? } - * - * Member reference dependencies on A.scala are B.scala, C.scala. When the api of A changes - * then we would consider B and C for invalidation. However, B is also a dependency by inheritance - * so we always invalidate it. The api change to A is relevant when B is considered (because - * of how inheritance works) so we would invalidate B by inheritance and then we would like to - * invalidate member reference dependencies of B as well. In other words, we have a function - * because we want to apply it (with the same api change in mind) to all src files invalidated - * by inheritance of the originally modified file. - * - * The first question is a bit more straightforward to answer. We always invalidate internal - * source files (in given project) that are represented as File but they might depend either on - * internal source files (then T=File) or they can depend on external class name (then T=String). - * - * The specific invalidation strategy is determined based on APIChange that describes a change to api - * of a single source file. - * - * For example, if we get APIChangeDueToMacroDefinition then we invalidate all member reference - * dependencies unconditionally. On the other hand, if api change is due to modified name hashes - * of regular members then we'll invalidate sources that use those names. - */ -private[inc] class MemberRefInvalidator(log: Logger) { - def get[T](memberRef: Relation[File, T], usedNames: Relation[File, String], apiChange: APIChange[_]): T => Set[File] = apiChange match { - case _: APIChangeDueToMacroDefinition[_] => - new InvalidateUnconditionally(memberRef) - case NamesChange(_, modifiedNames) if modifiedNames.implicitNames.nonEmpty => - new InvalidateUnconditionally(memberRef) - case NamesChange(modifiedSrcFile, modifiedNames) => - new NameHashFilteredInvalidator[T](usedNames, memberRef, modifiedNames.regularNames) - case _: SourceAPIChange[_] => - sys.error(wrongAPIChangeMsg) - } - - def invalidationReason(apiChange: APIChange[_]): String = apiChange match { - case APIChangeDueToMacroDefinition(modifiedSrcFile) => - s"The $modifiedSrcFile source file declares a macro." - case NamesChange(modifiedSrcFile, modifiedNames) if modifiedNames.implicitNames.nonEmpty => - s"""|The $modifiedSrcFile source file has the following implicit definitions changed: - |\t${modifiedNames.implicitNames.mkString(", ")}.""".stripMargin - case NamesChange(modifiedSrcFile, modifiedNames) => - s"""|The $modifiedSrcFile source file has the following regular definitions changed: - |\t${modifiedNames.regularNames.mkString(", ")}.""".stripMargin - case _: SourceAPIChange[_] => - sys.error(wrongAPIChangeMsg) - } - - private val wrongAPIChangeMsg = - "MemberReferenceInvalidator.get should be called when name hashing is enabled " + - "and in that case we shouldn't have SourceAPIChange as an api change." - - private class InvalidateUnconditionally[T](memberRef: Relation[File, T]) extends (T => Set[File]) { - def apply(from: T): Set[File] = { - val invalidated = memberRef.reverse(from) - if (invalidated.nonEmpty) - log.debug(s"The following member ref dependencies of $from are invalidated:\n" + - formatInvalidated(invalidated)) - invalidated - } - private def formatInvalidated(invalidated: Set[File]): String = { - val sortedFiles = invalidated.toSeq.sortBy(_.getAbsolutePath) - sortedFiles.map(file => "\t" + file).mkString("\n") - } - } - - private class NameHashFilteredInvalidator[T]( - usedNames: Relation[File, String], - memberRef: Relation[File, T], - modifiedNames: Set[String]) extends (T => Set[File]) { - - def apply(to: T): Set[File] = { - val dependent = memberRef.reverse(to) - filteredDependencies(dependent) - } - private def filteredDependencies(dependent: Set[File]): Set[File] = { - dependent.filter { - case from if APIUtil.isScalaSourceName(from.getName) => - val usedNamesInDependent = usedNames.forward(from) - val modifiedAndUsedNames = modifiedNames intersect usedNamesInDependent - if (modifiedAndUsedNames.isEmpty) { - log.debug(s"None of the modified names appears in $from. This dependency is not being considered for invalidation.") - false - } else { - log.debug(s"The following modified names cause invalidation of $from: $modifiedAndUsedNames") - true - } - case from => - log.debug(s"Name hashing optimization doesn't apply to non-Scala dependency: $from") - true - } - } - } -} diff --git a/compile/inc/src/main/scala/sbt/inc/Relations.scala b/compile/inc/src/main/scala/sbt/inc/Relations.scala deleted file mode 100644 index 0ad2e3aa7..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Relations.scala +++ /dev/null @@ -1,752 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import java.io.File -import Relations.Source -import Relations.SourceDependencies -import xsbti.api.{ Source => APISource } -import xsbti.DependencyContext -import xsbti.DependencyContext._ - -/** - * Provides mappings between source files, generated classes (products), and binaries. - * Dependencies that are tracked include internal: a dependency on a source in the same compilation group (project), - * external: a dependency on a source in another compilation group (tracked as the name of the class), - * binary: a dependency on a class or jar file not generated by a source file in any tracked compilation group, - * inherited: a dependency that resulted from a public template inheriting, - * direct: any type of dependency, including inheritance. - */ -trait Relations { - /** All sources _with at least one product_ . */ - def allSources: collection.Set[File] - - /** All products associated with sources. */ - def allProducts: collection.Set[File] - - /** All files that are recorded as a binary dependency of a source file.*/ - def allBinaryDeps: collection.Set[File] - - /** All files in this compilation group (project) that are recorded as a source dependency of a source file in this group.*/ - def allInternalSrcDeps: collection.Set[File] - - /** All files in another compilation group (project) that are recorded as a source dependency of a source file in this group.*/ - def allExternalDeps: collection.Set[String] - - /** Fully qualified names of classes generated from source file `src`. */ - def classNames(src: File): Set[String] - - /** Source files that generated a class with the given fully qualified `name`. This is typically a set containing a single file. */ - def definesClass(name: String): Set[File] - - /** The classes that were generated for source file `src`. */ - def products(src: File): Set[File] - /** The source files that generated class file `prod`. This is typically a set containing a single file. */ - def produced(prod: File): Set[File] - - /** The binary dependencies for the source file `src`. */ - def binaryDeps(src: File): Set[File] - /** The source files that depend on binary file `dep`. */ - def usesBinary(dep: File): Set[File] - - /** Internal source dependencies for `src`. This includes both direct and inherited dependencies. */ - def internalSrcDeps(src: File): Set[File] - /** Internal source files that depend on internal source `dep`. This includes both direct and inherited dependencies. */ - def usesInternalSrc(dep: File): Set[File] - - /** External source dependencies that internal source file `src` depends on. This includes both direct and inherited dependencies. */ - def externalDeps(src: File): Set[String] - /** Internal source dependencies that depend on external source file `dep`. This includes both direct and inherited dependencies. */ - def usesExternal(dep: String): Set[File] - - private[inc] def usedNames(src: File): Set[String] - - /** Records internal source file `src` as generating class file `prod` with top-level class `name`. */ - @deprecated("Record all products using `addProducts`.", "0.13.8") - def addProduct(src: File, prod: File, name: String): Relations - - /** - * Records internal source file `src` as dependending on `dependsOn`. If this dependency is introduced - * by an inheritance relation, `inherited` is set to true. Note that in this case, the dependency is - * also registered as a direct dependency. - */ - @deprecated("Record all external dependencies using `addExternalDeps`.", "0.13.8") - def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations - - /** Records internal source file `src` depending on a dependency binary dependency `dependsOn`.*/ - @deprecated("Record all binary dependencies using `addBinaryDeps`.", "0.13.8") - def addBinaryDep(src: File, dependsOn: File): Relations - - /** - * Records internal source file `src` as having direct dependencies on internal source files `directDependsOn` - * and inheritance dependencies on `inheritedDependsOn`. Everything in `inheritedDependsOn` must be included in `directDependsOn`; - * this method does not automatically record direct dependencies like `addExternalDep` does. - */ - @deprecated("Record all internal dependencies using `addInternalSrcDeps(File, Iterable[InternalDependencies])`.", "0.13.8") - def addInternalSrcDeps(src: File, directDependsOn: Iterable[File], inheritedDependsOn: Iterable[File]): Relations - - /** - * Records that the file `src` generates products `products`, has internal dependencies `internalDeps`, - * has external dependencies `externalDeps` and binary dependencies `binaryDeps`. - */ - def addSource(src: File, - products: Iterable[(File, String)], - internalDeps: Iterable[InternalDependency], - externalDeps: Iterable[ExternalDependency], - binaryDeps: Iterable[(File, String, Stamp)]): Relations = - addProducts(src, products).addInternalSrcDeps(src, internalDeps).addExternalDeps(src, externalDeps).addBinaryDeps(src, binaryDeps) - - /** - * Records all the products `prods` generated by `src` - */ - private[inc] def addProducts(src: File, prods: Iterable[(File, String)]): Relations - - /** - * Records all the internal source dependencies `deps` of `src` - */ - private[inc] def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]): Relations - - /** - * Records all the external dependencies `deps` of `src` - */ - private[inc] def addExternalDeps(src: File, deps: Iterable[ExternalDependency]): Relations - - /** - * Records all the binary dependencies `deps` of `src` - */ - private[inc] def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]): Relations - - private[inc] def addUsedName(src: File, name: String): Relations - - /** Concatenates the two relations. Acts naively, i.e., doesn't internalize external deps on added files. */ - def ++(o: Relations): Relations - - /** Drops all dependency mappings a->b where a is in `sources`. Acts naively, i.e., doesn't externalize internal deps on removed files. */ - def --(sources: Iterable[File]): Relations - - @deprecated("OK to remove in 0.14", "0.13.1") - def groupBy[K](f: (File => K)): Map[K, Relations] - - /** The relation between internal sources and generated class files. */ - def srcProd: Relation[File, File] - - /** The dependency relation between internal sources and binaries. */ - def binaryDep: Relation[File, File] - - /** The dependency relation between internal sources. This includes both direct and inherited dependencies.*/ - def internalSrcDep: Relation[File, File] - - /** The dependency relation between internal and external sources. This includes both direct and inherited dependencies.*/ - def externalDep: Relation[File, String] - - /** All the internal dependencies */ - private[inc] def internalDependencies: InternalDependencies - - /** All the external dependencies */ - private[inc] def externalDependencies: ExternalDependencies - - /** - * The source dependency relation between source files introduced by member reference. - * - * NOTE: All inheritance dependencies are included in this relation because in order to - * inherit from a member you have to refer to it. If you check documentation of `inheritance` - * you'll see that there's small oddity related to traits being the first parent of a - * class/trait that results in additional parents being introduced due to normalization. - * This relation properly accounts for that so the invariant that `memberRef` is a superset - * of `inheritance` is preserved. - */ - private[inc] def memberRef: SourceDependencies - - /** - * The source dependency relation between source files introduced by inheritance. - * The dependency by inheritance is introduced when a template (class or trait) mentions - * a given type in a parent position. - * - * NOTE: Due to an oddity in how Scala's type checker works there's one unexpected dependency - * on a class being introduced. An example illustrates the best the problem. Let's consider - * the following structure: - * - * trait A extends B - * trait B extends C - * trait C extends D - * class D - * - * We are interested in dependencies by inheritance of `A`. One would expect it to be just `B` - * but the answer is `B` and `D`. The reason is because Scala's type checker performs a certain - * normalization so the first parent of a type is a class. Therefore the example above is normalized - * to the following form: - * - * trait A extends D with B - * trait B extends D with C - * trait C extends D - * class D - * - * Therefore if you inherit from a trait you'll get an additional dependency on a class that is - * resolved transitively. You should not rely on this behavior, though. - * - */ - private[inc] def inheritance: SourceDependencies - - /** The dependency relations between sources. These include both direct and inherited dependencies.*/ - def direct: Source - - /** The inheritance dependency relations between sources.*/ - def publicInherited: Source - - /** The relation between a source file and the fully qualified names of classes generated from it.*/ - def classes: Relation[File, String] - - /** - * Flag which indicates whether given Relations object supports operations needed by name hashing algorithm. - * - * At the moment the list includes the following operations: - * - * - memberRef: SourceDependencies - * - inheritance: SourceDependencies - * - * The `memberRef` and `inheritance` implement a new style source dependency tracking. When this flag is - * enabled access to `direct` and `publicInherited` relations is illegal and will cause runtime exception - * being thrown. That is done as an optimization that prevents from storing two overlapping sets of - * dependencies. - * - * Conversely, when `nameHashing` flag is disabled access to `memberRef` and `inheritance` - * relations is illegal and will cause runtime exception being thrown. - */ - private[inc] def nameHashing: Boolean - /** - * Relation between source files and _unqualified_ term and type names used in given source file. - */ - private[inc] def names: Relation[File, String] - - /** - * Lists of all the pairs (header, relation) that sbt knows of. - * Used by TextAnalysisFormat to persist relations. - * This cannot be stored as a Map because the order is important. - */ - private[inc] def allRelations: List[(String, Relation[File, _])] -} - -object Relations { - - /** - * Represents all the relations that sbt knows of along with a way to recreate each - * of their elements from their string representation. - */ - private[inc] val existingRelations = { - val string2File: String => File = new File(_) - List( - ("products", string2File), - ("binary dependencies", string2File), - ("direct source dependencies", string2File), - ("direct external dependencies", identity[String] _), - ("public inherited source dependencies", string2File), - ("public inherited external dependencies", identity[String] _), - ("member reference internal dependencies", string2File), - ("member reference external dependencies", identity[String] _), - ("inheritance internal dependencies", string2File), - ("inheritance external dependencies", identity[String] _), - ("class names", identity[String] _), - ("used names", identity[String] _)) - } - /** - * Reconstructs a Relations from a list of Relation - * The order in which the relations are read matters and is defined by `existingRelations`. - */ - def construct(nameHashing: Boolean, relations: List[Relation[_, _]]) = - relations match { - case p :: bin :: di :: de :: pii :: pie :: mri :: mre :: ii :: ie :: cn :: un :: Nil => - val srcProd = p.asInstanceOf[Relation[File, File]] - val binaryDep = bin.asInstanceOf[Relation[File, File]] - val directSrcDeps = makeSource(di.asInstanceOf[Relation[File, File]], de.asInstanceOf[Relation[File, String]]) - val publicInheritedSrcDeps = makeSource(pii.asInstanceOf[Relation[File, File]], pie.asInstanceOf[Relation[File, String]]) - val memberRefSrcDeps = makeSourceDependencies(mri.asInstanceOf[Relation[File, File]], mre.asInstanceOf[Relation[File, String]]) - val inheritanceSrcDeps = makeSourceDependencies(ii.asInstanceOf[Relation[File, File]], ie.asInstanceOf[Relation[File, String]]) - val classes = cn.asInstanceOf[Relation[File, String]] - val names = un.asInstanceOf[Relation[File, String]] - - // we don't check for emptiness of publicInherited/inheritance relations because - // we assume that invariant that says they are subsets of direct/memberRef holds - assert(nameHashing || (memberRefSrcDeps == emptySourceDependencies), "When name hashing is disabled the `memberRef` relation should be empty.") - assert(!nameHashing || (directSrcDeps == emptySource), "When name hashing is enabled the `direct` relation should be empty.") - - if (nameHashing) { - val internal = InternalDependencies(Map(DependencyByMemberRef -> mri.asInstanceOf[Relation[File, File]], DependencyByInheritance -> ii.asInstanceOf[Relation[File, File]])) - val external = ExternalDependencies(Map(DependencyByMemberRef -> mre.asInstanceOf[Relation[File, String]], DependencyByInheritance -> ie.asInstanceOf[Relation[File, String]])) - Relations.make(srcProd, binaryDep, internal, external, classes, names) - } else { - assert(names.all.isEmpty, s"When `nameHashing` is disabled `names` relation should be empty: $names") - Relations.make(srcProd, binaryDep, directSrcDeps, publicInheritedSrcDeps, classes) - } - case _ => throw new java.io.IOException(s"Expected to read ${existingRelations.length} relations but read ${relations.length}.") - } - - /** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/ - final class Source private[sbt] (val internal: Relation[File, File], val external: Relation[File, String]) { - def addInternal(source: File, dependsOn: Iterable[File]): Source = new Source(internal + (source, dependsOn), external) - @deprecated("Use addExternal(File, Iterable[String])", "0.13.8") - def addExternal(source: File, dependsOn: String): Source = new Source(internal, external + (source, dependsOn)) - def addExternal(source: File, dependsOn: Iterable[String]): Source = new Source(internal, external + (source, dependsOn)) - /** Drops all dependency mappings from `sources`. Acts naively, i.e., doesn't externalize internal deps on removed files.*/ - def --(sources: Iterable[File]): Source = new Source(internal -- sources, external -- sources) - def ++(o: Source): Source = new Source(internal ++ o.internal, external ++ o.external) - - @deprecated("Broken implementation. OK to remove in 0.14", "0.13.1") - def groupBySource[K](f: File => K): Map[K, Source] = { - - val i = internal.groupBy { case (a, b) => f(a) } - val e = external.groupBy { case (a, b) => f(a) } - val pairs = for (k <- i.keySet ++ e.keySet) yield (k, new Source(getOrEmpty(i, k), getOrEmpty(e, k))) - pairs.toMap - } - - override def equals(other: Any) = other match { - case o: Source => internal == o.internal && external == o.external - case _ => false - } - - override def hashCode = (internal, external).hashCode - } - - /** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/ - private[inc] final class SourceDependencies(val internal: Relation[File, File], val external: Relation[File, String]) { - def addInternal(source: File, dependsOn: Iterable[File]): SourceDependencies = new SourceDependencies(internal + (source, dependsOn), external) - @deprecated("Use addExternal(File, Iterable[String])", "0.13.8") - def addExternal(source: File, dependsOn: String): SourceDependencies = new SourceDependencies(internal, external + (source, dependsOn)) - def addExternal(source: File, dependsOn: Iterable[String]): SourceDependencies = new SourceDependencies(internal, external + (source, dependsOn)) - /** Drops all dependency mappings from `sources`. Acts naively, i.e., doesn't externalize internal deps on removed files.*/ - def --(sources: Iterable[File]): SourceDependencies = new SourceDependencies(internal -- sources, external -- sources) - def ++(o: SourceDependencies): SourceDependencies = new SourceDependencies(internal ++ o.internal, external ++ o.external) - - override def equals(other: Any) = other match { - case o: SourceDependencies => internal == o.internal && external == o.external - case _ => false - } - - override def hashCode = (internal, external).hashCode - } - - private[sbt] def getOrEmpty[A, B, K](m: Map[K, Relation[A, B]], k: K): Relation[A, B] = m.getOrElse(k, Relation.empty) - - private[this] lazy val e = Relation.empty[File, File] - private[this] lazy val estr = Relation.empty[File, String] - private[this] lazy val es = new Source(e, estr) - - def emptySource: Source = es - private[inc] lazy val emptySourceDependencies: SourceDependencies = new SourceDependencies(e, estr) - def empty: Relations = empty(nameHashing = IncOptions.nameHashingDefault) - private[inc] def empty(nameHashing: Boolean): Relations = - if (nameHashing) - new MRelationsNameHashing(e, e, InternalDependencies.empty, ExternalDependencies.empty, estr, estr) - else - new MRelationsDefaultImpl(e, e, es, es, estr) - - def make(srcProd: Relation[File, File], binaryDep: Relation[File, File], direct: Source, publicInherited: Source, classes: Relation[File, String]): Relations = - new MRelationsDefaultImpl(srcProd, binaryDep, direct = direct, publicInherited = publicInherited, classes) - - private[inc] def make(srcProd: Relation[File, File], binaryDep: Relation[File, File], - internalDependencies: InternalDependencies, externalDependencies: ExternalDependencies, - classes: Relation[File, String], names: Relation[File, String]): Relations = - new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies, externalDependencies = externalDependencies, classes, names) - def makeSource(internal: Relation[File, File], external: Relation[File, String]): Source = new Source(internal, external) - private[inc] def makeSourceDependencies(internal: Relation[File, File], external: Relation[File, String]): SourceDependencies = new SourceDependencies(internal, external) -} - -private object DependencyCollection { - /** - * Combine `m1` and `m2` such that the result contains all the dependencies they represent. - * `m1` is expected to be smaller than `m2`. - */ - def joinMaps[T](m1: Map[DependencyContext, Relation[File, T]], m2: Map[DependencyContext, Relation[File, T]]) = - m1.foldLeft(m2) { case (tmp, (key, values)) => tmp.updated(key, tmp.getOrElse(key, Relation.empty) ++ values) } -} - -private object InternalDependencies { - /** - * Constructs an empty `InteralDependencies` - */ - def empty = InternalDependencies(Map.empty) -} - -private case class InternalDependencies(dependencies: Map[DependencyContext, Relation[File, File]]) { - /** - * Adds `dep` to the dependencies - */ - def +(dep: InternalDependency): InternalDependencies = - InternalDependencies(dependencies.updated(dep.context, dependencies.getOrElse(dep.context, Relation.empty) + (dep.sourceFile, dep.targetFile))) - - /** - * Adds all `deps` to the dependencies - */ - def ++(deps: Iterable[InternalDependency]): InternalDependencies = deps.foldLeft(this)(_ + _) - def ++(deps: InternalDependencies): InternalDependencies = InternalDependencies(DependencyCollection.joinMaps(dependencies, deps.dependencies)) - - /** - * Removes all dependencies from `sources` to another file from the dependencies - */ - def --(sources: Iterable[File]): InternalDependencies = InternalDependencies(dependencies.mapValues(_ -- sources).filter(_._2.size > 0)) -} - -private object ExternalDependencies { - /** - * Constructs an empty `ExternalDependencies` - */ - def empty = ExternalDependencies(Map.empty) -} - -private case class ExternalDependencies(dependencies: Map[DependencyContext, Relation[File, String]]) { - /** - * Adds `dep` to the dependencies - */ - def +(dep: ExternalDependency): ExternalDependencies = ExternalDependencies(dependencies.updated(dep.context, dependencies.getOrElse(dep.context, Relation.empty) + (dep.sourceFile, dep.targetClassName))) - - /** - * Adds all `deps` to the dependencies - */ - def ++(deps: Iterable[ExternalDependency]): ExternalDependencies = deps.foldLeft(this)(_ + _) - def ++(deps: ExternalDependencies): ExternalDependencies = ExternalDependencies(DependencyCollection.joinMaps(dependencies, deps.dependencies)) - - /** - * Removes all dependencies from `sources` to another file from the dependencies - */ - def --(sources: Iterable[File]): ExternalDependencies = ExternalDependencies(dependencies.mapValues(_ -- sources).filter(_._2.size > 0)) -} - -/** - * An abstract class that contains common functionality inherited by two implementations of Relations trait. - * - * A little note why we have two different implementations of Relations trait. This is needed for the time - * being when we are slowly migrating to the new invalidation algorithm called "name hashing" which requires - * some subtle changes to dependency tracking. For some time we plan to keep both algorithms side-by-side - * and have a runtime switch which allows to pick one. So we need logic for both old and new dependency - * tracking to be available. That's exactly what two subclasses of MRelationsCommon implement. Once name - * hashing is proven to be stable and reliable we'll phase out the old algorithm and the old dependency tracking - * logic. - * - * `srcProd` is a relation between a source file and a product: (source, product). - * Note that some source files may not have a product and will not be included in this relation. - * - * `binaryDeps` is a relation between a source file and a binary dependency: (source, binary dependency). - * This only includes dependencies on classes and jars that do not have a corresponding source/API to track instead. - * A class or jar with a corresponding source should only be tracked in one of the source dependency relations. - * - * `classes` is a relation between a source file and its generated fully-qualified class names. - */ -private abstract class MRelationsCommon(val srcProd: Relation[File, File], val binaryDep: Relation[File, File], - val classes: Relation[File, String]) extends Relations { - def allSources: collection.Set[File] = srcProd._1s - - def allProducts: collection.Set[File] = srcProd._2s - def allBinaryDeps: collection.Set[File] = binaryDep._2s - def allInternalSrcDeps: collection.Set[File] = internalSrcDep._2s - def allExternalDeps: collection.Set[String] = externalDep._2s - - def classNames(src: File): Set[String] = classes.forward(src) - def definesClass(name: String): Set[File] = classes.reverse(name) - - def products(src: File): Set[File] = srcProd.forward(src) - def produced(prod: File): Set[File] = srcProd.reverse(prod) - - def binaryDeps(src: File): Set[File] = binaryDep.forward(src) - def usesBinary(dep: File): Set[File] = binaryDep.reverse(dep) - - def internalSrcDeps(src: File): Set[File] = internalSrcDep.forward(src) - def usesInternalSrc(dep: File): Set[File] = internalSrcDep.reverse(dep) - - def externalDeps(src: File): Set[String] = externalDep.forward(src) - def usesExternal(dep: String): Set[File] = externalDep.reverse(dep) - - def usedNames(src: File): Set[String] = names.forward(src) - - /** Making large Relations a little readable. */ - private val userDir = sys.props("user.dir").stripSuffix("/") + "/" - private def nocwd(s: String) = s stripPrefix userDir - private def line_s(kv: (Any, Any)) = " " + nocwd("" + kv._1) + " -> " + nocwd("" + kv._2) + "\n" - protected def relation_s(r: Relation[_, _]) = ( - if (r.forwardMap.isEmpty) "Relation [ ]" - else (r.all.toSeq map line_s sorted) mkString ("Relation [\n", "", "]") - ) -} - -/** - * This class implements Relations trait with support for tracking of `direct` and `publicInherited` source - * dependencies. Therefore this class preserves the "old" (from sbt 0.13.0) dependency tracking logic and it's - * a default implementation. - * - * `direct` defines relations for dependencies between internal and external source dependencies. It includes all types of - * dependencies, including inheritance. - * - * `publicInherited` defines relations for internal and external source dependencies, only including dependencies - * introduced by inheritance. - * - */ -private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Relation[File, File], - // direct should include everything in inherited - val direct: Source, val publicInherited: Source, - classes: Relation[File, String]) extends MRelationsCommon(srcProd, binaryDep, classes) { - def internalSrcDep: Relation[File, File] = direct.internal - def externalDep: Relation[File, String] = direct.external - - def nameHashing: Boolean = false - - def memberRef: SourceDependencies = - throw new UnsupportedOperationException("The `memberRef` source dependencies relation is not supported " + - "when `nameHashing` flag is disabled.") - def inheritance: SourceDependencies = - throw new UnsupportedOperationException("The `memberRef` source dependencies relation is not supported " + - "when `nameHashing` flag is disabled.") - - def addProduct(src: File, prod: File, name: String): Relations = - new MRelationsDefaultImpl(srcProd + (src, prod), binaryDep, direct = direct, - publicInherited = publicInherited, classes + (src, name)) - - def addProducts(src: File, products: Iterable[(File, String)]): Relations = - new MRelationsDefaultImpl(srcProd ++ products.map(p => (src, p._1)), binaryDep, direct = direct, - publicInherited = publicInherited, classes ++ products.map(p => (src, p._2))) - - def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]) = { - val depsByInheritance = deps.collect { case InternalDependency(_, targetFile, DependencyByInheritance) => targetFile } - - val newD = direct.addInternal(src, deps.map(_.targetFile)) - val newI = publicInherited.addInternal(src, depsByInheritance) - - new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes) - } - - def addInternalSrcDeps(src: File, directDependsOn: Iterable[File], inheritedDependsOn: Iterable[File]): Relations = { - val directDeps = directDependsOn.map(d => InternalDependency(src, d, DependencyByMemberRef)) - val inheritedDeps = inheritedDependsOn.map(d => InternalDependency(src, d, DependencyByInheritance)) - addInternalSrcDeps(src, directDeps ++ inheritedDeps) - } - - def addExternalDeps(src: File, deps: Iterable[ExternalDependency]) = { - val depsByInheritance = deps.collect { case ExternalDependency(_, targetClassName, _, DependencyByInheritance) => targetClassName } - - val newD = direct.addExternal(src, deps.map(_.targetClassName)) - val newI = publicInherited.addExternal(src, depsByInheritance) - - new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes) - } - - def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations = { - val newI = if (inherited) publicInherited.addExternal(src, dependsOn :: Nil) else publicInherited - val newD = direct.addExternal(src, dependsOn :: Nil) - new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes) - } - - def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]) = - new MRelationsDefaultImpl(srcProd, binaryDep + (src, deps.map(_._1)), direct, publicInherited, classes) - - def addBinaryDep(src: File, dependsOn: File): Relations = - new MRelationsDefaultImpl(srcProd, binaryDep + (src, dependsOn), direct = direct, - publicInherited = publicInherited, classes) - - def names: Relation[File, String] = - throw new UnsupportedOperationException("Tracking of used names is not supported " + - "when `nameHashing` is disabled.") - - def addUsedName(src: File, name: String): Relations = - throw new UnsupportedOperationException("Tracking of used names is not supported " + - "when `nameHashing` is disabled.") - - override def externalDependencies: ExternalDependencies = ExternalDependencies(Map(DependencyByMemberRef -> direct.external, DependencyByInheritance -> publicInherited.external)) - override def internalDependencies: InternalDependencies = InternalDependencies(Map(DependencyByMemberRef -> direct.internal, DependencyByInheritance -> publicInherited.internal)) - - def ++(o: Relations): Relations = { - if (nameHashing != o.nameHashing) - throw new UnsupportedOperationException("The `++` operation is not supported for relations " + - "with different values of `nameHashing` flag.") - new MRelationsDefaultImpl(srcProd ++ o.srcProd, binaryDep ++ o.binaryDep, direct ++ o.direct, - publicInherited ++ o.publicInherited, classes ++ o.classes) - } - def --(sources: Iterable[File]) = - new MRelationsDefaultImpl(srcProd -- sources, binaryDep -- sources, direct = direct -- sources, - publicInherited = publicInherited -- sources, classes -- sources) - - @deprecated("Broken implementation. OK to remove in 0.14", "0.13.1") - def groupBy[K](f: File => K): Map[K, Relations] = - { - type MapRel[T] = Map[K, Relation[File, T]] - def outerJoin(srcProdMap: MapRel[File], binaryDepMap: MapRel[File], direct: Map[K, Source], - inherited: Map[K, Source], classesMap: MapRel[String], - namesMap: MapRel[String]): Map[K, Relations] = - { - def kRelations(k: K): Relations = { - def get[T](m: Map[K, Relation[File, T]]) = Relations.getOrEmpty(m, k) - def getSrc(m: Map[K, Source]): Source = m.getOrElse(k, Relations.emptySource) - def getSrcDeps(m: Map[K, SourceDependencies]): SourceDependencies = - m.getOrElse(k, Relations.emptySourceDependencies) - new MRelationsDefaultImpl(get(srcProdMap), get(binaryDepMap), getSrc(direct), getSrc(inherited), - get(classesMap)) - } - val keys = (srcProdMap.keySet ++ binaryDepMap.keySet ++ direct.keySet ++ inherited.keySet ++ classesMap.keySet).toList - Map(keys.map((k: K) => (k, kRelations(k))): _*) - } - - def f1[B](item: (File, B)): K = f(item._1) - - outerJoin(srcProd.groupBy(f1), binaryDep.groupBy(f1), direct.groupBySource(f), - publicInherited.groupBySource(f), classes.groupBy(f1), names.groupBy(f1)) - } - - override def equals(other: Any) = other match { - case o: MRelationsDefaultImpl => - srcProd == o.srcProd && binaryDep == o.binaryDep && direct == o.direct && - publicInherited == o.publicInherited && classes == o.classes - case _ => false - } - - def allRelations = { - val rels = List( - srcProd, - binaryDep, - direct.internal, - direct.external, - publicInherited.internal, - publicInherited.external, - Relations.emptySourceDependencies.internal, // Default implementation doesn't provide memberRef source deps - Relations.emptySourceDependencies.external, // Default implementation doesn't provide memberRef source deps - Relations.emptySourceDependencies.internal, // Default implementation doesn't provide inheritance source deps - Relations.emptySourceDependencies.external, // Default implementation doesn't provide inheritance source deps - classes, - Relation.empty[File, String]) // Default implementation doesn't provide used names relation - Relations.existingRelations map (_._1) zip rels - } - - override def hashCode = (srcProd :: binaryDep :: direct :: publicInherited :: classes :: Nil).hashCode - - override def toString = ( - """ - |Relations: - | products: %s - | bin deps: %s - | src deps: %s - | ext deps: %s - | class names: %s - """.trim.stripMargin.format(List(srcProd, binaryDep, internalSrcDep, externalDep, classes) map relation_s: _*) - ) -} - -/** - * This class implements Relations trait with support for tracking of `memberRef` and `inheritance` source - * dependencies. Therefore this class implements the new (compared to sbt 0.13.0) dependency tracking logic - * needed by the name hashing invalidation algorithm. - */ -private class MRelationsNameHashing(srcProd: Relation[File, File], binaryDep: Relation[File, File], - val internalDependencies: InternalDependencies, - val externalDependencies: ExternalDependencies, - classes: Relation[File, String], - val names: Relation[File, String]) extends MRelationsCommon(srcProd, binaryDep, classes) { - def direct: Source = - throw new UnsupportedOperationException("The `direct` source dependencies relation is not supported " + - "when `nameHashing` flag is enabled.") - def publicInherited: Source = - throw new UnsupportedOperationException("The `publicInherited` source dependencies relation is not supported " + - "when `nameHashing` flag is enabled.") - - val nameHashing: Boolean = true - - def internalSrcDep: Relation[File, File] = memberRef.internal - def externalDep: Relation[File, String] = memberRef.external - - def addProduct(src: File, prod: File, name: String): Relations = - new MRelationsNameHashing(srcProd + (src, prod), binaryDep, internalDependencies = internalDependencies, - externalDependencies = externalDependencies, classes + (src, name), names = names) - - def addProducts(src: File, products: Iterable[(File, String)]): Relations = - new MRelationsNameHashing(srcProd ++ products.map(p => (src, p._1)), binaryDep, - internalDependencies = internalDependencies, externalDependencies = externalDependencies, - classes ++ products.map(p => (src, p._2)), names = names) - - def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]) = - new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies ++ deps, - externalDependencies = externalDependencies, classes, names) - - def addInternalSrcDeps(src: File, dependsOn: Iterable[File], inherited: Iterable[File]): Relations = { - val memberRefDeps = dependsOn.map(InternalDependency(src, _, DependencyByMemberRef)) - val inheritedDeps = inherited.map(InternalDependency(src, _, DependencyByInheritance)) - addInternalSrcDeps(src, memberRefDeps ++ inheritedDeps) - } - - def addExternalDeps(src: File, deps: Iterable[ExternalDependency]) = - new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies, - externalDependencies = externalDependencies ++ deps, classes, names) - - def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations = - throw new UnsupportedOperationException("This method is not supported when `nameHashing` flag is enabled.") - - def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]) = - new MRelationsNameHashing(srcProd, binaryDep + (src, deps.map(_._1)), internalDependencies = internalDependencies, - externalDependencies = externalDependencies, classes, names) - - def addBinaryDep(src: File, dependsOn: File): Relations = - new MRelationsNameHashing(srcProd, binaryDep + (src, dependsOn), internalDependencies = internalDependencies, - externalDependencies = externalDependencies, classes, names = names) - - def addUsedName(src: File, name: String): Relations = - new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies, - externalDependencies = externalDependencies, classes, names = names + (src, name)) - - override def inheritance: SourceDependencies = - new SourceDependencies(internalDependencies.dependencies.getOrElse(DependencyByInheritance, Relation.empty), externalDependencies.dependencies.getOrElse(DependencyByInheritance, Relation.empty)) - override def memberRef: SourceDependencies = - new SourceDependencies(internalDependencies.dependencies.getOrElse(DependencyByMemberRef, Relation.empty), externalDependencies.dependencies.getOrElse(DependencyByMemberRef, Relation.empty)) - - def ++(o: Relations): Relations = { - if (!o.nameHashing) - throw new UnsupportedOperationException("The `++` operation is not supported for relations " + - "with different values of `nameHashing` flag.") - new MRelationsNameHashing(srcProd ++ o.srcProd, binaryDep ++ o.binaryDep, - internalDependencies = internalDependencies ++ o.internalDependencies, externalDependencies = externalDependencies ++ o.externalDependencies, - classes ++ o.classes, names = names ++ o.names) - } - def --(sources: Iterable[File]) = - new MRelationsNameHashing(srcProd -- sources, binaryDep -- sources, - internalDependencies = internalDependencies -- sources, externalDependencies = externalDependencies -- sources, classes -- sources, - names = names -- sources) - - def groupBy[K](f: File => K): Map[K, Relations] = { - throw new UnsupportedOperationException("Merging of Analyses that have" + - "`relations.nameHashing` set to `true` is not supported.") - } - - override def equals(other: Any) = other match { - case o: MRelationsNameHashing => - srcProd == o.srcProd && binaryDep == o.binaryDep && memberRef == o.memberRef && - inheritance == o.inheritance && classes == o.classes - case _ => false - } - - def allRelations = { - val rels = List( - srcProd, - binaryDep, - Relations.emptySource.internal, // NameHashing doesn't provide direct dependencies - Relations.emptySource.external, // NameHashing doesn't provide direct dependencies - Relations.emptySource.internal, // NameHashing doesn't provide public inherited dependencies - Relations.emptySource.external, // NameHashing doesn't provide public inherited dependencies - memberRef.internal, - memberRef.external, - inheritance.internal, - inheritance.external, - classes, - names) - Relations.existingRelations map (_._1) zip rels - } - - override def hashCode = (srcProd :: binaryDep :: memberRef :: inheritance :: classes :: Nil).hashCode - - override def toString = ( - """ - |Relations (with name hashing enabled): - | products: %s - | bin deps: %s - | src deps: %s - | ext deps: %s - | class names: %s - | used names: %s - """.trim.stripMargin.format(List(srcProd, binaryDep, internalSrcDep, externalDep, classes, names) map relation_s: _*) - ) - -} diff --git a/compile/inc/src/main/scala/sbt/inc/SourceInfo.scala b/compile/inc/src/main/scala/sbt/inc/SourceInfo.scala deleted file mode 100644 index 1b1ab4e4b..000000000 --- a/compile/inc/src/main/scala/sbt/inc/SourceInfo.scala +++ /dev/null @@ -1,36 +0,0 @@ -package sbt -package inc - -import xsbti.Problem - -import java.io.File - -trait SourceInfo { - def reportedProblems: Seq[Problem] - def unreportedProblems: Seq[Problem] -} -trait SourceInfos { - def ++(o: SourceInfos): SourceInfos - def add(file: File, info: SourceInfo): SourceInfos - def --(files: Iterable[File]): SourceInfos - def groupBy[K](f: (File) => K): Map[K, SourceInfos] - def get(file: File): SourceInfo - def allInfos: Map[File, SourceInfo] -} -object SourceInfos { - def empty: SourceInfos = make(Map.empty) - def make(m: Map[File, SourceInfo]): SourceInfos = new MSourceInfos(m) - - val emptyInfo: SourceInfo = makeInfo(Nil, Nil) - def makeInfo(reported: Seq[Problem], unreported: Seq[Problem]): SourceInfo = - new MSourceInfo(reported, unreported) - def merge(infos: Traversable[SourceInfos]): SourceInfos = (SourceInfos.empty /: infos)(_ ++ _) -} -private final class MSourceInfos(val allInfos: Map[File, SourceInfo]) extends SourceInfos { - def ++(o: SourceInfos) = new MSourceInfos(allInfos ++ o.allInfos) - def --(sources: Iterable[File]) = new MSourceInfos(allInfos -- sources) - def groupBy[K](f: File => K): Map[K, SourceInfos] = allInfos groupBy (x => f(x._1)) map { x => (x._1, new MSourceInfos(x._2)) } - def add(file: File, info: SourceInfo) = new MSourceInfos(allInfos + ((file, info))) - def get(file: File) = allInfos.getOrElse(file, SourceInfos.emptyInfo) -} -private final class MSourceInfo(val reportedProblems: Seq[Problem], val unreportedProblems: Seq[Problem]) extends SourceInfo diff --git a/compile/inc/src/main/scala/sbt/inc/Stamp.scala b/compile/inc/src/main/scala/sbt/inc/Stamp.scala deleted file mode 100644 index bb262d95c..000000000 --- a/compile/inc/src/main/scala/sbt/inc/Stamp.scala +++ /dev/null @@ -1,189 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import java.io.{ File, IOException } -import Stamp.getStamp -import scala.util.matching.Regex - -trait ReadStamps { - /** The Stamp for the given product at the time represented by this Stamps instance.*/ - def product(prod: File): Stamp - /** The Stamp for the given source file at the time represented by this Stamps instance.*/ - def internalSource(src: File): Stamp - /** The Stamp for the given binary dependency at the time represented by this Stamps instance.*/ - def binary(bin: File): Stamp -} - -/** Provides information about files as they were at a specific time.*/ -trait Stamps extends ReadStamps { - def allInternalSources: collection.Set[File] - def allBinaries: collection.Set[File] - def allProducts: collection.Set[File] - - def sources: Map[File, Stamp] - def binaries: Map[File, Stamp] - def products: Map[File, Stamp] - def classNames: Map[File, String] - - def className(bin: File): Option[String] - - def markInternalSource(src: File, s: Stamp): Stamps - def markBinary(bin: File, className: String, s: Stamp): Stamps - def markProduct(prod: File, s: Stamp): Stamps - - def filter(prod: File => Boolean, removeSources: Iterable[File], bin: File => Boolean): Stamps - - def ++(o: Stamps): Stamps - def groupBy[K](prod: Map[K, File => Boolean], sourcesGrouping: File => K, bin: Map[K, File => Boolean]): Map[K, Stamps] -} - -sealed trait Stamp { - override def equals(other: Any): Boolean = other match { - case o: Stamp => Stamp.equivStamp.equiv(this, o) - case _ => false - } - - override def toString: String = Stamp.toString(this) -} - -final class Hash(val value: Array[Byte]) extends Stamp { - override def hashCode: Int = java.util.Arrays.hashCode(value) -} -final class LastModified(val value: Long) extends Stamp { - override def hashCode: Int = (value ^ (value >>> 32)).toInt -} -final class Exists(val value: Boolean) extends Stamp { - override def hashCode: Int = if (value) 0 else 1 -} - -object Stamp { - implicit val equivStamp: Equiv[Stamp] = new Equiv[Stamp] { - def equiv(a: Stamp, b: Stamp) = (a, b) match { - case (h1: Hash, h2: Hash) => h1.value sameElements h2.value - case (e1: Exists, e2: Exists) => e1.value == e2.value - case (lm1: LastModified, lm2: LastModified) => lm1.value == lm2.value - case _ => false - } - } - - // NOTE: toString/fromString used for serialization, not just for debug prints. - - def toString(s: Stamp): String = s match { - case e: Exists => if (e.value) "exists" else "absent" - case h: Hash => "hash(" + Hash.toHex(h.value) + ")" - case lm: LastModified => "lastModified(" + lm.value + ")" - } - - private val hashPattern = """hash\((\w+)\)""".r - private val lastModifiedPattern = """lastModified\((\d+)\)""".r - - def fromString(s: String): Stamp = s match { - case "exists" => new Exists(true) - case "absent" => new Exists(false) - case hashPattern(value) => new Hash(Hash.fromHex(value)) - case lastModifiedPattern(value) => new LastModified(java.lang.Long.parseLong(value)) - case _ => throw new IllegalArgumentException("Unrecognized Stamp string representation: " + s) - } - - def show(s: Stamp): String = s match { - case h: Hash => "hash(" + Hash.toHex(h.value) + ")" - case e: Exists => if (e.value) "exists" else "does not exist" - case lm: LastModified => "last modified(" + lm.value + ")" - } - - val hash = (f: File) => tryStamp(new Hash(Hash(f))) - val lastModified = (f: File) => tryStamp(new LastModified(f.lastModified)) - val exists = (f: File) => tryStamp(if (f.exists) present else notPresent) - - def tryStamp(g: => Stamp): Stamp = try { g } catch { case i: IOException => notPresent } - - val notPresent = new Exists(false) - val present = new Exists(true) - - def getStamp(map: Map[File, Stamp], src: File): Stamp = map.getOrElse(src, notPresent) -} - -object Stamps { - /** - * Creates a ReadStamps instance that will calculate and cache the stamp for sources and binaries - * on the first request according to the provided `srcStamp` and `binStamp` functions. Each - * stamp is calculated separately on demand. - * The stamp for a product is always recalculated. - */ - def initial(prodStamp: File => Stamp, srcStamp: File => Stamp, binStamp: File => Stamp): ReadStamps = new InitialStamps(prodStamp, srcStamp, binStamp) - - def empty: Stamps = - { - val eSt = Map.empty[File, Stamp] - apply(eSt, eSt, eSt, Map.empty[File, String]) - } - def apply(products: Map[File, Stamp], sources: Map[File, Stamp], binaries: Map[File, Stamp], binaryClassNames: Map[File, String]): Stamps = - new MStamps(products, sources, binaries, binaryClassNames) - - def merge(stamps: Traversable[Stamps]): Stamps = (Stamps.empty /: stamps)(_ ++ _) -} - -private class MStamps(val products: Map[File, Stamp], val sources: Map[File, Stamp], val binaries: Map[File, Stamp], val classNames: Map[File, String]) extends Stamps { - def allInternalSources: collection.Set[File] = sources.keySet - def allBinaries: collection.Set[File] = binaries.keySet - def allProducts: collection.Set[File] = products.keySet - - def ++(o: Stamps): Stamps = - new MStamps(products ++ o.products, sources ++ o.sources, binaries ++ o.binaries, classNames ++ o.classNames) - - def markInternalSource(src: File, s: Stamp): Stamps = - new MStamps(products, sources.updated(src, s), binaries, classNames) - - def markBinary(bin: File, className: String, s: Stamp): Stamps = - new MStamps(products, sources, binaries.updated(bin, s), classNames.updated(bin, className)) - - def markProduct(prod: File, s: Stamp): Stamps = - new MStamps(products.updated(prod, s), sources, binaries, classNames) - - def filter(prod: File => Boolean, removeSources: Iterable[File], bin: File => Boolean): Stamps = - new MStamps(products.filterKeys(prod), sources -- removeSources, binaries.filterKeys(bin), classNames.filterKeys(bin)) - - def groupBy[K](prod: Map[K, File => Boolean], f: File => K, bin: Map[K, File => Boolean]): Map[K, Stamps] = - { - val sourcesMap: Map[K, Map[File, Stamp]] = sources.groupBy(x => f(x._1)) - - val constFalse = (f: File) => false - def kStamps(k: K): Stamps = new MStamps( - products.filterKeys(prod.getOrElse(k, constFalse)), - sourcesMap.getOrElse(k, Map.empty[File, Stamp]), - binaries.filterKeys(bin.getOrElse(k, constFalse)), - classNames.filterKeys(bin.getOrElse(k, constFalse)) - ) - - (for (k <- prod.keySet ++ sourcesMap.keySet ++ bin.keySet) yield (k, kStamps(k))).toMap - } - - def product(prod: File) = getStamp(products, prod) - def internalSource(src: File) = getStamp(sources, src) - def binary(bin: File) = getStamp(binaries, bin) - def className(bin: File) = classNames get bin - - override def equals(other: Any): Boolean = other match { - case o: MStamps => products == o.products && sources == o.sources && binaries == o.binaries && classNames == o.classNames - case _ => false - } - - override lazy val hashCode: Int = (products :: sources :: binaries :: classNames :: Nil).hashCode - - override def toString: String = - "Stamps for: %d products, %d sources, %d binaries, %d classNames".format(products.size, sources.size, binaries.size, classNames.size) -} - -private class InitialStamps(prodStamp: File => Stamp, srcStamp: File => Stamp, binStamp: File => Stamp) extends ReadStamps { - import collection.mutable.{ HashMap, Map } - // cached stamps for files that do not change during compilation - private val sources: Map[File, Stamp] = new HashMap - private val binaries: Map[File, Stamp] = new HashMap - - def product(prod: File): Stamp = prodStamp(prod) - def internalSource(src: File): Stamp = synchronized { sources.getOrElseUpdate(src, srcStamp(src)) } - def binary(bin: File): Stamp = synchronized { binaries.getOrElseUpdate(bin, binStamp(bin)) } -} \ No newline at end of file diff --git a/compile/inc/src/test/scala/sbt/inc/AnalysisTest.scala b/compile/inc/src/test/scala/sbt/inc/AnalysisTest.scala deleted file mode 100644 index 1b0450e25..000000000 --- a/compile/inc/src/test/scala/sbt/inc/AnalysisTest.scala +++ /dev/null @@ -1,90 +0,0 @@ -package sbt -package inc - -import java.io.File -import scala.math.abs -import sbt.inc.TestCaseGenerators._ -import org.scalacheck._ -import Gen._ -import Prop._ -import xsbti.DependencyContext._ - -object AnalysisTest extends Properties("Analysis") { - // Merge and split a hard-coded trivial example. - property("Simple Merge and Split") = { - def f(s: String) = new File(s) - val aScala = f("A.scala") - val bScala = f("B.scala") - val aSource = genSource("A" :: "A$" :: Nil).sample.get - val bSource = genSource("B" :: "B$" :: Nil).sample.get - val cSource = genSource("C" :: Nil).sample.get - val exists = new Exists(true) - val sourceInfos = SourceInfos.makeInfo(Nil, Nil) - - // a - val aProducts = (f("A.class"), "A", exists) :: (f("A$.class"), "A$", exists) :: Nil - val aInternal = Nil - val aExternal = ExternalDependency(aScala, "C", cSource, DependencyByMemberRef) :: Nil - val aBinary = (f("x.jar"), "x", exists) :: Nil - - val a = Analysis.empty(false).addSource(aScala, aSource, exists, sourceInfos, aProducts, aInternal, aExternal, aBinary) - - // b - val bProducts = (f("B.class"), "B", exists) :: (f("B$.class"), "B$", exists) :: Nil - val bInternal = Nil - val bExternal = ExternalDependency(bScala, "A", aSource, DependencyByInheritance) :: Nil - val bBinary = (f("x.jar"), "x", exists) :: (f("y.jar"), "y", exists) :: Nil - - val b = Analysis.empty(false).addSource(bScala, bSource, exists, sourceInfos, bProducts, bInternal, bExternal, bBinary) - - // ab - // `b` has an external dependency on `a` that will be internalized - val abAProducts = (f("A.class"), "A", exists) :: (f("A$.class"), "A$", exists) :: Nil - val abAInternal = Nil - val abAExternal = ExternalDependency(aScala, "C", cSource, DependencyByMemberRef) :: Nil - val abABinary = (f("x.jar"), "x", exists) :: Nil - - val abBProducts = (f("B.class"), "B", exists) :: (f("B$.class"), "B$", exists) :: Nil - val abBInternal = InternalDependency(bScala, aScala, DependencyByMemberRef) :: InternalDependency(bScala, aScala, DependencyByInheritance) :: Nil - val abBExternal = Nil - val abBBinary = (f("x.jar"), "x", exists) :: (f("y.jar"), "y", exists) :: Nil - - val ab = Analysis.empty(false).addSource(aScala, aSource, exists, sourceInfos, abAProducts, abAInternal, abAExternal, abABinary) - .addSource(bScala, bSource, exists, sourceInfos, abBProducts, abBInternal, abBExternal, abBBinary) - - val split: Map[String, Analysis] = ab.groupBy({ f: File => f.getName.substring(0, 1) }) - - val aSplit = split.getOrElse("A", Analysis.empty(false)) - val bSplit = split.getOrElse("B", Analysis.empty(false)) - - val merged = Analysis.merge(a :: b :: Nil) - - ("split(AB)(A) == A" |: compare(a, aSplit)) && - ("split(AB)(B) == B" |: compare(b, bSplit)) && - ("merge(A, B) == AB" |: compare(merged, ab)) - } - - // Merge and split large, generated examples. - // Mustn't shrink, as the default Shrink[Int] doesn't respect the lower bound of choose(), which will cause - // a divide-by-zero error masking the original error. - // Note that the generated Analyses have nameHashing = false (Grouping of Analyses with name hashing enabled - // is not supported right now) - property("Complex Merge and Split") = forAllNoShrink(genAnalysis(nameHashing = false), choose(1, 10)) { (analysis: Analysis, numSplits: Int) => - val grouped: Map[Int, Analysis] = analysis.groupBy({ f: File => abs(f.hashCode()) % numSplits }) - def getGroup(i: Int): Analysis = grouped.getOrElse(i, Analysis.empty(false)) - val splits = (Range(0, numSplits) map getGroup).toList - - val merged: Analysis = Analysis.merge(splits) - "Merge all" |: compare(analysis, merged) - } - - // Compare two analyses with useful labelling when they aren't equal. - private[this] def compare(left: Analysis, right: Analysis): Prop = - s" LEFT: $left" |: - s"RIGHT: $right" |: - s"STAMPS EQUAL: ${left.stamps == right.stamps}" |: - s"APIS EQUAL: ${left.apis == right.apis}" |: - s"RELATIONS EQUAL: ${left.relations == right.relations}" |: - "UNEQUAL" |: - (left == right) -} diff --git a/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala b/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala deleted file mode 100644 index 7591183b1..000000000 --- a/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala +++ /dev/null @@ -1,188 +0,0 @@ -package sbt -package inc - -import java.io.File - -import org.scalacheck._ -import Arbitrary._ -import Gen._ - -import sbt.Relation -import xsbti.api._ -import xsbti.SafeLazy -import xsbti.DependencyContext._ - -/** - * Scalacheck generators for Analysis objects and their substructures. - * Fairly complex, as Analysis has interconnected state that can't be - * independently generated. - */ -object TestCaseGenerators { - // We restrict sizes, otherwise the generated Analysis objects get huge and the tests take a long time. - val maxSources = 10 // Max number of source files. - val maxRelatives = 10 // Max number of things that a source x can relate to in a single Relation. - val maxPathSegmentLen = 10 // Max number of characters in a path segment. - val maxPathLen = 6 // Max number of path segments in a path. - - // Ensure that we generate unique class names and file paths every time. - // Using repeated strings may lead to all sorts of undesirable interactions. - val used1 = scala.collection.mutable.Set.empty[String] - val used2 = scala.collection.mutable.Set.empty[String] - - // When using `retryUntil`, the condition is actually tested twice (see implementation in ScalaCheck), - // which is why we need to insert twice the element. - // If the element is present in both sets, then it has already been used. - def unique[T](g: Gen[T]) = g retryUntil { o: T => - if (used1.add(o.toString)) - true - else - used2.add(o.toString) - } - - def identifier: Gen[String] = sized { size => - resize(Math.max(size, 3), Gen.identifier) - } - - def genFilePathSegment: Gen[String] = for { - n <- choose(3, maxPathSegmentLen) // Segments have at least 3 characters. - c <- alphaChar - cs <- listOfN(n - 1, alphaNumChar) - } yield (c :: cs).mkString - - def genFile: Gen[File] = for { - n <- choose(2, maxPathLen) // Paths have at least 2 segments. - path <- listOfN(n, genFilePathSegment) - } yield new File(path.mkString("/")) - - def genStamp: Gen[Stamp] = for { - b <- oneOf(true, false) - } yield new Exists(b) - - def zipMap[A, B](a: Seq[A], b: Seq[B]): Map[A, B] = (a zip b).toMap - - def genStamps(rel: Relations): Gen[Stamps] = { - val prod = rel.allProducts.toList - val src = rel.allSources.toList - val bin = rel.allBinaryDeps.toList - for { - prodStamps <- listOfN(prod.length, genStamp) - srcStamps <- listOfN(src.length, genStamp) - binStamps <- listOfN(bin.length, genStamp) - binClassNames <- listOfN(bin.length, unique(identifier)) - } yield Stamps(zipMap(prod, prodStamps), zipMap(src, srcStamps), zipMap(bin, binStamps), zipMap(bin, binClassNames)) - } - - // We need "proper" definitions with specific class names, as groupBy use these to pick a representative top-level class when splitting. - private[this] def makeDefinition(name: String): Definition = - new ClassLike(DefinitionType.ClassDef, lzy(new EmptyType()), - lzy(new Structure(lzy(Array()), lzy(Array()), lzy(Array()))), Array(), Array(), - name, new Public(), new Modifiers(false, false, false, false, false, false, false), Array()) - - private[this] def lzy[T <: AnyRef](x: T) = SafeLazy.strict(x) - - def genNameHash(defn: String): Gen[xsbti.api._internalOnly_NameHash] = - const(new xsbti.api._internalOnly_NameHash(defn, defn.hashCode())) - - def genNameHashes(defns: Seq[String]): Gen[xsbti.api._internalOnly_NameHashes] = { - def partitionAccordingToMask[T](mask: List[Boolean], xs: List[T]): (List[T], List[T]) = { - val (p1, p2) = (mask zip xs).partition(_._1) - (p1.map(_._2), p2.map(_._2)) - } - val pairsOfGenerators = for (defn <- defns) yield { - for { - isRegularMember <- arbitrary[Boolean] - nameHash <- genNameHash(defn) - } yield (isRegularMember, nameHash) - } - val genNameHashesList = Gen.sequence[List, xsbti.api._internalOnly_NameHash](defns.map(genNameHash)) - val genTwoListOfNameHashes = for { - nameHashesList <- genNameHashesList - isRegularMemberList <- listOfN(nameHashesList.length, arbitrary[Boolean]) - } yield partitionAccordingToMask(isRegularMemberList, nameHashesList) - for { - (regularMemberNameHashes, implicitMemberNameHashes) <- genTwoListOfNameHashes - } yield new xsbti.api._internalOnly_NameHashes(regularMemberNameHashes.toArray, implicitMemberNameHashes.toArray) - } - - def genSource(defns: Seq[String]): Gen[Source] = for { - startTime <- arbitrary[Long] - hashLen <- choose(10, 20) // Requred by SameAPI to be > 0. - hash <- Gen.containerOfN[Array, Byte](hashLen, arbitrary[Byte]) - apiHash <- arbitrary[Int] - hasMacro <- arbitrary[Boolean] - nameHashes <- genNameHashes(defns) - } yield new Source(new Compilation(startTime, Array()), hash, new SourceAPI(Array(), Array(defns map makeDefinition: _*)), apiHash, nameHashes, hasMacro) - - def genSources(all_defns: Seq[Seq[String]]): Gen[Seq[Source]] = Gen.sequence[List, Source](all_defns.map(genSource)) - - def genAPIs(rel: Relations): Gen[APIs] = { - val internal = rel.allInternalSrcDeps.toList.sorted - val external = rel.allExternalDeps.toList.sorted - for { - internalSources <- genSources(internal map { f: File => rel.classNames(f).toList.sorted }) - externalSources <- genSources(external map { s: String => s :: Nil }) - } yield APIs(zipMap(internal, internalSources), zipMap(external, externalSources)) - } - - def genRelation[T](g: Gen[T])(srcs: List[File]): Gen[Relation[File, T]] = for { - n <- choose(1, maxRelatives) - entries <- listOfN(srcs.length, containerOfN[Set, T](n, g)) - } yield Relation.reconstruct(zipMap(srcs, entries)) - - val genFileRelation = genRelation[File](unique(genFile)) _ - val genStringRelation = genRelation[String](unique(identifier)) _ - - def genRSource(srcs: List[File]): Gen[Relations.Source] = for { - internal <- listOfN(srcs.length, someOf(srcs)) // Internal dep targets must come from list of sources. - external <- genStringRelation(srcs) - } yield Relations.makeSource( // Ensure that we don't generate a dep of some file on itself. - Relation.reconstruct((srcs zip (internal map { _.toSet }) map { case (a, b) => (a, b - a) }).toMap), - external) - - def genSubRSource(src: Relations.Source): Gen[Relations.Source] = for { - internal <- someOf(src.internal.all.toList) - external <- someOf(src.external.all.toList) - } yield Relations.makeSource(Relation.empty ++ internal, Relation.empty ++ external) - - def genRSourceDependencies(srcs: List[File]): Gen[Relations.SourceDependencies] = for { - internal <- listOfN(srcs.length, someOf(srcs)) - external <- genStringRelation(srcs) - } yield Relations.makeSourceDependencies( - Relation.reconstruct((srcs zip (internal map { _.toSet }) map { case (a, b) => (a, b - a) }).toMap), - external) - - def genSubRSourceDependencies(src: Relations.SourceDependencies): Gen[Relations.SourceDependencies] = for { - internal <- someOf(src.internal.all.toList) - external <- someOf(src.external.all.toList) - } yield Relations.makeSourceDependencies(Relation.empty ++ internal, Relation.empty ++ external) - - def genRelations: Gen[Relations] = for { - numSrcs <- choose(0, maxSources) - srcs <- listOfN(numSrcs, genFile) - srcProd <- genFileRelation(srcs) - binaryDep <- genFileRelation(srcs) - direct <- genRSource(srcs) - publicInherited <- genSubRSource(direct) - classes <- genStringRelation(srcs) - - } yield Relations.make(srcProd, binaryDep, direct, publicInherited, classes) - - def genRelationsNameHashing: Gen[Relations] = for { - numSrcs <- choose(0, maxSources) - srcs <- listOfN(numSrcs, genFile) - srcProd <- genFileRelation(srcs) - binaryDep <- genFileRelation(srcs) - memberRef <- genRSourceDependencies(srcs) - inheritance <- genSubRSourceDependencies(memberRef) - classes <- genStringRelation(srcs) - names <- genStringRelation(srcs) - internal <- InternalDependencies(Map(DependencyByMemberRef -> memberRef.internal, DependencyByInheritance -> inheritance.internal)) - external <- ExternalDependencies(Map(DependencyByMemberRef -> memberRef.external, DependencyByInheritance -> inheritance.external)) - } yield Relations.make(srcProd, binaryDep, internal, external, classes, names) - - def genAnalysis(nameHashing: Boolean): Gen[Analysis] = for { - rels <- if (nameHashing) genRelationsNameHashing else genRelations - stamps <- genStamps(rels) - apis <- genAPIs(rels) - } yield new MAnalysis(stamps, apis, rels, SourceInfos.empty, Compilations.empty) -} diff --git a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala b/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala deleted file mode 100644 index d7da7eb8a..000000000 --- a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala +++ /dev/null @@ -1,249 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package compiler - -import inc._ - -import scala.annotation.tailrec -import java.io.File -import classpath.ClasspathUtilities -import classfile.Analyze -import inc.Locate.DefinesClass -import inc.IncOptions -import CompileSetup._ -import sbinary.DefaultProtocol.{ immutableMapFormat, immutableSetFormat, StringFormat } - -import xsbti.{ Reporter, AnalysisCallback } -import xsbti.api.Source -import xsbti.compile.{ CompileOrder, DependencyChanges, GlobalsCache, Output, SingleOutput, MultipleOutput, CompileProgress } -import CompileOrder.{ JavaThenScala, Mixed, ScalaThenJava } - -@deprecated("Use MixedAnalyzingCompiler or IC instead.", "0.13.8") -class AggressiveCompile(cacheFile: File) { - @deprecated("Use IC.compile instead.", "0.13.8") - def apply(compiler: AnalyzingCompiler, - javac: xsbti.compile.JavaCompiler, - sources: Seq[File], classpath: Seq[File], - output: Output, - cache: GlobalsCache, - progress: Option[CompileProgress] = None, - options: Seq[String] = Nil, - javacOptions: Seq[String] = Nil, - analysisMap: File => Option[Analysis] = { _ => None }, - definesClass: DefinesClass = Locate.definesClass _, - reporter: Reporter, - compileOrder: CompileOrder = Mixed, - skip: Boolean = false, - incrementalCompilerOptions: IncOptions)(implicit log: Logger): Analysis = - { - val setup = new CompileSetup(output, new CompileOptions(options, javacOptions), - compiler.scalaInstance.actualVersion, compileOrder, incrementalCompilerOptions.nameHashing) - compile1(sources, classpath, setup, progress, store, analysisMap, definesClass, - compiler, javac, reporter, skip, cache, incrementalCompilerOptions) - } - - def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = - args.bootClasspathFor(classpath) ++ args.extClasspath ++ args.finishClasspath(classpath) - - def compile1(sources: Seq[File], - classpath: Seq[File], - setup: CompileSetup, progress: Option[CompileProgress], - store: AnalysisStore, - analysis: File => Option[Analysis], - definesClass: DefinesClass, - compiler: AnalyzingCompiler, - javac: xsbti.compile.JavaCompiler, - reporter: Reporter, skip: Boolean, - cache: GlobalsCache, - incrementalCompilerOptions: IncOptions)(implicit log: Logger): Analysis = - { - val (previousAnalysis, previousSetup) = extract(store.get(), incrementalCompilerOptions) - if (skip) - previousAnalysis - else { - val config = new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, - progress, analysis, definesClass, reporter, compiler, javac, cache, incrementalCompilerOptions) - val (modified, result) = compile2(config) - if (modified) - store.set(result, setup) - result - } - } - def compile2(config: CompileConfiguration)(implicit log: Logger, equiv: Equiv[CompileSetup]): (Boolean, Analysis) = - { - import config._ - import currentSetup._ - val absClasspath = classpath.map(_.getAbsoluteFile) - val apiOption = (api: Either[Boolean, Source]) => api.right.toOption - val cArgs = new CompilerArguments(compiler.scalaInstance, compiler.cp) - val searchClasspath = explicitBootClasspath(options.options) ++ withBootclasspath(cArgs, absClasspath) - val entry = Locate.entry(searchClasspath, definesClass) - - val compile0 = (include: Set[File], changes: DependencyChanges, callback: AnalysisCallback) => { - val outputDirs = outputDirectories(output) - outputDirs foreach (IO.createDirectory) - val incSrc = sources.filter(include) - val (javaSrcs, scalaSrcs) = incSrc partition javaOnly - logInputs(log, javaSrcs.size, scalaSrcs.size, outputDirs) - def compileScala() = - if (scalaSrcs.nonEmpty) { - val sources = if (order == Mixed) incSrc else scalaSrcs - val arguments = cArgs(Nil, absClasspath, None, options.options) - timed("Scala compilation", log) { - compiler.compile(sources, changes, arguments, output, callback, reporter, cache, log, progress) - } - } - def compileJava() = - if (javaSrcs.nonEmpty) { - import Path._ - @tailrec def ancestor(f1: File, f2: File): Boolean = - if (f2 eq null) false else if (f1 == f2) true else ancestor(f1, f2.getParentFile) - - val chunks: Map[Option[File], Seq[File]] = output match { - case single: SingleOutput => Map(Some(single.outputDirectory) -> javaSrcs) - case multi: MultipleOutput => - javaSrcs groupBy { src => - multi.outputGroups find { out => ancestor(out.sourceDirectory, src) } map (_.outputDirectory) - } - } - chunks.get(None) foreach { srcs => - log.error("No output directory mapped for: " + srcs.map(_.getAbsolutePath).mkString(",")) - } - val memo = for ((Some(outputDirectory), srcs) <- chunks) yield { - val classesFinder = PathFinder(outputDirectory) ** "*.class" - (classesFinder, classesFinder.get, srcs) - } - - val loader = ClasspathUtilities.toLoader(searchClasspath) - timed("Java compilation", log) { - try javac.compileWithReporter(javaSrcs.toArray, absClasspath.toArray, output, options.javacOptions.toArray, reporter, log) - catch { - // Handle older APIs - case _: NoSuchMethodError => - javac.compile(javaSrcs.toArray, absClasspath.toArray, output, options.javacOptions.toArray, log) - } - } - - def readAPI(source: File, classes: Seq[Class[_]]): Set[String] = { - val (api, inherits) = ClassToAPI.process(classes) - callback.api(source, api) - inherits.map(_.getName) - } - - timed("Java analysis", log) { - for ((classesFinder, oldClasses, srcs) <- memo) { - val newClasses = Set(classesFinder.get: _*) -- oldClasses - Analyze(newClasses.toSeq, srcs, log)(callback, loader, readAPI) - } - } - } - if (order == JavaThenScala) { compileJava(); compileScala() } else { compileScala(); compileJava() } - } - - val sourcesSet = sources.toSet - val analysis = previousSetup match { - case Some(previous) if previous.nameHashing != currentSetup.nameHashing => - // if the value of `nameHashing` flag has changed we have to throw away - // previous Analysis completely and start with empty Analysis object - // that supports the particular value of the `nameHashing` flag. - // Otherwise we'll be getting UnsupportedOperationExceptions - log.warn("Ignoring previous analysis due to incompatible nameHashing setting.") - Analysis.empty(currentSetup.nameHashing) - case Some(previous) if equiv.equiv(previous, currentSetup) => previousAnalysis - case _ => - log.warn("Pruning sources from previous analysis, due to incompatible CompileSetup.") - Incremental.prune(sourcesSet, previousAnalysis) - } - IncrementalCompile(sourcesSet, entry, compile0, analysis, getAnalysis, output, log, incOptions) - } - private[this] def outputDirectories(output: Output): Seq[File] = output match { - case single: SingleOutput => List(single.outputDirectory) - case mult: MultipleOutput => mult.outputGroups map (_.outputDirectory) - } - private[this] def timed[T](label: String, log: Logger)(t: => T): T = - { - val start = System.nanoTime - val result = t - val elapsed = System.nanoTime - start - log.debug(label + " took " + (elapsed / 1e9) + " s") - result - } - private[this] def logInputs(log: Logger, javaCount: Int, scalaCount: Int, outputDirs: Seq[File]): Unit = { - val scalaMsg = Analysis.counted("Scala source", "", "s", scalaCount) - val javaMsg = Analysis.counted("Java source", "", "s", javaCount) - val combined = scalaMsg ++ javaMsg - if (combined.nonEmpty) - log.info(combined.mkString("Compiling ", " and ", " to " + outputDirs.map(_.getAbsolutePath).mkString(",") + "...")) - } - private def extract(previous: Option[(Analysis, CompileSetup)], incOptions: IncOptions): (Analysis, Option[CompileSetup]) = - previous match { - case Some((an, setup)) => (an, Some(setup)) - case None => (Analysis.empty(nameHashing = incOptions.nameHashing), None) - } - def javaOnly(f: File) = f.getName.endsWith(".java") - - private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] = - options.dropWhile(_ != CompilerArguments.BootClasspathOption).slice(1, 2).headOption.toList.flatMap(IO.parseClasspath) - - val store = MixedAnalyzingCompiler.staticCachedStore(cacheFile) - -} -@deprecated("Use MixedAnalyzingCompiler instead.", "0.13.8") -object AggressiveCompile { - @deprecated("Use MixedAnalyzingCompiler.staticCachedStore instead.", "0.13.8") - def staticCachedStore(cacheFile: File) = MixedAnalyzingCompiler.staticCachedStore(cacheFile) - - @deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8") - def directOrFork(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File]): JavaTool = - if (javaHome.isDefined) - JavaCompiler.fork(cpOptions, instance)(forkJavac(javaHome)) - else - JavaCompiler.directOrFork(cpOptions, instance)(forkJavac(None)) - - @deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8") - def forkJavac(javaHome: Option[File]): JavaCompiler.Fork = - { - import Path._ - def exec(jc: JavacContract) = javaHome match { case None => jc.name; case Some(jh) => (jh / "bin" / jc.name).absolutePath } - (contract: JavacContract, args: Seq[String], log: Logger) => { - log.debug("Forking " + contract.name + ": " + exec(contract) + " " + args.mkString(" ")) - val javacLogger = new JavacLogger(log) - var exitCode = -1 - try { - exitCode = Process(exec(contract), args) ! javacLogger - } finally { - javacLogger.flush(exitCode) - } - exitCode - } - } -} - -@deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8") -private[sbt] class JavacLogger(log: Logger) extends ProcessLogger { - import scala.collection.mutable.ListBuffer - import Level.{ Info, Warn, Error, Value => LogLevel } - - private val msgs: ListBuffer[(LogLevel, String)] = new ListBuffer() - - def info(s: => String): Unit = - synchronized { msgs += ((Info, s)) } - - def error(s: => String): Unit = - synchronized { msgs += ((Error, s)) } - - def buffer[T](f: => T): T = f - - private def print(desiredLevel: LogLevel)(t: (LogLevel, String)) = t match { - case (Info, msg) => log.info(msg) - case (Error, msg) => log.log(desiredLevel, msg) - } - - def flush(exitCode: Int): Unit = { - val level = if (exitCode == 0) Warn else Error - msgs foreach print(level) - msgs.clear() - } -} diff --git a/compile/integration/src/main/scala/sbt/compiler/CompileConfiguration.scala b/compile/integration/src/main/scala/sbt/compiler/CompileConfiguration.scala deleted file mode 100644 index d5e6b0a91..000000000 --- a/compile/integration/src/main/scala/sbt/compiler/CompileConfiguration.scala +++ /dev/null @@ -1,41 +0,0 @@ -package sbt.compiler - -import java.io.File - -import sbt.CompileSetup -import sbt.inc.{ IncOptions, Analysis } -import sbt.inc.Locate._ -import xsbti.Reporter -import xsbti.compile.{ GlobalsCache, CompileProgress } - -/** - * Configuration used for running an analyzing compiler (a compiler which can extract dependencies between source files and JARs). - * - * @param sources - * @param classpath - * @param previousAnalysis - * @param previousSetup - * @param currentSetup - * @param progress - * @param getAnalysis - * @param definesClass - * @param reporter - * @param compiler - * @param javac - * @param cache - * @param incOptions - */ -final class CompileConfiguration( - val sources: Seq[File], - val classpath: Seq[File], - val previousAnalysis: Analysis, - val previousSetup: Option[CompileSetup], - val currentSetup: CompileSetup, - val progress: Option[CompileProgress], - val getAnalysis: File => Option[Analysis], - val definesClass: DefinesClass, - val reporter: Reporter, - val compiler: AnalyzingCompiler, - val javac: xsbti.compile.JavaCompiler, - val cache: GlobalsCache, - val incOptions: IncOptions) diff --git a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala b/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala deleted file mode 100644 index f3a73adc7..000000000 --- a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala +++ /dev/null @@ -1,162 +0,0 @@ -package sbt.compiler - -import java.io.File -import sbt.compiler.javac.AnalyzingJavaCompiler -import sbt.inc.Locate._ -import sbt._ -import sbt.inc._ -import xsbti.Logger -import xsbti.api.Source -import xsbti.compile.ClasspathOptions -import xsbti.compile.CompileOrder._ -import xsbti.compile.DefinesClass -import xsbti.compile.ScalaInstance -import xsbti.{ Reporter, Logger, Maybe } -import xsbti.compile._ - -// TODO - -// 1. Move analyzingCompile from MixedAnalyzingCompiler into here -// 2. Create AnalyzingJavaComiler class -// 3. MixedAnalyzingCompiler should just provide the raw 'compile' method used in incremental compiler (and -// by this class. - -/** - * An implementation of the incremental compiler that can compile inputs and dump out source dependency analysis. - */ -object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler] { - - override def compile(in: Inputs[Analysis, AnalyzingCompiler], log: Logger): Analysis = - { - val setup = in.setup; import setup._ - val options = in.options; import options.{ options => scalacOptions, _ } - val compilers = in.compilers; import compilers._ - val aMap = (f: File) => m2o(analysisMap(f)) - val defClass = (f: File) => { val dc = definesClass(f); (name: String) => dc.apply(name) } - val incOptions = IncOptions.fromStringMap(incrementalCompilerOptions) - val (previousAnalysis, previousSetup) = { - MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile()).get().map { - case (a, s) => (a, Some(s)) - } getOrElse { - (Analysis.empty(nameHashing = incOptions.nameHashing), None) - } - } - incrementalCompile(scalac, javac, sources, classpath, output, cache, m2o(progress), scalacOptions, javacOptions, previousAnalysis, - previousSetup, aMap, defClass, reporter, order, skip, incOptions)(log).analysis - } - - private[this] def m2o[S](opt: Maybe[S]): Option[S] = if (opt.isEmpty) None else Some(opt.get) - - @deprecated("A logger is no longer needed.", "0.13.8") - override def newScalaCompiler(instance: ScalaInstance, interfaceJar: File, options: ClasspathOptions, log: Logger): AnalyzingCompiler = - new AnalyzingCompiler(instance, CompilerInterfaceProvider.constant(interfaceJar), options) - - override def newScalaCompiler(instance: ScalaInstance, interfaceJar: File, options: ClasspathOptions): AnalyzingCompiler = - new AnalyzingCompiler(instance, CompilerInterfaceProvider.constant(interfaceJar), options) - - def compileInterfaceJar(label: String, sourceJar: File, targetJar: File, interfaceJar: File, instance: ScalaInstance, log: Logger) { - val raw = new RawCompiler(instance, sbt.ClasspathOptions.auto, log) - AnalyzingCompiler.compileSources(sourceJar :: Nil, targetJar, interfaceJar :: Nil, label, raw, log) - } - - def readCache(file: File): Maybe[(Analysis, CompileSetup)] = - try { Maybe.just(readCacheUncaught(file)) } catch { case _: Exception => Maybe.nothing() } - - @deprecated("Use overloaded variant which takes `IncOptions` as parameter.", "0.13.2") - def readAnalysis(file: File): Analysis = - try { readCacheUncaught(file)._1 } catch { case _: Exception => Analysis.Empty } - - def readAnalysis(file: File, incOptions: IncOptions): Analysis = - try { readCacheUncaught(file)._1 } catch { - case _: Exception => Analysis.empty(nameHashing = incOptions.nameHashing) - } - - def readCacheUncaught(file: File): (Analysis, CompileSetup) = - Using.fileReader(IO.utf8)(file) { reader => - try { - TextAnalysisFormat.read(reader) - } catch { - case ex: sbt.inc.ReadException => - throw new java.io.IOException(s"Error while reading $file", ex) - } - } - - /** The result of running the compilation. */ - final case class Result(analysis: Analysis, setup: CompileSetup, hasModified: Boolean) - - /** - * This will run a mixed-compilation of Java/Scala sources - * - * - * TODO - this is the interface sbt uses. Somehow this needs to be exposed further. - * - * @param scalac An instances of the Scalac compiler which can also extract "Analysis" (dependencies) - * @param javac An instance of the Javac compiler. - * @param sources The set of sources to compile - * @param classpath The classpath to use when compiling. - * @param output Configuration for where to output .class files. - * @param cache The caching mechanism to use instead of insantiating new compiler instances. - * @param progress Progress listening for the compilation process. TODO - Feed this through the Javac Compiler! - * @param options Options for the Scala compiler - * @param javacOptions Options for the Java compiler - * @param previousAnalysis The previous dependency Analysis object/ - * @param previousSetup The previous compilation setup (if any) - * @param analysisMap A map of file to the dependency analysis of that file. - * @param definesClass A mehcnaism of looking up whether or not a JAR defines a particular Class. - * @param reporter Where we sent all compilation error/warning events - * @param compileOrder The order we'd like to mix compilation. JavaThenScala, ScalaThenJava or Mixed. - * @param skip IF true, we skip compilation and just return the previous analysis file. - * @param incrementalCompilerOptions Options specific to incremental compilation. - * @param log The location where we write log messages. - * @return The full configuration used to instantiate this mixed-analyzing compiler, the set of extracted dependencies and - * whether or not any file were modified. - */ - def incrementalCompile(scalac: AnalyzingCompiler, - javac: xsbti.compile.JavaCompiler, - sources: Seq[File], - classpath: Seq[File], - output: Output, - cache: GlobalsCache, - progress: Option[CompileProgress] = None, - options: Seq[String] = Nil, - javacOptions: Seq[String] = Nil, - previousAnalysis: Analysis, - previousSetup: Option[CompileSetup], - analysisMap: File => Option[Analysis] = { _ => None }, - definesClass: Locate.DefinesClass = Locate.definesClass _, - reporter: Reporter, - compileOrder: CompileOrder = Mixed, - skip: Boolean = false, - incrementalCompilerOptions: IncOptions)(implicit log: Logger): Result = { - val config = MixedAnalyzingCompiler.makeConfig(scalac, javac, sources, classpath, output, cache, - progress, options, javacOptions, previousAnalysis, previousSetup, analysisMap, definesClass, reporter, - compileOrder, skip, incrementalCompilerOptions - ) - import config.{ currentSetup => setup } - - if (skip) Result(previousAnalysis, setup, false) - else { - val (analysis, changed) = compileInternal(MixedAnalyzingCompiler(config)(log)) - Result(analysis, setup, changed) - } - } - - /** Actually runs the incremental compiler using the given mixed compiler. This will prune the inputs based on the CompileSetup. */ - private def compileInternal(mixedCompiler: MixedAnalyzingCompiler)(implicit log: Logger, equiv: Equiv[CompileSetup]): (Analysis, Boolean) = { - val entry = MixedAnalyzingCompiler.classPathLookup(mixedCompiler.config) - import mixedCompiler.config._ - import mixedCompiler.config.currentSetup.output - val sourcesSet = sources.toSet - val analysis = previousSetup match { - case Some(previous) if previous.nameHashing != currentSetup.nameHashing => - // if the value of `nameHashing` flag has changed we have to throw away - // previous Analysis completely and start with empty Analysis object - // that supports the particular value of the `nameHashing` flag. - // Otherwise we'll be getting UnsupportedOperationExceptions - Analysis.empty(currentSetup.nameHashing) - case Some(previous) if equiv.equiv(previous, currentSetup) => previousAnalysis - case _ => Incremental.prune(sourcesSet, previousAnalysis) - } - // Run the incremental compiler using the mixed compiler we've defined. - IncrementalCompile(sourcesSet, entry, mixedCompiler.compile, analysis, getAnalysis, output, log, incOptions).swap - } -} diff --git a/compile/integration/src/main/scala/sbt/compiler/MixedAnalyzingCompiler.scala b/compile/integration/src/main/scala/sbt/compiler/MixedAnalyzingCompiler.scala deleted file mode 100644 index 4081ca473..000000000 --- a/compile/integration/src/main/scala/sbt/compiler/MixedAnalyzingCompiler.scala +++ /dev/null @@ -1,205 +0,0 @@ -package sbt.compiler - -import java.io.File -import java.lang.ref.{ SoftReference, Reference } - -import sbt.classfile.Analyze -import sbt.classpath.ClasspathUtilities -import sbt.compiler.javac.AnalyzingJavaCompiler -import sbt.inc.Locate.DefinesClass -import sbt._ -import sbt.inc._ -import sbt.inc.Locate -import xsbti.{ AnalysisCallback, Reporter } -import xsbti.api.Source -import xsbti.compile.CompileOrder._ -import xsbti.compile._ - -/** An instance of an analyzing compiler that can run both javac + scalac. */ -final class MixedAnalyzingCompiler( - val scalac: AnalyzingCompiler, - val javac: AnalyzingJavaCompiler, - val config: CompileConfiguration, - val log: Logger) { - import config._ - import currentSetup._ - - private[this] val absClasspath = classpath.map(_.getAbsoluteFile) - /** Mechanism to work with compiler arguments. */ - private[this] val cArgs = new CompilerArguments(compiler.scalaInstance, compiler.cp) - - /** - * Compiles the given Java/Scala files. - * - * @param include The files to compile right now - * @param changes A list of dependency changes. - * @param callback The callback where we report dependency issues. - */ - def compile(include: Set[File], changes: DependencyChanges, callback: AnalysisCallback): Unit = { - val outputDirs = outputDirectories(output) - outputDirs foreach (IO.createDirectory) - val incSrc = sources.filter(include) - val (javaSrcs, scalaSrcs) = incSrc partition javaOnly - logInputs(log, javaSrcs.size, scalaSrcs.size, outputDirs) - /** compiles the scala code necessary using the analyzing compiler. */ - def compileScala(): Unit = - if (scalaSrcs.nonEmpty) { - val sources = if (order == Mixed) incSrc else scalaSrcs - val arguments = cArgs(Nil, absClasspath, None, options.options) - timed("Scala compilation", log) { - compiler.compile(sources, changes, arguments, output, callback, reporter, config.cache, log, progress) - } - } - /** - * Compiles the Java code necessary. All analysis code is included in this method. - */ - def compileJava(): Unit = - if (javaSrcs.nonEmpty) { - // Runs the analysis portion of Javac. - timed("Java compile + analysis", log) { - javac.compile(javaSrcs, options.javacOptions.toArray[String], output, callback, reporter, log, progress) - } - } - // TODO - Maybe on "Mixed" we should try to compile both Scala + Java. - if (order == JavaThenScala) { compileJava(); compileScala() } else { compileScala(); compileJava() } - } - - private[this] def outputDirectories(output: Output): Seq[File] = output match { - case single: SingleOutput => List(single.outputDirectory) - case mult: MultipleOutput => mult.outputGroups map (_.outputDirectory) - } - /** Debugging method to time how long it takes to run various compilation tasks. */ - private[this] def timed[T](label: String, log: Logger)(t: => T): T = { - val start = System.nanoTime - val result = t - val elapsed = System.nanoTime - start - log.debug(label + " took " + (elapsed / 1e9) + " s") - result - } - - private[this] def logInputs(log: Logger, javaCount: Int, scalaCount: Int, outputDirs: Seq[File]): Unit = { - val scalaMsg = Analysis.counted("Scala source", "", "s", scalaCount) - val javaMsg = Analysis.counted("Java source", "", "s", javaCount) - val combined = scalaMsg ++ javaMsg - if (combined.nonEmpty) - log.info(combined.mkString("Compiling ", " and ", " to " + outputDirs.map(_.getAbsolutePath).mkString(",") + "...")) - } - - /** Returns true if the file is java. */ - private[this] def javaOnly(f: File) = f.getName.endsWith(".java") -} - -/** - * This is a compiler that mixes the `sbt.compiler.AnalyzingCompiler` for Scala incremental compilation - * with a `xsbti.JavaCompiler`, allowing cross-compilation of mixed Java/Scala projects with analysis output. - * - * - * NOTE: this class *defines* how to run one step of cross-Java-Scala compilation and then delegates - * down to the incremental compiler for the rest. - */ -object MixedAnalyzingCompiler { - - def makeConfig(scalac: AnalyzingCompiler, - javac: xsbti.compile.JavaCompiler, - sources: Seq[File], - classpath: Seq[File], - output: Output, - cache: GlobalsCache, - progress: Option[CompileProgress] = None, - options: Seq[String] = Nil, - javacOptions: Seq[String] = Nil, - previousAnalysis: Analysis, - previousSetup: Option[CompileSetup], - analysisMap: File => Option[Analysis] = { _ => None }, - definesClass: DefinesClass = Locate.definesClass _, - reporter: Reporter, - compileOrder: CompileOrder = Mixed, - skip: Boolean = false, - incrementalCompilerOptions: IncOptions): CompileConfiguration = - { - val compileSetup = new CompileSetup(output, new CompileOptions(options, javacOptions), - scalac.scalaInstance.actualVersion, compileOrder, incrementalCompilerOptions.nameHashing) - config( - sources, - classpath, - compileSetup, - progress, - previousAnalysis, - previousSetup, - analysisMap, - definesClass, - scalac, - javac, - reporter, - skip, - cache, - incrementalCompilerOptions) - } - - def config( - sources: Seq[File], - classpath: Seq[File], - setup: CompileSetup, - progress: Option[CompileProgress], - previousAnalysis: Analysis, - previousSetup: Option[CompileSetup], - analysis: File => Option[Analysis], - definesClass: DefinesClass, - compiler: AnalyzingCompiler, - javac: xsbti.compile.JavaCompiler, - reporter: Reporter, - skip: Boolean, - cache: GlobalsCache, - incrementalCompilerOptions: IncOptions): CompileConfiguration = { - import CompileSetup._ - new CompileConfiguration(sources, classpath, previousAnalysis, previousSetup, setup, - progress, analysis, definesClass, reporter, compiler, javac, cache, incrementalCompilerOptions) - } - - /** Returns the search classpath (for dependencies) and a function which can also do so. */ - def searchClasspathAndLookup(config: CompileConfiguration): (Seq[File], String => Option[File]) = { - import config._ - import currentSetup._ - val absClasspath = classpath.map(_.getAbsoluteFile) - val apiOption = (api: Either[Boolean, Source]) => api.right.toOption - val cArgs = new CompilerArguments(compiler.scalaInstance, compiler.cp) - val searchClasspath = explicitBootClasspath(options.options) ++ withBootclasspath(cArgs, absClasspath) - (searchClasspath, Locate.entry(searchClasspath, definesClass)) - } - - /** Returns a "lookup file for a given class name" function. */ - def classPathLookup(config: CompileConfiguration): String => Option[File] = - searchClasspathAndLookup(config)._2 - - def apply(config: CompileConfiguration)(implicit log: Logger): MixedAnalyzingCompiler = { - import config._ - val (searchClasspath, entry) = searchClasspathAndLookup(config) - // Construct a compiler which can handle both java and scala sources. - new MixedAnalyzingCompiler( - compiler, - // TODO - Construction of analyzing Java compiler MAYBE should be earlier... - new AnalyzingJavaCompiler(javac, classpath, compiler.scalaInstance, entry, searchClasspath), - config, - log - ) - } - - def withBootclasspath(args: CompilerArguments, classpath: Seq[File]): Seq[File] = - args.bootClasspathFor(classpath) ++ args.extClasspath ++ args.finishClasspath(classpath) - private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] = - options.dropWhile(_ != CompilerArguments.BootClasspathOption).slice(1, 2).headOption.toList.flatMap(IO.parseClasspath) - - private[this] val cache = new collection.mutable.HashMap[File, Reference[AnalysisStore]] - private def staticCache(file: File, backing: => AnalysisStore): AnalysisStore = - synchronized { - cache get file flatMap { ref => Option(ref.get) } getOrElse { - val b = backing - cache.put(file, new SoftReference(b)) - b - } - } - - /** Create a an analysis store cache at the desired location. */ - def staticCachedStore(cacheFile: File) = staticCache(cacheFile, AnalysisStore.sync(AnalysisStore.cached(FileBasedStore(cacheFile)))) - -} diff --git a/compile/integration/src/main/scala/sbt/compiler/javac/AnalyzingJavaCompiler.scala b/compile/integration/src/main/scala/sbt/compiler/javac/AnalyzingJavaCompiler.scala deleted file mode 100644 index 9bae1d5af..000000000 --- a/compile/integration/src/main/scala/sbt/compiler/javac/AnalyzingJavaCompiler.scala +++ /dev/null @@ -1,98 +0,0 @@ -package sbt.compiler.javac - -import java.io.File - -import sbt._ -import sbt.classfile.Analyze -import sbt.classpath.ClasspathUtilities -import sbt.compiler.CompilerArguments -import sbt.inc.Locate -import xsbti.api.Source -import xsbti.compile._ -import xsbti.{ AnalysisCallback, Reporter } - -/** - * This is a java compiler which will also report any discovered source dependencies/apis out via - * an analysis callback. - * - * @param searchClasspath Differes from classpath in that we look up binary dependencies via this classpath. - * @param classLookup A mechanism by which we can figure out if a JAR contains a classfile. - */ -final class AnalyzingJavaCompiler private[sbt] ( - val javac: xsbti.compile.JavaCompiler, - val classpath: Seq[File], - val scalaInstance: xsbti.compile.ScalaInstance, - val classLookup: (String => Option[File]), - val searchClasspath: Seq[File]) { - /** - * Compile some java code using the current configured compiler. - * - * @param sources The sources to compile - * @param options The options for the Java compiler - * @param output The output configuration for this compiler - * @param callback A callback to report discovered source/binary dependencies on. - * @param reporter A reporter where semantic compiler failures can be reported. - * @param log A place where we can log debugging/error messages. - * @param progressOpt An optional compilation progress reporter. Where we can report back what files we're currently compiling. - */ - def compile(sources: Seq[File], options: Seq[String], output: Output, callback: AnalysisCallback, reporter: Reporter, log: Logger, progressOpt: Option[CompileProgress]): Unit = { - if (sources.nonEmpty) { - val absClasspath = classpath.map(_.getAbsoluteFile) - @annotation.tailrec def ancestor(f1: File, f2: File): Boolean = - if (f2 eq null) false else if (f1 == f2) true else ancestor(f1, f2.getParentFile) - // Here we outline "chunks" of compiles we need to run so that the .class files end up in the right - // location for Java. - val chunks: Map[Option[File], Seq[File]] = output match { - case single: SingleOutput => Map(Some(single.outputDirectory) -> sources) - case multi: MultipleOutput => - sources groupBy { src => - multi.outputGroups find { out => ancestor(out.sourceDirectory, src) } map (_.outputDirectory) - } - } - // Report warnings about source files that have no output directory. - chunks.get(None) foreach { srcs => - log.error("No output directory mapped for: " + srcs.map(_.getAbsolutePath).mkString(",")) - } - // Here we try to memoize (cache) the known class files in the output directory. - val memo = for ((Some(outputDirectory), srcs) <- chunks) yield { - val classesFinder = PathFinder(outputDirectory) ** "*.class" - (classesFinder, classesFinder.get, srcs) - } - // Here we construct a class-loader we'll use to load + analyze the - val loader = ClasspathUtilities.toLoader(searchClasspath) - // TODO - Perhaps we just record task 0/2 here - timed("Java compilation", log) { - try javac.compileWithReporter(sources.toArray, absClasspath.toArray, output, options.toArray, reporter, log) - catch { - // Handle older APIs - case _: NoSuchMethodError => - javac.compile(sources.toArray, absClasspath.toArray, output, options.toArray, log) - } - } - // TODO - Perhaps we just record task 1/2 here - - /** Reads the API information directly from the Class[_] object. Used when Analyzing dependencies. */ - def readAPI(source: File, classes: Seq[Class[_]]): Set[String] = { - val (api, inherits) = ClassToAPI.process(classes) - callback.api(source, api) - inherits.map(_.getName) - } - // Runs the analysis portion of Javac. - timed("Java analysis", log) { - for ((classesFinder, oldClasses, srcs) <- memo) { - val newClasses = Set(classesFinder.get: _*) -- oldClasses - Analyze(newClasses.toSeq, srcs, log)(callback, loader, readAPI) - } - } - // TODO - Perhaps we just record task 2/2 here - } - } - /** Debugging method to time how long it takes to run various compilation tasks. */ - private[this] def timed[T](label: String, log: Logger)(t: => T): T = { - val start = System.nanoTime - val result = t - val elapsed = System.nanoTime - start - log.debug(label + " took " + (elapsed / 1e9) + " s") - result - } -} diff --git a/compile/interface/NOTICE b/compile/interface/NOTICE deleted file mode 100644 index df4893a46..000000000 --- a/compile/interface/NOTICE +++ /dev/null @@ -1,7 +0,0 @@ -Simple Build Tool: Compiler Interface Component -Copyright 2008, 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) - -Portions based on code from the Scala compiler. -Copyright 2002-2008 EPFL, Lausanne -Licensed under BSD-style license (see licenses/LICENSE_Scala) \ No newline at end of file diff --git a/compile/interface/src/main/scala/xsbt/API.scala b/compile/interface/src/main/scala/xsbt/API.scala deleted file mode 100644 index 8af37f6b0..000000000 --- a/compile/interface/src/main/scala/xsbt/API.scala +++ /dev/null @@ -1,89 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010, 2011 Mark Harrah - */ -package xsbt - -import java.io.File -import java.util.{ Arrays, Comparator } -import scala.tools.nsc.{ io, plugins, symtab, Global, Phase } -import io.{ AbstractFile, PlainFile, ZipArchive } -import plugins.{ Plugin, PluginComponent } -import symtab.Flags -import scala.collection.mutable.{ HashMap, HashSet, ListBuffer } -import xsbti.api.{ ClassLike, DefinitionType, PathComponent, SimpleType } - -object API { - val name = "xsbt-api" -} - -final class API(val global: CallbackGlobal) extends Compat { - import global._ - - @inline def debug(msg: => String) = if (settings.verbose.value) inform(msg) - - def newPhase(prev: Phase) = new ApiPhase(prev) - class ApiPhase(prev: Phase) extends Phase(prev) { - override def description = "Extracts the public API from source files." - def name = API.name - def run: Unit = - { - val start = System.currentTimeMillis - currentRun.units.foreach(processUnit) - val stop = System.currentTimeMillis - debug("API phase took : " + ((stop - start) / 1000.0) + " s") - } - def processUnit(unit: CompilationUnit) = if (!unit.isJava) processScalaUnit(unit) - def processScalaUnit(unit: CompilationUnit): Unit = { - val sourceFile = unit.source.file.file - debug("Traversing " + sourceFile) - val extractApi = new ExtractAPI[global.type](global, sourceFile) - val traverser = new TopLevelHandler(extractApi) - traverser.apply(unit.body) - if (global.callback.nameHashing) { - val extractUsedNames = new ExtractUsedNames[global.type](global) - val names = extractUsedNames.extract(unit) - debug("The " + sourceFile + " contains the following used names " + names) - names foreach { (name: String) => callback.usedName(sourceFile, name) } - } - val packages = traverser.packages.toArray[String].map(p => new xsbti.api.Package(p)) - val source = new xsbti.api.SourceAPI(packages, traverser.definitions.toArray[xsbti.api.Definition]) - extractApi.forceStructures() - callback.api(sourceFile, source) - } - } - - private final class TopLevelHandler(extractApi: ExtractAPI[global.type]) extends TopLevelTraverser { - val packages = new HashSet[String] - val definitions = new ListBuffer[xsbti.api.Definition] - def `class`(c: Symbol): Unit = { - definitions += extractApi.classLike(c.owner, c) - } - /** Record packages declared in the source file*/ - def `package`(p: Symbol): Unit = { - if ((p eq null) || p == NoSymbol || p.isRoot || p.isRootPackage || p.isEmptyPackageClass || p.isEmptyPackage) - () - else { - packages += p.fullName - `package`(p.enclosingPackage) - } - } - } - - private abstract class TopLevelTraverser extends Traverser { - def `class`(s: Symbol) - def `package`(s: Symbol) - override def traverse(tree: Tree): Unit = { - tree match { - case (_: ClassDef | _: ModuleDef) if isTopLevel(tree.symbol) => `class`(tree.symbol) - case p: PackageDef => - `package`(p.symbol) - super.traverse(tree) - case _ => - } - } - def isTopLevel(sym: Symbol): Boolean = - (sym ne null) && (sym != NoSymbol) && !sym.isImplClass && !sym.isNestedClass && sym.isStatic && - !sym.hasFlag(Flags.SYNTHETIC) && !sym.hasFlag(Flags.JAVA) - } - -} diff --git a/compile/interface/src/main/scala/xsbt/Analyzer.scala b/compile/interface/src/main/scala/xsbt/Analyzer.scala deleted file mode 100644 index 2bf01f630..000000000 --- a/compile/interface/src/main/scala/xsbt/Analyzer.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import scala.tools.nsc.{ io, plugins, symtab, Global, Phase } -import io.{ AbstractFile, PlainFile, ZipArchive } -import plugins.{ Plugin, PluginComponent } -import scala.collection.mutable.{ HashMap, HashSet, Map, Set } - -import java.io.File -import java.util.zip.ZipFile -import xsbti.AnalysisCallback - -object Analyzer { - def name = "xsbt-analyzer" -} -final class Analyzer(val global: CallbackGlobal) extends LocateClassFile { - import global._ - - def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev) - private class AnalyzerPhase(prev: Phase) extends Phase(prev) { - override def description = "Finds concrete instances of provided superclasses, and application entry points." - def name = Analyzer.name - def run { - for (unit <- currentRun.units if !unit.isJava) { - val sourceFile = unit.source.file.file - // build list of generated classes - for (iclass <- unit.icode) { - val sym = iclass.symbol - def addGenerated(separatorRequired: Boolean): Unit = { - for (classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists)) - callback.generatedClass(sourceFile, classFile, className(sym, '.', separatorRequired)) - } - if (sym.isModuleClass && !sym.isImplClass) { - if (isTopLevelModule(sym) && sym.companionClass == NoSymbol) - addGenerated(false) - addGenerated(true) - } else - addGenerated(false) - } - } - } - } -} diff --git a/compile/interface/src/main/scala/xsbt/Command.scala b/compile/interface/src/main/scala/xsbt/Command.scala deleted file mode 100644 index 4b127e5ff..000000000 --- a/compile/interface/src/main/scala/xsbt/Command.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Jason Zaugg - */ -package xsbt - -import scala.tools.nsc.{ CompilerCommand, Settings } - -object Command { - /** - * Construct a CompilerCommand using reflection, to be compatible with Scalac before and after - * r21274 - */ - def apply(arguments: List[String], settings: Settings): CompilerCommand = { - def constr(params: Class[_]*) = classOf[CompilerCommand].getConstructor(params: _*) - try { - constr(classOf[List[_]], classOf[Settings]).newInstance(arguments, settings) - } catch { - case e: NoSuchMethodException => - constr(classOf[List[_]], classOf[Settings], classOf[Function1[_, _]], classOf[Boolean]).newInstance(arguments, settings, (s: String) => throw new RuntimeException(s), false.asInstanceOf[AnyRef]) - } - } - - def getWarnFatal(settings: Settings): Boolean = - settings.Xwarnfatal.value - - def getNoWarn(settings: Settings): Boolean = - settings.nowarn.value -} diff --git a/compile/interface/src/main/scala/xsbt/Compat.scala b/compile/interface/src/main/scala/xsbt/Compat.scala deleted file mode 100644 index 74116c0af..000000000 --- a/compile/interface/src/main/scala/xsbt/Compat.scala +++ /dev/null @@ -1,129 +0,0 @@ -package xsbt - -import scala.tools.nsc.Global -import scala.tools.nsc.symtab.Flags - -/** - * Collection of hacks that make it possible for the compiler interface - * to stay source compatible with Scala compiler 2.9, 2.10 and 2.11. - * - * One common technique used in `Compat` class is use of implicit conversions to deal - * with methods that got renamed or moved between different Scala compiler versions. - * - * Let's pick a specific example. In Scala 2.9 and 2.10 there was a method called `toplevelClass` - * defined on `Symbol`. In 2.10 that method has been deprecated and `enclosingTopLevelClass` - * method has been introduce as a replacement. In Scala 2.11 the old `toplevelClass` method has - * been removed. How can we pick the right version based on availability of those two methods? - * - * We define an implicit conversion from Symbol to a class that contains both method definitions: - * - * implicit def symbolCompat(sym: Symbol): SymbolCompat = new SymbolCompat(sym) - * class SymbolCompat(sym: Symbol) { - * def enclosingTopLevelClass: Symbol = sym.toplevelClass - * def toplevelClass: Symbol = - * throw new RuntimeException("For source compatibility only: should not get here.") - * } - * - * We assume that client code (code in compiler interface) should always call `enclosingTopLevelClass` - * method. If we compile that code against 2.11 it will just directly link against method provided by - * Symbol. However, if we compile against 2.9 or 2.10 `enclosingTopLevelClass` won't be found so the - * implicit conversion defined above will kick in. That conversion will provide `enclosingTopLevelClass` - * that simply forwards to the old `toplevelClass` method that is available in 2.9 and 2.10 so that - * method will be called in the end. There's one twist: since `enclosingTopLevelClass` forwards to - * `toplevelClass` which doesn't exist in 2.11! Therefore, we need to also define `toplevelClass` - * that will be provided by an implicit conversion as well. However, we should never reach that method - * at runtime if either `enclosingTopLevelClass` or `toplevelClass` is available on Symbol so this - * is purely source compatibility stub. - * - * The technique described above is used in several places below. - * - */ -abstract class Compat { - val global: Global - import global._ - val LocalChild = global.tpnme.LOCAL_CHILD - val Nullary = global.NullaryMethodType - val ScalaObjectClass = definitions.ScalaObjectClass - - private[this] final class MiscCompat { - // in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD - def tpnme = nme - def LOCAL_CHILD = nme.LOCALCHILD - def LOCALCHILD = sourceCompatibilityOnly - - // in 2.10, ScalaObject was removed - def ScalaObjectClass = definitions.ObjectClass - - def NullaryMethodType = NullaryMethodTpe - - def MACRO = DummyValue - - // in 2.10, sym.moduleSuffix exists, but genJVM.moduleSuffix(Symbol) does not - def moduleSuffix(sym: Symbol): String = sourceCompatibilityOnly - // in 2.11 genJVM does not exist - def genJVM = this - } - // in 2.9, NullaryMethodType was added to Type - object NullaryMethodTpe { - def unapply(t: Type): Option[Type] = None - } - - protected implicit def symbolCompat(sym: Symbol): SymbolCompat = new SymbolCompat(sym) - protected final class SymbolCompat(sym: Symbol) { - // before 2.10, sym.moduleSuffix doesn't exist, but genJVM.moduleSuffix does - def moduleSuffix = global.genJVM.moduleSuffix(sym) - - def enclosingTopLevelClass: Symbol = sym.toplevelClass - def toplevelClass: Symbol = sourceCompatibilityOnly - } - - val DummyValue = 0 - def hasMacro(s: Symbol): Boolean = - { - val MACRO = Flags.MACRO // will be DummyValue for versions before 2.10 - MACRO != DummyValue && s.hasFlag(MACRO) - } - def moduleSuffix(s: Symbol): String = s.moduleSuffix - - private[this] def sourceCompatibilityOnly: Nothing = throw new RuntimeException("For source compatibility only: should not get here.") - - private[this] final implicit def miscCompat(n: AnyRef): MiscCompat = new MiscCompat - - object MacroExpansionOf { - def unapply(tree: Tree): Option[Tree] = { - - // MacroExpansionAttachment (MEA) compatibility for 2.8.x and 2.9.x - object Compat { - class MacroExpansionAttachment(val original: Tree) - - // Trees have no attachments in 2.8.x and 2.9.x - implicit def withAttachments(tree: Tree): WithAttachments = new WithAttachments(tree) - class WithAttachments(val tree: Tree) { - object EmptyAttachments { - def all = Set.empty[Any] - } - val attachments = EmptyAttachments - } - } - import Compat._ - - locally { - // Wildcard imports are necessary since 2.8.x and 2.9.x don't have `MacroExpansionAttachment` at all - import global._ // this is where MEA lives in 2.10.x - - // `original` has been renamed to `expandee` in 2.11.x - implicit def withExpandee(att: MacroExpansionAttachment): WithExpandee = new WithExpandee(att) - class WithExpandee(att: MacroExpansionAttachment) { - def expandee: Tree = att.original - } - - locally { - import analyzer._ // this is where MEA lives in 2.11.x - tree.attachments.all.collect { - case att: MacroExpansionAttachment => att.expandee - } headOption - } - } - } - } -} diff --git a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala b/compile/interface/src/main/scala/xsbt/CompilerInterface.scala deleted file mode 100644 index 65271d222..000000000 --- a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala +++ /dev/null @@ -1,254 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import xsbti.{ AnalysisCallback, Logger, Problem, Reporter, Severity } -import xsbti.compile._ -import scala.tools.nsc.{ backend, io, reporters, symtab, util, Phase, Global, Settings, SubComponent } -import scala.tools.nsc.interactive.RangePositions -import backend.JavaPlatform -import scala.tools.util.PathResolver -import symtab.SymbolLoaders -import util.{ ClassPath, DirectoryClassPath, MergedClassPath, JavaClassPath } -import ClassPath.{ ClassPathContext, JavaContext } -import io.AbstractFile -import scala.annotation.tailrec -import scala.collection.mutable -import Log.debug -import java.io.File - -final class CompilerInterface { - def newCompiler(options: Array[String], output: Output, initialLog: Logger, initialDelegate: Reporter, resident: Boolean): CachedCompiler = - new CachedCompiler0(options, output, new WeakLog(initialLog, initialDelegate), resident) - - def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress, cached: CachedCompiler): Unit = - cached.run(sources, changes, callback, log, delegate, progress) -} -// for compatibility with Scala versions without Global.registerTopLevelSym (2.8.1 and earlier) -sealed trait GlobalCompat { self: Global => - def registerTopLevelSym(sym: Symbol): Unit - sealed trait RunCompat { - def informUnitStarting(phase: Phase, unit: CompilationUnit): Unit = () - } -} -sealed abstract class CallbackGlobal(settings: Settings, reporter: reporters.Reporter, output: Output) extends Global(settings, reporter) with GlobalCompat { - def callback: AnalysisCallback - def findClass(name: String): Option[(AbstractFile, Boolean)] - lazy val outputDirs: Iterable[File] = { - output match { - case single: SingleOutput => List(single.outputDirectory) - case multi: MultipleOutput => multi.outputGroups.toStream map (_.outputDirectory) - } - } - // Map source files to public inherited dependencies. These dependencies are tracked as the symbol for the dealiased base class. - val inheritedDependencies = new mutable.HashMap[File, mutable.Set[Symbol]] - def addInheritedDependencies(file: File, deps: Iterable[Symbol]): Unit = { - inheritedDependencies.getOrElseUpdate(file, new mutable.HashSet) ++= deps - } -} -class InterfaceCompileFailed(val arguments: Array[String], val problems: Array[Problem], override val toString: String) extends xsbti.CompileFailed - -class InterfaceCompileCancelled(val arguments: Array[String], override val toString: String) extends xsbti.CompileCancelled - -private final class WeakLog(private[this] var log: Logger, private[this] var delegate: Reporter) { - def apply(message: String): Unit = { - assert(log ne null, "Stale reference to logger") - log.error(Message(message)) - } - def logger: Logger = log - def reporter: Reporter = delegate - def clear(): Unit = { - log = null - delegate = null - } -} - -private final class CachedCompiler0(args: Array[String], output: Output, initialLog: WeakLog, resident: Boolean) extends CachedCompiler { - val settings = new Settings(s => initialLog(s)) - output match { - case multi: MultipleOutput => - for (out <- multi.outputGroups) - settings.outputDirs.add(out.sourceDirectory.getAbsolutePath, out.outputDirectory.getAbsolutePath) - case single: SingleOutput => - settings.outputDirs.setSingleOutput(single.outputDirectory.getAbsolutePath) - } - - val command = Command(args.toList, settings) - private[this] val dreporter = DelegatingReporter(settings, initialLog.reporter) - try { - if (!noErrors(dreporter)) { - dreporter.printSummary() - handleErrors(dreporter, initialLog.logger) - } - } finally - initialLog.clear() - - def noErrors(dreporter: DelegatingReporter) = !dreporter.hasErrors && command.ok - - def commandArguments(sources: Array[File]): Array[String] = - (command.settings.recreateArgs ++ sources.map(_.getAbsolutePath)).toArray[String] - - def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress): Unit = synchronized { - debug(log, "Running cached compiler " + hashCode.toHexString + ", interfacing (CompilerInterface) with Scala compiler " + scala.tools.nsc.Properties.versionString) - val dreporter = DelegatingReporter(settings, delegate) - try { run(sources.toList, changes, callback, log, dreporter, progress) } - finally { dreporter.dropDelegate() } - } - private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, dreporter: DelegatingReporter, compileProgress: CompileProgress): Unit = { - if (command.shouldStopWithInfo) { - dreporter.info(null, command.getInfoMessage(compiler), true) - throw new InterfaceCompileFailed(args, Array(), "Compiler option supplied that disabled actual compilation.") - } - if (noErrors(dreporter)) { - debug(log, args.mkString("Calling Scala compiler with arguments (CompilerInterface):\n\t", "\n\t", "")) - compiler.set(callback, dreporter) - val run = new compiler.Run with compiler.RunCompat { - override def informUnitStarting(phase: Phase, unit: compiler.CompilationUnit): Unit = { - compileProgress.startUnit(phase.name, unit.source.path) - } - override def progress(current: Int, total: Int): Unit = { - if (!compileProgress.advance(current, total)) - cancel - } - } - val sortedSourceFiles = sources.map(_.getAbsolutePath).sortWith(_ < _) - run compile sortedSourceFiles - processUnreportedWarnings(run) - dreporter.problems foreach { p => callback.problem(p.category, p.position, p.message, p.severity, true) } - } - dreporter.printSummary() - if (!noErrors(dreporter)) handleErrors(dreporter, log) - // the case where we cancelled compilation _after_ some compilation errors got reported - // will be handled by line above so errors still will be reported properly just potentially not - // all of them (because we cancelled the compilation) - if (dreporter.cancelled) handleCompilationCancellation(dreporter, log) - } - def handleErrors(dreporter: DelegatingReporter, log: Logger): Nothing = - { - debug(log, "Compilation failed (CompilerInterface)") - throw new InterfaceCompileFailed(args, dreporter.problems, "Compilation failed") - } - def handleCompilationCancellation(dreporter: DelegatingReporter, log: Logger): Nothing = { - assert(dreporter.cancelled, "We should get here only if when compilation got cancelled") - debug(log, "Compilation cancelled (CompilerInterface)") - throw new InterfaceCompileCancelled(args, "Compilation has been cancelled") - } - def processUnreportedWarnings(run: compiler.Run): Unit = { - // allConditionalWarnings and the ConditionalWarning class are only in 2.10+ - final class CondWarnCompat(val what: String, val warnings: mutable.ListBuffer[(compiler.Position, String)]) - implicit def compat(run: AnyRef): Compat = new Compat - final class Compat { def allConditionalWarnings = List[CondWarnCompat]() } - - val warnings = run.allConditionalWarnings - if (warnings.nonEmpty) - compiler.logUnreportedWarnings(warnings.map(cw => ("" /*cw.what*/ , cw.warnings.toList))) - } - - val compiler: Compiler = { - if (command.settings.Yrangepos.value) - new Compiler() with RangePositions // unnecessary in 2.11 - else - new Compiler() - } - class Compiler extends CallbackGlobal(command.settings, dreporter, output) { - object dummy // temporary fix for #4426 - object sbtAnalyzer extends { - val global: Compiler.this.type = Compiler.this - val phaseName = Analyzer.name - val runsAfter = List("jvm") - override val runsBefore = List("terminal") - val runsRightAfter = None - } with SubComponent { - val analyzer = new Analyzer(global) - def newPhase(prev: Phase) = analyzer.newPhase(prev) - def name = phaseName - } - - /** Phase that extracts dependency information */ - object sbtDependency extends { - val global: Compiler.this.type = Compiler.this - val phaseName = Dependency.name - val runsAfter = List(API.name) - override val runsBefore = List("refchecks") - // keep API and dependency close to each other - // we might want to merge them in the future and even if don't - // do that then it makes sense to run those phases next to each other - val runsRightAfter = Some(API.name) - } with SubComponent { - val dependency = new Dependency(global) - def newPhase(prev: Phase) = dependency.newPhase(prev) - def name = phaseName - } - - /** - * This phase walks trees and constructs a representation of the public API, which is used for incremental recompilation. - * - * We extract the api after picklers, since that way we see the same symbol information/structure - * irrespective of whether we were typechecking from source / unpickling previously compiled classes. - */ - object apiExtractor extends { - val global: Compiler.this.type = Compiler.this - val phaseName = API.name - val runsAfter = List("typer") - override val runsBefore = List("erasure") - // allow apiExtractor's phase to be overridden using the sbt.api.phase property - // (in case someone would like the old timing, which was right after typer) - // TODO: consider migrating to simply specifying "pickler" for `runsAfter` and "uncurry" for `runsBefore` - val runsRightAfter = Option(System.getProperty("sbt.api.phase")) orElse Some("pickler") - } with SubComponent { - val api = new API(global) - def newPhase(prev: Phase) = api.newPhase(prev) - def name = phaseName - } - - override lazy val phaseDescriptors = - { - phasesSet += sbtAnalyzer - phasesSet += sbtDependency - phasesSet += apiExtractor - superComputePhaseDescriptors - } - // Required because computePhaseDescriptors is private in 2.8 (changed to protected sometime later). - private[this] def superComputePhaseDescriptors() = superCall("computePhaseDescriptors").asInstanceOf[List[SubComponent]] - private[this] def superDropRun(): Unit = - try { superCall("dropRun") } catch { case e: NoSuchMethodException => () } // dropRun not in 2.8.1 - private[this] def superCall(methodName: String): AnyRef = - { - val meth = classOf[Global].getDeclaredMethod(methodName) - meth.setAccessible(true) - meth.invoke(this) - } - def logUnreportedWarnings(seq: Seq[(String, List[(Position, String)])]): Unit = // Scala 2.10.x and later - { - val drep = reporter.asInstanceOf[DelegatingReporter] - for ((what, warnings) <- seq; (pos, msg) <- warnings) yield callback.problem(what, drep.convert(pos), msg, Severity.Warn, false) - } - - def set(callback: AnalysisCallback, dreporter: DelegatingReporter): Unit = { - this.callback0 = callback - reporter = dreporter - } - def clear(): Unit = { - callback0 = null - superDropRun() - reporter = null - } - - def findClass(name: String): Option[(AbstractFile, Boolean)] = - getOutputClass(name).map(f => (f, true)) orElse findOnClassPath(name).map(f => (f, false)) - - def getOutputClass(name: String): Option[AbstractFile] = - { - // This could be improved if a hint where to look is given. - val className = name.replace('.', '/') + ".class" - outputDirs map (new File(_, className)) find (_.exists) map (AbstractFile.getFile(_)) - } - - def findOnClassPath(name: String): Option[AbstractFile] = - classPath.findClass(name).flatMap(_.binary.asInstanceOf[Option[AbstractFile]]) - - private[this] var callback0: AnalysisCallback = null - def callback: AnalysisCallback = callback0 - } -} diff --git a/compile/interface/src/main/scala/xsbt/ConsoleInterface.scala b/compile/interface/src/main/scala/xsbt/ConsoleInterface.scala deleted file mode 100644 index f3cf22a7f..000000000 --- a/compile/interface/src/main/scala/xsbt/ConsoleInterface.scala +++ /dev/null @@ -1,97 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import xsbti.Logger -import scala.tools.nsc.{ GenericRunnerCommand, Interpreter, InterpreterLoop, ObjectRunner, Settings } -import scala.tools.nsc.interpreter.InteractiveReader -import scala.tools.nsc.reporters.Reporter -import scala.tools.nsc.util.ClassPath - -class ConsoleInterface { - def commandArguments(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Array[String] = - MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String] - - def run(args: Array[String], bootClasspathString: String, classpathString: String, initialCommands: String, cleanupCommands: String, loader: ClassLoader, bindNames: Array[String], bindValues: Array[Any], log: Logger): Unit = { - lazy val interpreterSettings = MakeSettings.sync(args.toList, log) - val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log) - - if (!bootClasspathString.isEmpty) - compilerSettings.bootclasspath.value = bootClasspathString - compilerSettings.classpath.value = classpathString - log.info(Message("Starting scala interpreter...")) - log.info(Message("")) - val loop = new InterpreterLoop { - - override def createInterpreter() = { - - if (loader ne null) { - in = InteractiveReader.createDefault() - interpreter = new Interpreter(settings) { - override protected def parentClassLoader = if (loader eq null) super.parentClassLoader else loader - override protected def newCompiler(settings: Settings, reporter: Reporter) = super.newCompiler(compilerSettings, reporter) - } - interpreter.setContextClassLoader() - } else - super.createInterpreter() - - def bind(values: Seq[(String, Any)]): Unit = { - // for 2.8 compatibility - final class Compat { - def bindValue(id: String, value: Any) = - interpreter.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value) - } - implicit def compat(a: AnyRef): Compat = new Compat - - for ((id, value) <- values) - interpreter.beQuietDuring(interpreter.bindValue(id, value)) - } - - bind(bindNames zip bindValues) - - if (!initialCommands.isEmpty) - interpreter.interpret(initialCommands) - } - override def closeInterpreter(): Unit = { - if (!cleanupCommands.isEmpty) - interpreter.interpret(cleanupCommands) - super.closeInterpreter() - } - } - loop.main(if (loader eq null) compilerSettings else interpreterSettings) - } -} -object MakeSettings { - def apply(args: List[String], log: Logger) = - { - val command = new GenericRunnerCommand(args, message => log.error(Message(message))) - if (command.ok) - command.settings - else - throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg) - } - - def sync(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Settings = - { - val compilerSettings = sync(args.toList, log) - if (!bootClasspathString.isEmpty) - compilerSettings.bootclasspath.value = bootClasspathString - compilerSettings.classpath.value = classpathString - compilerSettings - } - - def sync(options: List[String], log: Logger) = - { - val settings = apply(options, log) - - // -Yrepl-sync is only in 2.9.1+ - final class Compat { - def Yreplsync = settings.BooleanSetting("-Yrepl-sync", "For compatibility only.") - } - implicit def compat(s: Settings): Compat = new Compat - - settings.Yreplsync.value = true - settings - } -} diff --git a/compile/interface/src/main/scala/xsbt/DelegatingReporter.scala b/compile/interface/src/main/scala/xsbt/DelegatingReporter.scala deleted file mode 100644 index b1c7a4f4f..000000000 --- a/compile/interface/src/main/scala/xsbt/DelegatingReporter.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbt - -import xsbti.{ F0, Logger, Maybe } -import java.io.File - -private object DelegatingReporter { - def apply(settings: scala.tools.nsc.Settings, delegate: xsbti.Reporter): DelegatingReporter = - new DelegatingReporter(Command.getWarnFatal(settings), Command.getNoWarn(settings), delegate) -} - -// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter} -// Copyright 2002-2009 LAMP/EPFL -// Original author: Martin Odersky -private final class DelegatingReporter(warnFatal: Boolean, noWarn: Boolean, private[this] var delegate: xsbti.Reporter) extends scala.tools.nsc.reporters.Reporter { - import scala.tools.nsc.util.{ FakePos, NoPosition, Position } - - def dropDelegate(): Unit = { delegate = null } - def error(msg: String): Unit = error(FakePos("scalac"), msg) - - def printSummary(): Unit = delegate.printSummary() - - override def hasErrors = delegate.hasErrors - override def hasWarnings = delegate.hasWarnings - def problems = delegate.problems - override def comment(pos: Position, msg: String): Unit = delegate.comment(convert(pos), msg) - - override def reset(): Unit = { - super.reset - delegate.reset() - } - protected def info0(pos: Position, msg: String, rawSeverity: Severity, force: Boolean): Unit = { - val skip = rawSeverity == WARNING && noWarn - if (!skip) { - val severity = if (warnFatal && rawSeverity == WARNING) ERROR else rawSeverity - delegate.log(convert(pos), msg, convert(severity)) - } - } - def convert(posIn: Position): xsbti.Position = - { - val pos = - posIn match { - case null | NoPosition => NoPosition - case x: FakePos => x - case x => - posIn.inUltimateSource(posIn.source) - } - pos match { - case NoPosition | FakePos(_) => position(None, None, None, "", None, None, None) - case _ => makePosition(pos) - } - } - private[this] def makePosition(pos: Position): xsbti.Position = - { - val src = pos.source - val sourcePath = src.file.path - val sourceFile = src.file.file - val line = pos.line - val lineContent = pos.lineContent.stripLineEnd - val offset = getOffset(pos) - val pointer = offset - src.lineToOffset(src.offsetToLine(offset)) - val pointerSpace = ((lineContent: Seq[Char]).take(pointer).map { case '\t' => '\t'; case x => ' ' }).mkString - position(Some(sourcePath), Some(sourceFile), Some(line), lineContent, Some(offset), Some(pointer), Some(pointerSpace)) - } - private[this] def getOffset(pos: Position): Int = - { - // for compatibility with 2.8 - implicit def withPoint(p: Position): WithPoint = new WithPoint(pos) - final class WithPoint(val p: Position) { def point = p.offset.get } - pos.point - } - private[this] def position(sourcePath0: Option[String], sourceFile0: Option[File], line0: Option[Int], lineContent0: String, offset0: Option[Int], pointer0: Option[Int], pointerSpace0: Option[String]) = - new xsbti.Position { - val line = o2mi(line0) - val lineContent = lineContent0 - val offset = o2mi(offset0) - val sourcePath = o2m(sourcePath0) - val sourceFile = o2m(sourceFile0) - val pointer = o2mi(pointer0) - val pointerSpace = o2m(pointerSpace0) - override def toString = - (sourcePath0, line0) match { - case (Some(s), Some(l)) => s + ":" + l - case (Some(s), _) => s + ":" - case _ => "" - } - } - - import xsbti.Severity.{ Info, Warn, Error } - private[this] def convert(sev: Severity): xsbti.Severity = - sev match { - case INFO => Info - case WARNING => Warn - case ERROR => Error - } - - import java.lang.{ Integer => I } - private[this] def o2mi(opt: Option[Int]): Maybe[I] = opt match { case None => Maybe.nothing[I]; case Some(s) => Maybe.just[I](s) } - private[this] def o2m[S](opt: Option[S]): Maybe[S] = opt match { case None => Maybe.nothing[S]; case Some(s) => Maybe.just(s) } -} diff --git a/compile/interface/src/main/scala/xsbt/Dependency.scala b/compile/interface/src/main/scala/xsbt/Dependency.scala deleted file mode 100644 index a72f615a6..000000000 --- a/compile/interface/src/main/scala/xsbt/Dependency.scala +++ /dev/null @@ -1,202 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import scala.tools.nsc.{ io, symtab, Phase } -import io.{ AbstractFile, PlainFile, ZipArchive } -import symtab.Flags -import xsbti.DependencyContext -import xsbti.DependencyContext._ - -import java.io.File - -object Dependency { - def name = "xsbt-dependency" -} -/** - * Extracts dependency information from each compilation unit. - * - * This phase uses CompilationUnit.depends and CallbackGlobal.inheritedDependencies - * to collect all symbols that given compilation unit depends on. Those symbols are - * guaranteed to represent Class-like structures. - * - * The CallbackGlobal.inheritedDependencies is populated by the API phase. See, - * ExtractAPI class. - * - * When dependency symbol is processed, it is mapped back to either source file where - * it's defined in (if it's available in current compilation run) or classpath entry - * where it originates from. The Symbol->Classfile mapping is implemented by - * LocateClassFile that we inherit from. - */ -final class Dependency(val global: CallbackGlobal) extends LocateClassFile { - import global._ - - def newPhase(prev: Phase): Phase = new DependencyPhase(prev) - private class DependencyPhase(prev: Phase) extends Phase(prev) { - override def description = "Extracts dependency information" - def name = Dependency.name - def run { - for (unit <- currentRun.units if !unit.isJava) { - // build dependencies structure - val sourceFile = unit.source.file.file - if (global.callback.nameHashing) { - val dependenciesByMemberRef = extractDependenciesByMemberRef(unit) - for (on <- dependenciesByMemberRef) - processDependency(on, context = DependencyByMemberRef) - - val dependenciesByInheritance = extractDependenciesByInheritance(unit) - for (on <- dependenciesByInheritance) - processDependency(on, context = DependencyByInheritance) - } else { - for (on <- unit.depends) processDependency(on, context = DependencyByMemberRef) - for (on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, context = DependencyByInheritance) - } - /** - * Handles dependency on given symbol by trying to figure out if represents a term - * that is coming from either source code (not necessarily compiled in this compilation - * run) or from class file and calls respective callback method. - */ - def processDependency(on: Symbol, context: DependencyContext): Unit = { - def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, context) - val onSource = on.sourceFile - if (onSource == null) { - classFile(on) match { - case Some((f, className, inOutDir)) => - if (inOutDir && on.isJavaDefined) registerTopLevelSym(on) - f match { - case ze: ZipArchive#Entry => for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) binaryDependency(zipFile, className) - case pf: PlainFile => binaryDependency(pf.file, className) - case _ => () - } - case None => () - } - } else if (onSource.file != sourceFile) - callback.sourceDependency(onSource.file, sourceFile, context) - } - } - } - } - - /** - * Traverses given type and collects result of applying a partial function `pf`. - * - * NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier - * versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to - * reimplement that class here. - */ - private final class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser { - var collected: List[T] = Nil - def traverse(tpe: Type): Unit = { - if (pf.isDefinedAt(tpe)) - collected = pf(tpe) :: collected - mapOver(tpe) - } - } - - private abstract class ExtractDependenciesTraverser extends Traverser { - protected val depBuf = collection.mutable.ArrayBuffer.empty[Symbol] - protected def addDependency(dep: Symbol): Unit = depBuf += dep - def dependencies: collection.immutable.Set[Symbol] = { - // convert to immutable set and remove NoSymbol if we have one - depBuf.toSet - NoSymbol - } - } - - private class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser { - - /* - * Some macros appear to contain themselves as original tree. - * We must check that we don't inspect the same tree over and over. - * See https://issues.scala-lang.org/browse/SI-8486 - * https://github.com/sbt/sbt/issues/1237 - * https://github.com/sbt/sbt/issues/1544 - */ - private val inspectedOriginalTrees = collection.mutable.Set.empty[Tree] - - override def traverse(tree: Tree): Unit = { - tree match { - case Import(expr, selectors) => - selectors.foreach { - case ImportSelector(nme.WILDCARD, _, null, _) => - // in case of wildcard import we do not rely on any particular name being defined - // on `expr`; all symbols that are being used will get caught through selections - case ImportSelector(name: Name, _, _, _) => - def lookupImported(name: Name) = expr.symbol.info.member(name) - // importing a name means importing both a term and a type (if they exist) - addDependency(lookupImported(name.toTermName)) - addDependency(lookupImported(name.toTypeName)) - } - case select: Select => - addDependency(select.symbol) - /* - * Idents are used in number of situations: - * - to refer to local variable - * - to refer to a top-level package (other packages are nested selections) - * - to refer to a term defined in the same package as an enclosing class; - * this looks fishy, see this thread: - * https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion - */ - case ident: Ident => - addDependency(ident.symbol) - // In some cases (eg. macro annotations), `typeTree.tpe` may be null. - // See sbt/sbt#1593 and sbt/sbt#1655. - case typeTree: TypeTree if typeTree.tpe != null => - val typeSymbolCollector = new CollectTypeTraverser({ - case tpe if !tpe.typeSymbol.isPackage => tpe.typeSymbol - }) - typeSymbolCollector.traverse(typeTree.tpe) - val deps = typeSymbolCollector.collected.toSet - deps.foreach(addDependency) - case Template(parents, self, body) => - traverseTrees(body) - case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => - this.traverse(original) - case other => () - } - super.traverse(tree) - } - } - - private def extractDependenciesByMemberRef(unit: CompilationUnit): collection.immutable.Set[Symbol] = { - val traverser = new ExtractDependenciesByMemberRefTraverser - traverser.traverse(unit.body) - val dependencies = traverser.dependencies - dependencies.map(enclosingTopLevelClass) - } - - /** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */ - private final def debuglog(msg: => String): Unit = { - if (settings.debug.value) - log(msg) - } - - private final class ExtractDependenciesByInheritanceTraverser extends ExtractDependenciesTraverser { - override def traverse(tree: Tree): Unit = tree match { - case Template(parents, self, body) => - // we are using typeSymbol and not typeSymbolDirect because we want - // type aliases to be expanded - val parentTypeSymbols = parents.map(parent => parent.tpe.typeSymbol).toSet - debuglog("Parent type symbols for " + tree.pos + ": " + parentTypeSymbols.map(_.fullName)) - parentTypeSymbols.foreach(addDependency) - traverseTrees(body) - case tree => super.traverse(tree) - } - } - - private def extractDependenciesByInheritance(unit: CompilationUnit): collection.immutable.Set[Symbol] = { - val traverser = new ExtractDependenciesByInheritanceTraverser - traverser.traverse(unit.body) - val dependencies = traverser.dependencies - dependencies.map(enclosingTopLevelClass) - } - - /** - * We capture enclosing classes only because that's what CompilationUnit.depends does and we don't want - * to deviate from old behaviour too much for now. - */ - private def enclosingTopLevelClass(sym: Symbol): Symbol = - // for Scala 2.8 and 2.9 this method is provided through SymbolCompat - sym.enclosingTopLevelClass - -} diff --git a/compile/interface/src/main/scala/xsbt/ExtractAPI.scala b/compile/interface/src/main/scala/xsbt/ExtractAPI.scala deleted file mode 100644 index 5c2cbf61b..000000000 --- a/compile/interface/src/main/scala/xsbt/ExtractAPI.scala +++ /dev/null @@ -1,533 +0,0 @@ -package xsbt - -import java.io.File -import java.util.{ Arrays, Comparator } -import scala.tools.nsc.{ io, plugins, symtab, Global, Phase } -import io.{ AbstractFile, PlainFile, ZipArchive } -import plugins.{ Plugin, PluginComponent } -import symtab.Flags -import scala.collection.mutable.{ HashMap, HashSet, ListBuffer } -import xsbti.api.{ ClassLike, DefinitionType, PathComponent, SimpleType } - -/** - * Extracts API representation out of Symbols and Types. - * - * Each compilation unit should be processed by a fresh instance of this class. - * - * This class depends on instance of CallbackGlobal instead of regular Global because - * it has a call to `addInheritedDependencies` method defined in CallbackGlobal. In the future - * we should refactor this code so inherited dependencies are just accumulated in a buffer and - * exposed to a client that can pass them to an instance of CallbackGlobal it holds. - */ -class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType, - // Tracks the source file associated with the CompilationUnit currently being processed by the API phase. - // This is used when recording inheritance dependencies. - sourceFile: File) extends Compat { - - import global._ - - private def error(msg: String) = throw new RuntimeException(msg) - - // this cache reduces duplicate work both here and when persisting - // caches on other structures had minimal effect on time and cache size - // (tried: Definition, Modifier, Path, Id, String) - private[this] val typeCache = new HashMap[(Symbol, Type), xsbti.api.Type] - // these caches are necessary for correctness - private[this] val structureCache = new HashMap[Symbol, xsbti.api.Structure] - private[this] val classLikeCache = new HashMap[(Symbol, Symbol), xsbti.api.ClassLike] - private[this] val pending = new HashSet[xsbti.api.Lazy[_]] - - private[this] val emptyStringArray = new Array[String](0) - - /** - * Implements a work-around for https://github.com/sbt/sbt/issues/823 - * - * The strategy is to rename all type variables bound by existential type to stable - * names by assigning to each type variable a De Bruijn-like index. As a result, each - * type variable gets name of this shape: - * - * "existential_${nestingLevel}_${i}" - * - * where `nestingLevel` indicates nesting level of existential types and `i` variable - * indicates position of type variable in given existential type. - * - * For example, let's assume we have the following classes declared: - * - * class A[T]; class B[T,U] - * - * and we have type A[_] that is expanded by Scala compiler into - * - * A[_$1] forSome { type _$1 } - * - * After applying our renaming strategy we get - * - * A[existential_0_0] forSome { type existential_0_0 } - * - * Let's consider a bit more complicated example which shows how our strategy deals with - * nested existential types: - * - * A[_ <: B[_, _]] - * - * which gets expanded into: - * - * A[_$1] forSome { - * type _$1 <: B[_$2, _$3] forSome { type _$2; type _$3 } - * } - * - * After applying our renaming strategy we get - * - * A[existential_0_0] forSome { - * type existential_0_0 <: B[existential_1_0, existential_1_1] forSome { - * type existential_1_0; type existential_1_1 - * } - * } - * - * Note how the first index (nesting level) is bumped for both existential types. - * - * This way, all names of existential type variables depend only on the structure of - * existential types and are kept stable. - * - * Both examples presented above used placeholder syntax for existential types but our - * strategy is applied uniformly to all existential types no matter if they are written - * using placeholder syntax or explicitly. - */ - private[this] object existentialRenamings { - private var nestingLevel: Int = 0 - import scala.collection.mutable.Map - private var renameTo: Map[Symbol, String] = Map.empty - - def leaveExistentialTypeVariables(typeVariables: Seq[Symbol]): Unit = { - nestingLevel -= 1 - assert(nestingLevel >= 0) - typeVariables.foreach(renameTo.remove) - } - def enterExistentialTypeVariables(typeVariables: Seq[Symbol]): Unit = { - nestingLevel += 1 - typeVariables.zipWithIndex foreach { - case (tv, i) => - val newName = "existential_" + nestingLevel + "_" + i - renameTo(tv) = newName - } - } - def renaming(symbol: Symbol): Option[String] = renameTo.get(symbol) - } - - // call back to the xsbti.SafeLazy class in main sbt code to construct a SafeLazy instance - // we pass a thunk, whose class is loaded by the interface class loader (this class's loader) - // SafeLazy ensures that once the value is forced, the thunk is nulled out and so - // references to the thunk's classes are not retained. Specifically, it allows the interface classes - // (those in this subproject) to be garbage collected after compilation. - private[this] val safeLazy = Class.forName("xsbti.SafeLazy").getMethod("apply", classOf[xsbti.F0[_]]) - private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = - { - val z = safeLazy.invoke(null, Message(s)).asInstanceOf[xsbti.api.Lazy[S]] - pending += z - z - } - - /** - * Force all lazy structures. This is necessary so that we see the symbols/types at this phase and - * so that we don't hold on to compiler objects and classes - */ - def forceStructures(): Unit = - if (pending.isEmpty) - structureCache.clear() - else { - val toProcess = pending.toList - pending.clear() - toProcess foreach { _.get() } - forceStructures() - } - - private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil)) - private def path(components: List[PathComponent]) = new xsbti.api.Path(components.toArray[PathComponent]) - private def pathComponents(sym: Symbol, postfix: List[PathComponent]): List[PathComponent] = - { - if (sym == NoSymbol || sym.isRoot || sym.isEmptyPackageClass || sym.isRootPackage) postfix - else pathComponents(sym.owner, new xsbti.api.Id(simpleName(sym)) :: postfix) - } - private def simpleType(in: Symbol, t: Type): SimpleType = - processType(in, t) match { - case s: SimpleType => s - case x => log("Not a simple type:\n\tType: " + t + " (" + t.getClass + ")\n\tTransformed: " + x.getClass); Constants.emptyType - } - private def types(in: Symbol, t: List[Type]): Array[xsbti.api.Type] = t.toArray[Type].map(processType(in, _)) - private def projectionType(in: Symbol, pre: Type, sym: Symbol) = - { - if (pre == NoPrefix) { - if (sym.isLocalClass || sym.isRoot || sym.isRootPackage) Constants.emptyType - else if (sym.isTypeParameterOrSkolem || sym.isExistentiallyBound) reference(sym) - else { - // this appears to come from an existential type in an inherited member- not sure why isExistential is false here - /*println("Warning: Unknown prefixless type: " + sym + " in " + sym.owner + " in " + sym.enclClass) - println("\tFlags: " + sym.flags + ", istype: " + sym.isType + ", absT: " + sym.isAbstractType + ", alias: " + sym.isAliasType + ", nonclass: " + isNonClassType(sym))*/ - reference(sym) - } - } else if (sym.isRoot || sym.isRootPackage) Constants.emptyType - else new xsbti.api.Projection(simpleType(in, pre), simpleName(sym)) - } - private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(tparamID(sym)) - - private def annotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = as.toArray[AnnotationInfo].map(annotation(in, _)) - private def annotation(in: Symbol, a: AnnotationInfo) = - new xsbti.api.Annotation(processType(in, a.atp), - if (a.assocs.isEmpty) Array(new xsbti.api.AnnotationArgument("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree? - else a.assocs.map { case (name, value) => new xsbti.api.AnnotationArgument(name.toString, value.toString) }.toArray[xsbti.api.AnnotationArgument] - ) - private def annotated(in: Symbol, as: List[AnnotationInfo], tpe: Type) = new xsbti.api.Annotated(processType(in, tpe), annotations(in, as)) - - private def viewer(s: Symbol) = (if (s.isModule) s.moduleClass else s).thisType - private def printMember(label: String, in: Symbol, t: Type) = println(label + " in " + in + " : " + t + " (debug: " + debugString(t) + " )") - private def defDef(in: Symbol, s: Symbol) = - { - def build(t: Type, typeParams: Array[xsbti.api.TypeParameter], valueParameters: List[xsbti.api.ParameterList]): xsbti.api.Def = - { - def parameterList(syms: List[Symbol]): xsbti.api.ParameterList = - { - val isImplicitList = syms match { case head :: _ => isImplicit(head); case _ => false } - new xsbti.api.ParameterList(syms.map(parameterS).toArray, isImplicitList) - } - t match { - case PolyType(typeParams0, base) => - assert(typeParams.isEmpty) - assert(valueParameters.isEmpty) - build(base, typeParameters(in, typeParams0), Nil) - case MethodType(params, resultType) => - build(resultType, typeParams, parameterList(params) :: valueParameters) - case Nullary(resultType) => // 2.9 and later - build(resultType, typeParams, valueParameters) - case returnType => - val t2 = processType(in, dropConst(returnType)) - new xsbti.api.Def(valueParameters.reverse.toArray, t2, typeParams, simpleName(s), getAccess(s), getModifiers(s), annotations(in, s)) - } - } - def parameterS(s: Symbol): xsbti.api.MethodParameter = - makeParameter(simpleName(s), s.info, s.info.typeSymbol, s) - - // paramSym is only for 2.8 and is to determine if the parameter has a default - def makeParameter(name: String, tpe: Type, ts: Symbol, paramSym: Symbol): xsbti.api.MethodParameter = - { - import xsbti.api.ParameterModifier._ - val (t, special) = - if (ts == definitions.RepeatedParamClass) // || s == definitions.JavaRepeatedParamClass) - (tpe.typeArgs(0), Repeated) - else if (ts == definitions.ByNameParamClass) - (tpe.typeArgs(0), ByName) - else - (tpe, Plain) - new xsbti.api.MethodParameter(name, processType(in, t), hasDefault(paramSym), special) - } - val t = viewer(in).memberInfo(s) - build(t, Array(), Nil) - } - private def hasDefault(s: Symbol) = s != NoSymbol && s.hasFlag(Flags.DEFAULTPARAM) - private def fieldDef[T](in: Symbol, s: Symbol, keepConst: Boolean, create: (xsbti.api.Type, String, xsbti.api.Access, xsbti.api.Modifiers, Array[xsbti.api.Annotation]) => T): T = - { - val t = dropNullary(viewer(in).memberType(s)) - val t2 = if (keepConst) t else dropConst(t) - create(processType(in, t2), simpleName(s), getAccess(s), getModifiers(s), annotations(in, s)) - } - private def dropConst(t: Type): Type = t match { - case ConstantType(constant) => constant.tpe - case _ => t - } - private def dropNullary(t: Type): Type = t match { - case Nullary(un) => un - case _ => t - } - - private def typeDef(in: Symbol, s: Symbol): xsbti.api.TypeMember = - { - val (typeParams, tpe) = - viewer(in).memberInfo(s) match { - case PolyType(typeParams0, base) => (typeParameters(in, typeParams0), base) - case t => (Array[xsbti.api.TypeParameter](), t) - } - val name = simpleName(s) - val access = getAccess(s) - val modifiers = getModifiers(s) - val as = annotations(in, s) - - if (s.isAliasType) - new xsbti.api.TypeAlias(processType(in, tpe), typeParams, name, access, modifiers, as) - else if (s.isAbstractType) { - val bounds = tpe.bounds - new xsbti.api.TypeDeclaration(processType(in, bounds.lo), processType(in, bounds.hi), typeParams, name, access, modifiers, as) - } else - error("Unknown type member" + s) - } - - private def structure(in: Symbol, s: Symbol): xsbti.api.Structure = structure(viewer(in).memberInfo(s), s, true) - private def structure(info: Type): xsbti.api.Structure = structure(info, info.typeSymbol, false) - private def structure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure = - structureCache.getOrElseUpdate(s, mkStructure(info, s, inherit)) - - private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor } - - private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure = - { - val (declared, inherited) = info.members.toList.reverse.partition(_.owner == s) - val baseTypes = info.baseClasses.tail.map(info.baseType) - val ds = if (s.isModuleClass) removeConstructors(declared) else declared - val is = if (inherit) removeConstructors(inherited) else Nil - mkStructure(s, baseTypes, ds, is) - } - - // If true, this template is publicly visible and should be processed as a public inheritance dependency. - // Local classes and local refinements will never be traversed by the api phase, so we don't need to check for that. - private[this] def isPublicStructure(s: Symbol): Boolean = - s.isStructuralRefinement || - // do not consider templates that are private[this] or private - !(s.isPrivate && (s.privateWithin == NoSymbol || s.isLocal)) - - private def mkStructure(s: Symbol, bases: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure = { - if (isPublicStructure(s)) - addInheritedDependencies(sourceFile, bases.map(_.dealias.typeSymbol)) - new xsbti.api.Structure(lzy(types(s, bases)), lzy(processDefinitions(s, declared)), lzy(processDefinitions(s, inherited))) - } - private def processDefinitions(in: Symbol, defs: List[Symbol]): Array[xsbti.api.Definition] = - sort(defs.toArray).flatMap((d: Symbol) => definition(in, d)) - private[this] def sort(defs: Array[Symbol]): Array[Symbol] = { - Arrays.sort(defs, sortClasses) - defs - } - - private def definition(in: Symbol, sym: Symbol): Option[xsbti.api.Definition] = - { - def mkVar = Some(fieldDef(in, sym, false, new xsbti.api.Var(_, _, _, _, _))) - def mkVal = Some(fieldDef(in, sym, true, new xsbti.api.Val(_, _, _, _, _))) - if (isClass(sym)) - if (ignoreClass(sym)) None else Some(classLike(in, sym)) - else if (sym.isNonClassType) - Some(typeDef(in, sym)) - else if (sym.isVariable) - if (isSourceField(sym)) mkVar else None - else if (sym.isStable) - if (isSourceField(sym)) mkVal else None - else if (sym.isSourceMethod && !sym.isSetter) - if (sym.isGetter) mkVar else Some(defDef(in, sym)) - else - None - } - private def ignoreClass(sym: Symbol): Boolean = - sym.isLocalClass || sym.isAnonymousClass || sym.fullName.endsWith(LocalChild.toString) - - // This filters private[this] vals/vars that were not in the original source. - // The getter will be used for processing instead. - private def isSourceField(sym: Symbol): Boolean = - { - val getter = sym.getter(sym.enclClass) - // the check `getter eq sym` is a precaution against infinite recursion - // `isParamAccessor` does not exist in all supported versions of Scala, so the flag check is done directly - (getter == NoSymbol && !sym.hasFlag(Flags.PARAMACCESSOR)) || (getter eq sym) - } - private def getModifiers(s: Symbol): xsbti.api.Modifiers = - { - import Flags._ - val absOver = s.hasFlag(ABSOVERRIDE) - val abs = s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED) || absOver - val over = s.hasFlag(OVERRIDE) || absOver - new xsbti.api.Modifiers(abs, over, s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), hasMacro(s)) - } - - private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT) - private def getAccess(c: Symbol): xsbti.api.Access = - { - if (c.isPublic) Constants.public - else if (c.isPrivateLocal) Constants.privateLocal - else if (c.isProtectedLocal) Constants.protectedLocal - else { - val within = c.privateWithin - val qualifier = if (within == NoSymbol) Constants.unqualified else new xsbti.api.IdQualifier(within.fullName) - if (c.hasFlag(Flags.PROTECTED)) new xsbti.api.Protected(qualifier) - else new xsbti.api.Private(qualifier) - } - } - - /** - * Replace all types that directly refer to the `forbidden` symbol by `NoType`. - * (a specialized version of substThisAndSym) - */ - class SuppressSymbolRef(forbidden: Symbol) extends TypeMap { - def apply(tp: Type) = - if (tp.typeSymbolDirect == forbidden) NoType - else mapOver(tp) - } - - private def processType(in: Symbol, t: Type): xsbti.api.Type = typeCache.getOrElseUpdate((in, t), makeType(in, t)) - private def makeType(in: Symbol, t: Type): xsbti.api.Type = - { - - val dealiased = t match { - case TypeRef(_, sym, _) if sym.isAliasType => t.dealias - case _ => t - } - - dealiased match { - case NoPrefix => Constants.emptyType - case ThisType(sym) => new xsbti.api.Singleton(thisPath(sym)) - case SingleType(pre, sym) => projectionType(in, pre, sym) - case ConstantType(constant) => new xsbti.api.Constant(processType(in, constant.tpe), constant.stringValue) - - /* explaining the special-casing of references to refinement classes (https://support.typesafe.com/tickets/1882) - * - * goal: a representation of type references to refinement classes that's stable across compilation runs - * (and thus insensitive to typing from source or unpickling from bytecode) - * - * problem: the current representation, which corresponds to the owner chain of the refinement: - * 1. is affected by pickling, so typing from source or using unpickled symbols give different results (because the unpickler "localizes" owners -- this could be fixed in the compiler) - * 2. can't distinguish multiple refinements in the same owner (this is a limitation of SBT's internal representation and cannot be fixed in the compiler) - * - * potential solutions: - * - simply drop the reference: won't work as collapsing all refinement types will cause recompilation to be skipped when a refinement is changed to another refinement - * - represent the symbol in the api: can't think of a stable way of referring to an anonymous symbol whose owner changes when pickled - * + expand the reference to the corresponding refinement type: doing that recursively may not terminate, but we can deal with that by approximating recursive references - * (all we care about is being sound for recompilation: recompile iff a dependency changes, and this will happen as long as we have one unrolling of the reference to the refinement) - */ - case TypeRef(pre, sym, Nil) if sym.isRefinementClass => - // Since we only care about detecting changes reliably, we unroll a reference to a refinement class once. - // Recursive references are simply replaced by NoType -- changes to the type will be seen in the first unrolling. - // The API need not be type correct, so this truncation is acceptable. Most of all, the API should be compact. - val unrolling = pre.memberInfo(sym) // this is a refinement type - - // in case there are recursive references, suppress them -- does this ever happen? - // we don't have a test case for this, so warn and hope we'll get a contribution for it :-) - val withoutRecursiveRefs = new SuppressSymbolRef(sym).mapOver(unrolling) - if (unrolling ne withoutRecursiveRefs) - reporter.warning(sym.pos, "sbt-api: approximated refinement ref" + t + " (== " + unrolling + ") to " + withoutRecursiveRefs + "\nThis is currently untested, please report the code you were compiling.") - - structure(withoutRecursiveRefs) - case tr @ TypeRef(pre, sym, args) => - val base = projectionType(in, pre, sym) - if (args.isEmpty) - if (isRawType(tr)) - processType(in, rawToExistential(tr)) - else - base - else - new xsbti.api.Parameterized(base, types(in, args)) - case SuperType(thistpe: Type, supertpe: Type) => - warning("sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe); Constants.emptyType - case at: AnnotatedType => annotatedType(in, at) - case rt: CompoundType => structure(rt) - case t: ExistentialType => makeExistentialType(in, t) - case NoType => Constants.emptyType // this can happen when there is an error that will be reported by a later phase - case PolyType(typeParams, resultType) => new xsbti.api.Polymorphic(processType(in, resultType), typeParameters(in, typeParams)) - case Nullary(resultType) => - warning("sbt-api: Unexpected nullary method type " + in + " in " + in.owner); Constants.emptyType - case _ => warning("sbt-api: Unhandled type " + t.getClass + " : " + t); Constants.emptyType - } - } - private def makeExistentialType(in: Symbol, t: ExistentialType): xsbti.api.Existential = { - val ExistentialType(typeVariables, qualified) = t - existentialRenamings.enterExistentialTypeVariables(typeVariables) - try { - val typeVariablesConverted = typeParameters(in, typeVariables) - val qualifiedConverted = processType(in, qualified) - new xsbti.api.Existential(qualifiedConverted, typeVariablesConverted) - } finally { - existentialRenamings.leaveExistentialTypeVariables(typeVariables) - } - } - private def typeParameters(in: Symbol, s: Symbol): Array[xsbti.api.TypeParameter] = typeParameters(in, s.typeParams) - private def typeParameters(in: Symbol, s: List[Symbol]): Array[xsbti.api.TypeParameter] = s.map(typeParameter(in, _)).toArray[xsbti.api.TypeParameter] - private def typeParameter(in: Symbol, s: Symbol): xsbti.api.TypeParameter = - { - val varianceInt = s.variance - import xsbti.api.Variance._ - val annots = annotations(in, s) - val variance = if (varianceInt < 0) Contravariant else if (varianceInt > 0) Covariant else Invariant - viewer(in).memberInfo(s) match { - case TypeBounds(low, high) => new xsbti.api.TypeParameter(tparamID(s), annots, typeParameters(in, s), variance, processType(in, low), processType(in, high)) - case PolyType(typeParams, base) => new xsbti.api.TypeParameter(tparamID(s), annots, typeParameters(in, typeParams), variance, processType(in, base.bounds.lo), processType(in, base.bounds.hi)) - case x => error("Unknown type parameter info: " + x.getClass) - } - } - private def tparamID(s: Symbol): String = { - val renameTo = existentialRenamings.renaming(s) - renameTo match { - case Some(rename) => - // can't use debuglog because it doesn't exist in Scala 2.9.x - if (settings.debug.value) - log("Renaming existential type variable " + s.fullName + " to " + rename) - rename - case None => - s.fullName - } - } - private def selfType(in: Symbol, s: Symbol): xsbti.api.Type = processType(in, s.thisSym.typeOfThis) - - def classLike(in: Symbol, c: Symbol): ClassLike = classLikeCache.getOrElseUpdate((in, c), mkClassLike(in, c)) - private def mkClassLike(in: Symbol, c: Symbol): ClassLike = - { - val name = c.fullName - val isModule = c.isModuleClass || c.isModule - val struct = if (isModule) c.moduleClass else c - val defType = - if (c.isTrait) DefinitionType.Trait - else if (isModule) { - if (c.isPackage) DefinitionType.PackageModule - else DefinitionType.Module - } else DefinitionType.ClassDef - new xsbti.api.ClassLike(defType, lzy(selfType(in, c)), lzy(structure(in, struct)), emptyStringArray, typeParameters(in, c), name, getAccess(c), getModifiers(c), annotations(in, c)) - } - - private[this] def isClass(s: Symbol) = s.isClass || s.isModule - // necessary to ensure a stable ordering of classes in the definitions list: - // modules and classes come first and are sorted by name - // all other definitions come later and are not sorted - private[this] val sortClasses = new Comparator[Symbol] { - def compare(a: Symbol, b: Symbol) = { - val aIsClass = isClass(a) - val bIsClass = isClass(b) - if (aIsClass == bIsClass) - if (aIsClass) - if (a.isModule == b.isModule) - a.fullName.compareTo(b.fullName) - else if (a.isModule) - -1 - else - 1 - else - 0 // substantial performance hit if fullNames are compared here - else if (aIsClass) - -1 - else - 1 - } - } - private object Constants { - val local = new xsbti.api.ThisQualifier - val public = new xsbti.api.Public - val privateLocal = new xsbti.api.Private(local) - val protectedLocal = new xsbti.api.Protected(local) - val unqualified = new xsbti.api.Unqualified - val emptyPath = new xsbti.api.Path(Array()) - val thisPath = new xsbti.api.This - val emptyType = new xsbti.api.EmptyType - } - - private def simpleName(s: Symbol): String = - { - val n = s.originalName - val n2 = if (n.toString == "") n else n.decode - n2.toString.trim - } - - private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] = - atPhase(currentRun.typerPhase) { - val base = if (s.hasFlag(Flags.ACCESSOR)) s.accessed else NoSymbol - val b = if (base == NoSymbol) s else base - // annotations from bean methods are not handled because: - // a) they are recorded as normal source methods anyway - // b) there is no way to distinguish them from user-defined methods - val associated = List(b, b.getter(b.enclClass), b.setter(b.enclClass)).filter(_ != NoSymbol) - associated.flatMap(ss => annotations(in, ss.annotations)).distinct.toArray; - } - private def annotatedType(in: Symbol, at: AnnotatedType): xsbti.api.Type = - { - val annots = at.annotations - if (annots.isEmpty) processType(in, at.underlying) else annotated(in, annots, at.underlying) - } - -} \ No newline at end of file diff --git a/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala b/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala deleted file mode 100644 index 56f67f3e8..000000000 --- a/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala +++ /dev/null @@ -1,128 +0,0 @@ -package xsbt - -import scala.tools.nsc._ - -/** - * Extracts simple names used in given compilation unit. - * - * Extracts simple (unqualified) names mentioned in given in non-definition position by collecting - * all symbols associated with non-definition trees and extracting names from all collected symbols. - * - * If given symbol is mentioned both in definition and in non-definition position (e.g. in member - * selection) then that symbol is collected. It means that names of symbols defined and used in the - * same compilation unit are extracted. We've considered not extracting names of those symbols - * as an optimization strategy. It turned out that this is not correct. Check - * https://github.com/gkossakowski/sbt/issues/3 for an example of scenario where it matters. - * - * All extracted names are returned in _decoded_ form. This way we stay consistent with the rest - * of incremental compiler which works with names in decoded form. - * - * Names mentioned in Import nodes are handled properly but require some special logic for two - * reasons: - * - * 1. import node itself has a term symbol associated with it with a name `. - * I (gkossakowski) tried to track down what role this symbol serves but I couldn't. - * It doesn't look like there are many places in Scala compiler that refer to - * that kind of symbols explicitly. - * 2. ImportSelector is not subtype of Tree therefore is not processed by `Tree.foreach` - * - * Another type of tree nodes that requires special handling is TypeTree. TypeTree nodes - * has a little bit odd representation: - * - * 1. TypeTree.hasSymbol always returns false even when TypeTree.symbol - * returns a symbol - * 2. The original tree from which given TypeTree was derived is stored - * in TypeTree.original but Tree.forech doesn't walk into original - * tree so we missed it - * - * The tree walking algorithm walks into TypeTree.original explicitly. - * - */ -class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat { - import global._ - - def extract(unit: CompilationUnit): Set[String] = { - val tree = unit.body - val extractedByTreeWalk = extractByTreeWalk(tree) - extractedByTreeWalk - } - - private def extractByTreeWalk(tree: Tree): Set[String] = { - val namesBuffer = collection.mutable.ListBuffer.empty[String] - - /* - * Some macros appear to contain themselves as original tree. - * We must check that we don't inspect the same tree over and over. - * See https://issues.scala-lang.org/browse/SI-8486 - * https://github.com/sbt/sbt/issues/1237 - * https://github.com/sbt/sbt/issues/1544 - */ - val inspectedOriginalTrees = collection.mutable.Set.empty[Tree] - - def addSymbol(symbol: Symbol): Unit = { - val symbolNameAsString = symbol.name.decode.trim - namesBuffer += symbolNameAsString - } - - def handleTreeNode(node: Tree): Unit = { - def handleMacroExpansion(original: Tree): Unit = { - original.foreach(handleTreeNode) - } - - def handleClassicTreeNode(node: Tree): Unit = node match { - case _: DefTree | _: Template => () - // turns out that Import node has a TermSymbol associated with it - // I (Grzegorz) tried to understand why it's there and what does it represent but - // that logic was introduced in 2005 without any justification I'll just ignore the - // import node altogether and just process the selectors in the import node - case Import(_, selectors: List[ImportSelector]) => - def usedNameInImportSelector(name: Name): Unit = - if ((name != null) && (name != nme.WILDCARD)) namesBuffer += name.toString - selectors foreach { selector => - usedNameInImportSelector(selector.name) - usedNameInImportSelector(selector.rename) - } - // TODO: figure out whether we should process the original tree or walk the type - // the argument for processing the original tree: we process what user wrote - // the argument for processing the type: we catch all transformations that typer applies - // to types but that might be a bad thing because it might expand aliases eagerly which - // not what we need - case t: TypeTree if t.original != null => - t.original.foreach(handleTreeNode) - case t if t.hasSymbol && eligibleAsUsedName(t.symbol) => - addSymbol(t.symbol) - case _ => () - } - - node match { - case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => - handleClassicTreeNode(node) - handleMacroExpansion(original) - case _ => - handleClassicTreeNode(node) - } - } - - tree.foreach(handleTreeNode) - namesBuffer.toSet - } - - /** - * Needed for compatibility with Scala 2.8 which doesn't define `tpnme` - */ - private object tpnme { - val EMPTY = nme.EMPTY.toTypeName - val EMPTY_PACKAGE_NAME = nme.EMPTY_PACKAGE_NAME.toTypeName - } - - private def eligibleAsUsedName(symbol: Symbol): Boolean = { - def emptyName(name: Name): Boolean = name match { - case nme.EMPTY | nme.EMPTY_PACKAGE_NAME | tpnme.EMPTY | tpnme.EMPTY_PACKAGE_NAME => true - case _ => false - } - - (symbol != NoSymbol) && - !symbol.isSynthetic && - !emptyName(symbol.name) - } -} diff --git a/compile/interface/src/main/scala/xsbt/LocateClassFile.scala b/compile/interface/src/main/scala/xsbt/LocateClassFile.scala deleted file mode 100644 index c2faf24fb..000000000 --- a/compile/interface/src/main/scala/xsbt/LocateClassFile.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import scala.tools.nsc.symtab.Flags -import scala.tools.nsc.io.AbstractFile - -import java.io.File - -/** - * Contains utility methods for looking up class files corresponding to Symbols. - */ -abstract class LocateClassFile extends Compat { - val global: CallbackGlobal - import global._ - - private[this] final val classSeparator = '.' - protected def classFile(sym: Symbol): Option[(AbstractFile, String, Boolean)] = - // package can never have a corresponding class file; this test does not - // catch package objects (that do not have this flag set) - if (sym hasFlag scala.tools.nsc.symtab.Flags.PACKAGE) None else { - import scala.tools.nsc.symtab.Flags - val name = flatname(sym, classSeparator) + moduleSuffix(sym) - findClass(name).map { case (file, inOut) => (file, name, inOut) } orElse { - if (isTopLevelModule(sym)) { - val linked = sym.companionClass - if (linked == NoSymbol) - None - else - classFile(linked) - } else - None - } - } - private def flatname(s: Symbol, separator: Char) = - atPhase(currentRun.flattenPhase.next) { s fullName separator } - - protected def isTopLevelModule(sym: Symbol): Boolean = - atPhase(currentRun.picklerPhase.next) { - sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass - } - protected def className(s: Symbol, sep: Char, dollarRequired: Boolean): String = - flatname(s, sep) + (if (dollarRequired) "$" else "") - protected def fileForClass(outputDirectory: File, s: Symbol, separatorRequired: Boolean): File = - new File(outputDirectory, className(s, File.separatorChar, separatorRequired) + ".class") -} diff --git a/compile/interface/src/main/scala/xsbt/Log.scala b/compile/interface/src/main/scala/xsbt/Log.scala deleted file mode 100644 index 8b31bb9b2..000000000 --- a/compile/interface/src/main/scala/xsbt/Log.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -object Log { - def debug(log: xsbti.Logger, msg: => String) = log.debug(Message(msg)) - def settingsError(log: xsbti.Logger): String => Unit = - s => log.error(Message(s)) -} \ No newline at end of file diff --git a/compile/interface/src/main/scala/xsbt/Message.scala b/compile/interface/src/main/scala/xsbt/Message.scala deleted file mode 100644 index 9ce888d58..000000000 --- a/compile/interface/src/main/scala/xsbt/Message.scala +++ /dev/null @@ -1,8 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -object Message { - def apply[T](s: => T) = new xsbti.F0[T] { def apply() = s } -} \ No newline at end of file diff --git a/compile/interface/src/main/scala/xsbt/ScaladocInterface.scala b/compile/interface/src/main/scala/xsbt/ScaladocInterface.scala deleted file mode 100644 index 093fef986..000000000 --- a/compile/interface/src/main/scala/xsbt/ScaladocInterface.scala +++ /dev/null @@ -1,68 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import xsbti.Logger -import Log.debug - -class ScaladocInterface { - def run(args: Array[String], log: Logger, delegate: xsbti.Reporter) = (new Runner(args, log, delegate)).run -} -private class Runner(args: Array[String], log: Logger, delegate: xsbti.Reporter) { - import scala.tools.nsc.{ doc, Global, reporters } - import reporters.Reporter - val docSettings: doc.Settings = new doc.Settings(Log.settingsError(log)) - val command = Command(args.toList, docSettings) - val reporter = DelegatingReporter(docSettings, delegate) - def noErrors = !reporter.hasErrors && command.ok - - import forScope._ - def run(): Unit = { - debug(log, "Calling Scaladoc with arguments:\n\t" + args.mkString("\n\t")) - if (noErrors) { - import doc._ // 2.8 trunk and Beta1-RC4 have doc.DocFactory. For other Scala versions, the next line creates forScope.DocFactory - val processor = new DocFactory(reporter, docSettings) - processor.document(command.files) - } - reporter.printSummary() - if (!noErrors) throw new InterfaceCompileFailed(args, reporter.problems, "Scaladoc generation failed") - } - - object forScope { - class DocFactory(reporter: Reporter, docSettings: doc.Settings) // 2.7 compatibility - { - // see https://github.com/paulp/scala-full/commit/649823703a574641407d75d5c073be325ea31307 - trait GlobalCompat { - def onlyPresentation = false - - def forScaladoc = false - } - - object compiler extends Global(command.settings, reporter) with GlobalCompat { - override def onlyPresentation = true - override def forScaladoc = true - class DefaultDocDriver // 2.8 source compatibility - { - assert(false) - def process(units: Iterator[CompilationUnit]) = error("for 2.8 compatibility only") - } - } - def document(ignore: Seq[String]): Unit = { - import compiler._ - val run = new Run - run compile command.files - - val generator = - { - import doc._ - new DefaultDocDriver { - lazy val global: compiler.type = compiler - lazy val settings = docSettings - } - } - generator.process(run.units) - } - } - } -} diff --git a/compile/interface/src/test/scala/xsbt/DependencySpecification.scala b/compile/interface/src/test/scala/xsbt/DependencySpecification.scala deleted file mode 100644 index 192d0e000..000000000 --- a/compile/interface/src/test/scala/xsbt/DependencySpecification.scala +++ /dev/null @@ -1,146 +0,0 @@ -package xsbt - -import org.junit.runner.RunWith -import xsbti.api.ClassLike -import xsbti.api.Def -import xsbt.api.SameAPI -import org.specs2.mutable.Specification -import org.specs2.runner.JUnitRunner - -import ScalaCompilerForUnitTesting.ExtractedSourceDependencies - -@RunWith(classOf[JUnitRunner]) -class DependencySpecification extends Specification { - - "Extracted source dependencies from public members" in { - val sourceDependencies = extractSourceDependenciesPublic - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set('A, 'D) - inheritance('B) === Set('D) - memberRef('C) === Set('A) - inheritance('C) === Set.empty - memberRef('D) === Set.empty - inheritance('D) === Set.empty - memberRef('E) === Set.empty - inheritance('E) === Set.empty - memberRef('F) === Set('A, 'B, 'C, 'D, 'E) - inheritance('F) === Set('A, 'E) - memberRef('H) === Set('B, 'E, 'G) - // aliases and applied type constructors are expanded so we have inheritance dependency on B - inheritance('H) === Set('B, 'E) - } - - "Extracted source dependencies from private members" in { - val sourceDependencies = extractSourceDependenciesPrivate - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set.empty - inheritance('B) === Set.empty - memberRef('C) === Set('A) - inheritance('C) === Set('A) - memberRef('D) === Set('B) - inheritance('D) === Set('B) - } - - "Extracted source dependencies with trait as first parent" in { - val sourceDependencies = extractSourceDependenciesTraitAsFirstPatent - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set('A) - inheritance('B) === Set('A) - // verify that memberRef captures the oddity described in documentation of `Relations.inheritance` - // we are mainly interested whether dependency on A is captured in `memberRef` relation so - // the invariant that says that memberRef is superset of inheritance relation is preserved - memberRef('C) === Set('A, 'B) - inheritance('C) === Set('A, 'B) - // same as above but indirect (C -> B -> A), note that only A is visible here - memberRef('D) === Set('A, 'C) - inheritance('D) === Set('A, 'C) - } - - "Extracted source dependencies from macro arguments" in { - val sourceDependencies = extractSourceDependenciesFromMacroArgument - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - - memberRef('A) === Set('B, 'C) - inheritance('A) === Set.empty - memberRef('B) === Set.empty - inheritance('B) === Set.empty - memberRef('C) === Set.empty - inheritance('C) === Set.empty - } - - private def extractSourceDependenciesPublic: ExtractedSourceDependencies = { - val srcA = "class A" - val srcB = "class B extends D[A]" - val srcC = """|class C { - | def a: A = null - |}""".stripMargin - val srcD = "class D[T]" - val srcE = "trait E[T]" - val srcF = "trait F extends A with E[D[B]] { self: C => }" - val srcG = "object G { type T[x] = B }" - // T is a type constructor [x]B - // B extends D - // E verifies the core type gets pulled out - val srcH = "trait H extends G.T[Int] with (E[Int] @unchecked)" - - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, - 'D -> srcD, 'E -> srcE, 'F -> srcF, 'G -> srcG, 'H -> srcH) - sourceDependencies - } - - private def extractSourceDependenciesPrivate: ExtractedSourceDependencies = { - val srcA = "class A" - val srcB = "class B" - val srcC = "class C { private class Inner1 extends A }" - val srcD = "class D { def foo: Unit = { class Inner2 extends B } }" - - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD) - sourceDependencies - } - - private def extractSourceDependenciesTraitAsFirstPatent: ExtractedSourceDependencies = { - val srcA = "class A" - val srcB = "trait B extends A" - val srcC = "trait C extends B" - val srcD = "class D extends C" - - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD) - sourceDependencies - } - - private def extractSourceDependenciesFromMacroArgument: ExtractedSourceDependencies = { - val srcA = "class A { println(B.printTree(C.foo)) }" - val srcB = """ - |import scala.language.experimental.macros - |import scala.reflect.macros._ - |object B { - | def printTree(arg: Any) = macro printTreeImpl - | def printTreeImpl(c: Context)(arg: c.Expr[Any]): c.Expr[String] = { - | val argStr = arg.tree.toString - | val literalStr = c.universe.Literal(c.universe.Constant(argStr)) - | c.Expr[String](literalStr) - | } - |}""".stripMargin - val srcC = "object C { val foo = 1 }" - - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs(List(Map('B -> srcB, 'C -> srcC), Map('A -> srcA))) - sourceDependencies - } -} diff --git a/compile/interface/src/test/scala/xsbt/ExtractAPISpecification.scala b/compile/interface/src/test/scala/xsbt/ExtractAPISpecification.scala deleted file mode 100644 index ab158ee6e..000000000 --- a/compile/interface/src/test/scala/xsbt/ExtractAPISpecification.scala +++ /dev/null @@ -1,42 +0,0 @@ -package xsbt - -import org.junit.runner.RunWith -import xsbti.api.ClassLike -import xsbti.api.Def -import xsbt.api.SameAPI -import org.specs2.mutable.Specification -import org.specs2.runner.JUnitRunner - -@RunWith(classOf[JUnitRunner]) -class ExtractAPISpecification extends Specification { - - "Existential types in method signatures" should { - "have stable names" in { stableExistentialNames } - } - - def stableExistentialNames: Boolean = { - def compileAndGetFooMethodApi(src: String): Def = { - val compilerForTesting = new ScalaCompilerForUnitTesting - val sourceApi = compilerForTesting.extractApiFromSrc(src) - val FooApi = sourceApi.definitions().find(_.name() == "Foo").get.asInstanceOf[ClassLike] - val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get - fooMethodApi.asInstanceOf[Def] - } - val src1 = """ - |class Box[T] - |class Foo { - | def foo: Box[_] = null - | - }""".stripMargin - val fooMethodApi1 = compileAndGetFooMethodApi(src1) - val src2 = """ - |class Box[T] - |class Foo { - | def bar: Box[_] = null - | def foo: Box[_] = null - | - }""".stripMargin - val fooMethodApi2 = compileAndGetFooMethodApi(src2) - SameAPI.apply(fooMethodApi1, fooMethodApi2) - } -} diff --git a/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala b/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala deleted file mode 100644 index e9dcbf49e..000000000 --- a/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala +++ /dev/null @@ -1,108 +0,0 @@ -package xsbt - -import org.junit.runner.RunWith -import xsbti.api.ClassLike -import xsbti.api.Def -import xsbti.api.Package -import xsbt.api.SameAPI -import org.junit.runners.JUnit4 - -import org.specs2.mutable.Specification - -@RunWith(classOf[JUnit4]) -class ExtractUsedNamesSpecification extends Specification { - - /** - * Standard names that appear in every compilation unit that has any class - * definition. - */ - private val standardNames = Set( - // AnyRef is added as default parent of a class - "scala", "AnyRef", - // class receives a default constructor which is internally called "" - "") - - "imported name" in { - val src = """ - |package a { class A } - |package b { - | import a.{A => A2} - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) - val expectedNames = standardNames ++ Set("a", "A", "A2", "b") - usedNames === expectedNames - } - - // test covers https://github.com/gkossakowski/sbt/issues/6 - "names in type tree" in { - val srcA = """| - |package a { - | class A { - | class C { class D } - | } - | class B[T] - | class BB - |}""".stripMargin - val srcB = """| - |package b { - | abstract class X { - | def foo: a.A#C#D - | def bar: a.B[a.BB] - | } - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - val expectedNames = standardNames ++ Set("a", "A", "B", "C", "D", "b", "X", "BB") - usedNames === expectedNames - } - - // test for https://github.com/gkossakowski/sbt/issues/5 - "symbolic names" in { - val srcA = """| - |class A { - | def `=`: Int = 3 - |}""".stripMargin - val srcB = """| - |class B { - | def foo(a: A) = a.`=` - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - val expectedNames = standardNames ++ Set("A", "a", "B", "=") - usedNames === expectedNames - } - - // test for https://github.com/gkossakowski/sbt/issues/3 - "used names from the same compilation unit" in { - val src = "class A { def foo: Int = 0; def bar: Int = foo }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) - val expectedNames = standardNames ++ Set("A", "foo", "Int") - usedNames === expectedNames - } - - // pending test for https://issues.scala-lang.org/browse/SI-7173 - "names of constants" in { - val src = "class A { final val foo = 12; def bar: Int = foo }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) - val expectedNames = standardNames ++ Set("A", "foo", "Int") - usedNames === expectedNames - }.pendingUntilFixed("Scala's type checker inlines constants so we can't see the original name.") - - // pending test for https://github.com/gkossakowski/sbt/issues/4 - // TODO: we should fix it by having special treatment of `selectDynamic` and `applyDynamic` calls - "names from method calls on Dynamic" in { - val srcA = """|import scala.language.dynamics - |class A extends Dynamic { - | def selectDynamic(name: String): Int = name.length - |}""".stripMargin - val srcB = "class B { def foo(a: A): Int = a.bla }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - val expectedNames = standardNames ++ Set("B", "A", "a", "Int", "selectDynamic", "bla") - usedNames === expectedNames - }.pendingUntilFixed("Call to Dynamic is desugared in type checker so Select nodes is turned into string literal.") - -} diff --git a/compile/interface/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala b/compile/interface/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala deleted file mode 100644 index 019590dfc..000000000 --- a/compile/interface/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala +++ /dev/null @@ -1,182 +0,0 @@ -package xsbt - -import xsbti.compile.SingleOutput -import java.io.File -import _root_.scala.tools.nsc.reporters.ConsoleReporter -import _root_.scala.tools.nsc.Settings -import xsbti._ -import xsbti.api.SourceAPI -import sbt.IO.withTemporaryDirectory -import xsbti.api.ClassLike -import xsbti.api.Definition -import xsbti.api.Def -import xsbt.api.SameAPI -import sbt.ConsoleLogger -import xsbti.DependencyContext._ - -import ScalaCompilerForUnitTesting.ExtractedSourceDependencies - -/** - * Provides common functionality needed for unit tests that require compiling - * source code using Scala compiler. - */ -class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) { - - /** - * Compiles given source code using Scala compiler and returns API representation - * extracted by ExtractAPI class. - */ - def extractApiFromSrc(src: String): SourceAPI = { - val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) - analysisCallback.apis(tempSrcFile) - } - - def extractUsedNamesFromSrc(src: String): Set[String] = { - val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) - analysisCallback.usedNames(tempSrcFile) - } - - /** - * Extract used names from src provided as the second argument. - * - * The purpose of the first argument is to define names that the second - * source is going to refer to. Both files are compiled in the same compiler - * Run but only names used in the second src file are returned. - */ - def extractUsedNamesFromSrc(definitionSrc: String, actualSrc: String): Set[String] = { - // we drop temp src file corresponding to the definition src file - val (Seq(_, tempSrcFile), analysisCallback) = compileSrcs(definitionSrc, actualSrc) - analysisCallback.usedNames(tempSrcFile) - } - - /** - * Compiles given source code snippets (passed as Strings) using Scala compiler and returns extracted - * dependencies between snippets. Source code snippets are identified by symbols. Each symbol should - * be associated with one snippet only. - * - * Snippets can be grouped to be compiled together in the same compiler run. This is - * useful to compile macros, which cannot be used in the same compilation run that - * defines them. - * - * Symbols are used to express extracted dependencies between source code snippets. This way we have - * file system-independent way of testing dependencies between source code "files". - */ - def extractDependenciesFromSrcs(srcs: List[Map[Symbol, String]]): ExtractedSourceDependencies = { - val rawGroupedSrcs = srcs.map(_.values.toList) - val symbols = srcs.flatMap(_.keys) - val (tempSrcFiles, testCallback) = compileSrcs(rawGroupedSrcs) - val fileToSymbol = (tempSrcFiles zip symbols).toMap - - val memberRefFileDeps = testCallback.sourceDependencies collect { - // false indicates that those dependencies are not introduced by inheritance - case (target, src, DependencyByMemberRef) => (src, target) - } - val inheritanceFileDeps = testCallback.sourceDependencies collect { - // true indicates that those dependencies are introduced by inheritance - case (target, src, DependencyByInheritance) => (src, target) - } - def toSymbols(src: File, target: File): (Symbol, Symbol) = (fileToSymbol(src), fileToSymbol(target)) - val memberRefDeps = memberRefFileDeps map { case (src, target) => toSymbols(src, target) } - val inheritanceDeps = inheritanceFileDeps map { case (src, target) => toSymbols(src, target) } - def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { - import scala.collection.mutable.{ HashMap, MultiMap } - val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] - val multiMap = pairs.foldLeft(emptyMultiMap) { - case (acc, (key, value)) => - acc.addBinding(key, value) - } - // convert all collections to immutable variants - multiMap.toMap.mapValues(_.toSet).withDefaultValue(Set.empty) - } - - ExtractedSourceDependencies(pairsToMultiMap(memberRefDeps), pairsToMultiMap(inheritanceDeps)) - } - - def extractDependenciesFromSrcs(srcs: (Symbol, String)*): ExtractedSourceDependencies = { - val symbols = srcs.map(_._1) - assert(symbols.distinct.size == symbols.size, - s"Duplicate symbols for srcs detected: $symbols") - extractDependenciesFromSrcs(List(srcs.toMap)) - } - - /** - * Compiles given source code snippets written to temporary files. Each snippet is - * written to a separate temporary file. - * - * Snippets can be grouped to be compiled together in the same compiler run. This is - * useful to compile macros, which cannot be used in the same compilation run that - * defines them. - * - * The sequence of temporary files corresponding to passed snippets and analysis - * callback is returned as a result. - */ - private def compileSrcs(groupedSrcs: List[List[String]]): (Seq[File], TestCallback) = { - withTemporaryDirectory { temp => - val analysisCallback = new TestCallback(nameHashing) - val classesDir = new File(temp, "classes") - classesDir.mkdir() - - val compiler = prepareCompiler(classesDir, analysisCallback, classesDir.toString) - - val files = for ((compilationUnit, unitId) <- groupedSrcs.zipWithIndex) yield { - val run = new compiler.Run - val srcFiles = compilationUnit.toSeq.zipWithIndex map { - case (src, i) => - val fileName = s"Test-$unitId-$i.scala" - prepareSrcFile(temp, fileName, src) - } - val srcFilePaths = srcFiles.map(srcFile => srcFile.getAbsolutePath).toList - - run.compile(srcFilePaths) - - srcFilePaths.foreach(f => new File(f).delete) - srcFiles - } - (files.flatten.toSeq, analysisCallback) - } - } - - private def compileSrcs(srcs: String*): (Seq[File], TestCallback) = { - compileSrcs(List(srcs.toList)) - } - - private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = { - val srcFile = new File(baseDir, fileName) - sbt.IO.write(srcFile, src) - srcFile - } - - private def prepareCompiler(outputDir: File, analysisCallback: AnalysisCallback, classpath: String = "."): CachedCompiler0#Compiler = { - val args = Array.empty[String] - object output extends SingleOutput { - def outputDirectory: File = outputDir - override def toString = s"SingleOutput($outputDirectory)" - } - val weakLog = new WeakLog(ConsoleLogger(), ConsoleReporter) - val cachedCompiler = new CachedCompiler0(args, output, weakLog, false) - val settings = cachedCompiler.settings - settings.classpath.value = classpath - settings.usejavacp.value = true - val scalaReporter = new ConsoleReporter(settings) - val delegatingReporter = DelegatingReporter(settings, ConsoleReporter) - val compiler = cachedCompiler.compiler - compiler.set(analysisCallback, delegatingReporter) - compiler - } - - private object ConsoleReporter extends Reporter { - def reset(): Unit = () - def hasErrors: Boolean = false - def hasWarnings: Boolean = false - def printWarnings(): Unit = () - def problems: Array[Problem] = Array.empty - def log(pos: Position, msg: String, sev: Severity): Unit = println(msg) - def comment(pos: Position, msg: String): Unit = () - def printSummary(): Unit = () - } - -} - -object ScalaCompilerForUnitTesting { - case class ExtractedSourceDependencies(memberRef: Map[Symbol, Set[Symbol]], inheritance: Map[Symbol, Set[Symbol]]) -} diff --git a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala b/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala deleted file mode 100644 index ba478ea61..000000000 --- a/compile/ivy/src/main/scala/sbt/compiler/ComponentCompiler.scala +++ /dev/null @@ -1,211 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt -package compiler - -import java.io.File -import scala.util.Try - -object ComponentCompiler { - val xsbtiID = "xsbti" - val srcExtension = "-src" - val binSeparator = "-bin_" - val compilerInterfaceID = "compiler-interface" - val compilerInterfaceSrcID = compilerInterfaceID + srcExtension - val javaVersion = System.getProperty("java.class.version") - - @deprecated("Use `interfaceProvider(ComponentManager, IvyConfiguration, File)`.", "0.13.10") - def interfaceProvider(manager: ComponentManager): CompilerInterfaceProvider = new CompilerInterfaceProvider { - def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = - { - // this is the instance used to compile the interface component - val componentCompiler = new ComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager) - log.debug("Getting " + compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version) - componentCompiler(compilerInterfaceID) - } - } - - def interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, bootDirectory: File): CompilerInterfaceProvider = new CompilerInterfaceProvider { - def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = - { - // this is the instance used to compile the interface component - val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, ivyConfiguration, bootDirectory, log) - log.debug("Getting " + compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version) - componentCompiler(compilerInterfaceID) - } - } -} -/** - * This class provides source components compiled with the provided RawCompiler. - * The compiled classes are cached using the provided component manager according - * to the actualVersion field of the RawCompiler. - */ -@deprecated("Replaced by IvyComponentCompiler.", "0.13.10") -class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager) { - import ComponentCompiler._ - def apply(id: String): File = - try { getPrecompiled(id) } - catch { case _: InvalidComponent => getLocallyCompiled(id) } - - /** - * Gets the precompiled (distributed with sbt) component with the given 'id' - * If the component has not been precompiled, this throws InvalidComponent. - */ - def getPrecompiled(id: String): File = manager.file(binaryID(id, false))(IfMissing.Fail) - /** - * Get the locally compiled component with the given 'id' or compiles it if it has not been compiled yet. - * If the component does not exist, this throws InvalidComponent. - */ - def getLocallyCompiled(id: String): File = - { - val binID = binaryID(id, true) - manager.file(binID)(new IfMissing.Define(true, compileAndInstall(id, binID))) - } - def clearCache(id: String): Unit = manager.clearCache(binaryID(id, true)) - protected def binaryID(id: String, withJavaVersion: Boolean) = - { - val base = id + binSeparator + compiler.scalaInstance.actualVersion - if (withJavaVersion) base + "__" + javaVersion else base - } - protected def compileAndInstall(id: String, binID: String): Unit = { - val srcID = id + srcExtension - IO.withTemporaryDirectory { binaryDirectory => - val targetJar = new File(binaryDirectory, id + ".jar") - val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail) - AnalyzingCompiler.compileSources(manager.files(srcID)(IfMissing.Fail), targetJar, xsbtiJars, id, compiler, manager.log) - manager.define(binID, Seq(targetJar)) - } - } -} - -/** - * Component compiler which is able to find the most specific version available of - * the compiler interface sources using Ivy. - * The compiled classes are cached using the provided component manager according - * to the actualVersion field of the RawCompiler. - */ -private[compiler] class IvyComponentCompiler(compiler: RawCompiler, manager: ComponentManager, ivyConfiguration: IvyConfiguration, bootDirectory: File, log: Logger) { - import ComponentCompiler._ - - private val sbtOrg = xsbti.ArtifactInfo.SbtOrganization - private val sbtOrgTemp = JsonUtil.sbtOrgTemp - private val modulePrefixTemp = "temp-module-" - private val ivySbt: IvySbt = new IvySbt(ivyConfiguration) - private val sbtVersion = ComponentManager.version - private val buffered = new BufferedLogger(FullLogger(log)) - private val retrieveDirectory = new File(s"$bootDirectory/scala-${compiler.scalaInstance.version}/$sbtOrg/sbt/$sbtVersion/compiler-interface-srcs") - - def apply(id: String): File = { - val binID = binaryID(id) - manager.file(binID)(new IfMissing.Define(true, compileAndInstall(id, binID))) - } - - private def binaryID(id: String): String = { - val base = id + binSeparator + compiler.scalaInstance.actualVersion - base + "__" + javaVersion - } - - private def compileAndInstall(id: String, binID: String): Unit = { - def interfaceSources(moduleVersions: Vector[VersionNumber]): Iterable[File] = - moduleVersions match { - case Vector() => - def getAndDefineDefaultSources() = - update(getModule(id))(_.getName endsWith "-sources.jar") map { sourcesJar => - manager.define(id, sourcesJar) - sourcesJar - } getOrElse (throw new InvalidComponent(s"Couldn't retrieve default sources: module '$id'")) - - buffered.debug(s"Fetching default sources: module '$id'") - manager.files(id)(new IfMissing.Fallback(getAndDefineDefaultSources())) - - case version +: rest => - val moduleName = s"${id}_$version" - def getAndDefineVersionSpecificSources() = - update(getModule(moduleName))(_.getName endsWith "-sources.jar") map { sourcesJar => - manager.define(moduleName, sourcesJar) - sourcesJar - } getOrElse interfaceSources(rest) - - buffered.debug(s"Fetching version-specific sources: module '$moduleName'") - manager.files(moduleName)(new IfMissing.Fallback(getAndDefineVersionSpecificSources())) - } - IO.withTemporaryDirectory { binaryDirectory => - - val targetJar = new File(binaryDirectory, s"$binID.jar") - val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail) - - val sourceModuleVersions = VersionNumber(compiler.scalaInstance.actualVersion).cascadingVersions - val sources = buffered bufferQuietly interfaceSources(sourceModuleVersions) - AnalyzingCompiler.compileSources(sources, targetJar, xsbtiJars, id, compiler, log) - - manager.define(binID, Seq(targetJar)) - - } - } - - /** - * Returns a dummy module that depends on "org.scala-sbt" % `id` % `sbtVersion`. - * Note: Sbt's implementation of Ivy requires us to do this, because only the dependencies - * of the specified module will be downloaded. - */ - private def getModule(id: String): ivySbt.Module = { - val sha1 = Hash.toHex(Hash(id)) - val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, sbtVersion, Some("component")) - val moduleID = ModuleID(sbtOrg, id, sbtVersion, Some("component")).sources() - getModule(dummyID, Seq(moduleID)) - } - - private def getModule(moduleID: ModuleID, deps: Seq[ModuleID], uo: UpdateOptions = UpdateOptions()): ivySbt.Module = { - val moduleSetting = InlineConfiguration( - module = moduleID, - moduleInfo = ModuleInfo(moduleID.name), - dependencies = deps, - configurations = Seq(Configurations.Component), - ivyScala = None) - - new ivySbt.Module(moduleSetting) - } - - private def dependenciesNames(module: ivySbt.Module): String = module.moduleSettings match { - // `module` is a dummy module, we will only fetch its dependencies. - case ic: InlineConfiguration => - ic.dependencies map { - case mID: ModuleID => - import mID._ - s"$organization % $name % $revision" - } mkString ", " - case _ => - s"unknown" - } - - private def update(module: ivySbt.Module)(predicate: File => Boolean): Option[Seq[File]] = { - - val retrieveConfiguration = new RetrieveConfiguration(retrieveDirectory, Resolver.defaultRetrievePattern, false) - val updateConfiguration = new UpdateConfiguration(Some(retrieveConfiguration), true, UpdateLogging.DownloadOnly) - - buffered.info(s"Attempting to fetch ${dependenciesNames(module)}. This operation may fail.") - IvyActions.updateEither(module, updateConfiguration, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, buffered) match { - case Left(unresolvedWarning) => - buffered.debug("Couldn't retrieve module ${dependenciesNames(module)}.") - None - - case Right(updateReport) => - val allFiles = - for { - conf <- updateReport.configurations - m <- conf.modules - (_, f) <- m.artifacts - } yield f - - buffered.debug(s"Files retrieved for ${dependenciesNames(module)}:") - buffered.debug(allFiles mkString ", ") - - allFiles filter predicate match { - case Seq() => None - case files => Some(files) - } - - } - } -} diff --git a/compile/persist/NOTICE b/compile/persist/NOTICE deleted file mode 100644 index bf0a7de5e..000000000 --- a/compile/persist/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Analysis Store Component -Copyright 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala b/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala deleted file mode 100644 index a037d630f..000000000 --- a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala +++ /dev/null @@ -1,154 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import xsbti.api.{ Source, Compilation } -import xsbti.{ Position, Problem, Severity } -import xsbti.compile.{ CompileOrder, Output => APIOutput, SingleOutput, MultipleOutput } -import xsbti.DependencyContext._ -import MultipleOutput.OutputGroup -import java.io.File -import sbinary._ -import DefaultProtocol._ -import DefaultProtocol.tuple2Format -import Logger.{ m2o, position, problem } -import Relations.{ Source => RSource, SourceDependencies } - -@deprecated("Replaced by TextAnalysisFormat. OK to remove in 0.14.", since = "0.13.1") -object AnalysisFormats { - type RFF = Relation[File, File] - type RFS = Relation[File, String] - - import System.{ currentTimeMillis => now } - val start = now - def time(label: String) = - { - val end = now - println(label + ": " + (end - start) + " ms") - } - - def debug[T](label: String, f: Format[T]): Format[T] = new Format[T] { - def reads(in: Input): T = - { - time(label + ".read.start") - val r = f.reads(in) - time(label + ".read.end") - r - } - def writes(out: Output, t: T): Unit = { - time(label + ".write.start") - f.writes(out, t) - time(label + ".write.end") - } - } - - implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations], - infosF: Format[SourceInfos], compilationsF: Format[Compilations]): Format[Analysis] = - asProduct5(Analysis.Empty.copy _)(a => (a.stamps, a.apis, a.relations, a.infos, a.compilations))(stampsF, apisF, relationsF, infosF, compilationsF) - - implicit def infosFormat(implicit infoF: Format[Map[File, SourceInfo]]): Format[SourceInfos] = - wrap[SourceInfos, Map[File, SourceInfo]](_.allInfos, SourceInfos.make _) - - implicit def infoFormat: Format[SourceInfo] = - wrap[SourceInfo, (Seq[Problem], Seq[Problem])](si => (si.reportedProblems, si.unreportedProblems), { case (a, b) => SourceInfos.makeInfo(a, b) }) - - implicit def problemFormat: Format[Problem] = asProduct4(problem _)(p => (p.category, p.position, p.message, p.severity)) - - implicit def compilationsFormat: Format[Compilations] = { - implicit val compilationSeqF = seqFormat(xsbt.api.CompilationFormat) - wrap[Compilations, Seq[Compilation]](_.allCompilations, Compilations.make _) - } - - implicit def positionFormat: Format[Position] = - asProduct7(position _)(p => (m2o(p.line), p.lineContent, m2o(p.offset), m2o(p.pointer), m2o(p.pointerSpace), m2o(p.sourcePath), m2o(p.sourceFile))) - - implicit val fileOptionFormat: Format[Option[File]] = optionsAreFormat[File](fileFormat) - implicit val integerFormat: Format[Integer] = wrap[Integer, Int](_.toInt, Integer.valueOf) - implicit val severityFormat: Format[Severity] = - wrap[Severity, Byte](_.ordinal.toByte, b => Severity.values.apply(b.toInt)) - - implicit def setupFormat(implicit outputF: Format[APIOutput], optionF: Format[CompileOptions], compilerVersion: Format[String], orderF: Format[CompileOrder], nameHashingF: Format[Boolean]): Format[CompileSetup] = - asProduct5[CompileSetup, APIOutput, CompileOptions, String, CompileOrder, Boolean]((a, b, c, d, e) => new CompileSetup(a, b, c, d, e))(s => (s.output, s.options, s.compilerVersion, s.order, s.nameHashing))(outputF, optionF, compilerVersion, orderF, nameHashingF) - - implicit val outputGroupFormat: Format[OutputGroup] = - asProduct2((a: File, b: File) => new OutputGroup { def sourceDirectory = a; def outputDirectory = b }) { out => (out.sourceDirectory, out.outputDirectory) }(fileFormat, fileFormat) - implicit val multipleOutputFormat: Format[MultipleOutput] = - wrap[MultipleOutput, Array[OutputGroup]]( - (_.outputGroups), - { - groups => - new MultipleOutput { - def outputGroups = groups - override def toString = s"MultipleOutput($outputGroups)" - } - } - ) - implicit val singleOutputFormat: Format[SingleOutput] = - wrap[SingleOutput, File]( - (_.outputDirectory), - { out => new SingleOutput { def outputDirectory = out } } - )(fileFormat) - implicit val outputFormat: Format[APIOutput] = asUnion(singleOutputFormat, multipleOutputFormat) - - implicit def stampsFormat(implicit prodF: Format[Map[File, Stamp]], srcF: Format[Map[File, Stamp]], binF: Format[Map[File, Stamp]], nameF: Format[Map[File, String]]): Format[Stamps] = - asProduct4(Stamps.apply _)(s => (s.products, s.sources, s.binaries, s.classNames))(prodF, srcF, binF, nameF) - - implicit def stampFormat(implicit hashF: Format[Hash], modF: Format[LastModified], existsF: Format[Exists]): Format[Stamp] = - asUnion(hashF, modF, existsF) - - implicit def apisFormat(implicit internalF: Format[Map[File, Source]], externalF: Format[Map[String, Source]]): Format[APIs] = - asProduct2(APIs.apply _)(as => (as.internal, as.external))(internalF, externalF) - - implicit def relationsFormat(implicit prodF: Format[RFF], binF: Format[RFF], directF: Format[RSource], inheritedF: Format[RSource], memberRefF: Format[SourceDependencies], inheritanceF: Format[SourceDependencies], csF: Format[RFS], namesF: Format[RFS]): Format[Relations] = - { - def makeRelation(srcProd: RFF, binaryDep: RFF, direct: RSource, publicInherited: RSource, - memberRef: SourceDependencies, inheritance: SourceDependencies, classes: RFS, - nameHashing: Boolean, names: RFS): Relations = if (nameHashing) { - def isEmpty(sourceDependencies: RSource): Boolean = - sourceDependencies.internal.all.isEmpty && sourceDependencies.external.all.isEmpty - // we check direct dependencies only because publicInherited dependencies are subset of direct - assert(isEmpty(direct), "Direct dependencies are not empty but `nameHashing` flag is enabled.") - val internalDependencies = InternalDependencies(Map(DependencyByMemberRef -> memberRef.internal, DependencyByInheritance -> inheritance.internal)) - val externalDependencies = ExternalDependencies(Map(DependencyByMemberRef -> memberRef.external, DependencyByInheritance -> inheritance.external)) - Relations.make(srcProd, binaryDep, internalDependencies, externalDependencies, classes, names) - } else { - def isEmpty(sourceDependencies: SourceDependencies): Boolean = - sourceDependencies.internal.all.isEmpty && sourceDependencies.external.all.isEmpty - // we check memberRef dependencies only because inheritance dependencies are subset of memberRef - assert(isEmpty(memberRef), "Direct dependencies are not empty but `nameHashing` flag is enabled.") - Relations.make(srcProd, binaryDep, direct, publicInherited, classes) - } - asProduct9[Relations, RFF, RFF, RSource, RSource, SourceDependencies, SourceDependencies, RFS, Boolean, RFS]((a, b, c, d, e, f, g, h, i) => makeRelation(a, b, c, d, e, f, g, h, i))( - rs => (rs.srcProd, rs.binaryDep, rs.direct, rs.publicInherited, rs.memberRef, rs.inheritance, rs.classes, rs.nameHashing, rs.names))( - prodF, binF, directF, inheritedF, memberRefF, inheritanceF, csF, implicitly[Format[Boolean]], namesF) - } - - implicit def relationsSourceFormat(implicit internalFormat: Format[Relation[File, File]], externalFormat: Format[Relation[File, String]]): Format[RSource] = - asProduct2[RSource, RFF, RFS]((a, b) => Relations.makeSource(a, b))(rs => (rs.internal, rs.external)) - - implicit def relationsSourceDependenciesFormat(implicit internalFormat: Format[Relation[File, File]], externalFormat: Format[Relation[File, String]]): Format[SourceDependencies] = - asProduct2[SourceDependencies, RFF, RFS]((a, b) => Relations.makeSourceDependencies(a, b))(rs => (rs.internal, rs.external)) - - implicit def relationFormat[A, B](implicit af: Format[Map[A, Set[B]]], bf: Format[Map[B, Set[A]]]): Format[Relation[A, B]] = - asProduct2[Relation[A, B], Map[A, Set[B]], Map[B, Set[A]]](Relation.make _)(r => (r.forwardMap, r.reverseMap))(af, bf) - - implicit val sourceFormat: Format[Source] = xsbt.api.SourceFormat - - implicit def fileFormat: Format[File] = wrap[File, String](_.getAbsolutePath, s => new File(s)) - // can't require Format[Seq[String]] because its complexity is higher than Format[CompileOptions] - implicit def optsFormat(implicit strF: Format[String]): Format[CompileOptions] = - wrap[CompileOptions, (Seq[String], Seq[String])](co => (co.options, co.javacOptions), os => new CompileOptions(os._1, os._2)) - - implicit val orderFormat: Format[CompileOrder] = - { - val values = CompileOrder.values - wrap[CompileOrder, Int](_.ordinal, values) - } - implicit def seqFormat[T](implicit optionFormat: Format[T]): Format[Seq[T]] = viaSeq[Seq[T], T](x => x) - - implicit def hashStampFormat: Format[Hash] = wrap[Hash, Array[Byte]](_.value, new Hash(_)) - implicit def lastModFormat: Format[LastModified] = wrap[LastModified, Long](_.value, new LastModified(_)) - implicit def existsFormat: Format[Exists] = wrap[Exists, Boolean](_.value, new Exists(_)) -} diff --git a/compile/persist/src/main/scala/sbt/inc/FileBasedStore.scala b/compile/persist/src/main/scala/sbt/inc/FileBasedStore.scala deleted file mode 100644 index 01ceb92e1..000000000 --- a/compile/persist/src/main/scala/sbt/inc/FileBasedStore.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package inc - -import java.io.File - -object FileBasedStore { - def apply(file: File): AnalysisStore = new AnalysisStore { - def set(analysis: Analysis, setup: CompileSetup): Unit = { - Using.fileWriter(IO.utf8)(file) { writer => TextAnalysisFormat.write(writer, analysis, setup) } - } - - def get(): Option[(Analysis, CompileSetup)] = - try { Some(getUncaught()) } catch { case _: Exception => None } - def getUncaught(): (Analysis, CompileSetup) = - Using.fileReader(IO.utf8)(file) { reader => TextAnalysisFormat.read(reader) } - } -} diff --git a/compile/persist/src/main/scala/sbt/inc/TextAnalysisFormat.scala b/compile/persist/src/main/scala/sbt/inc/TextAnalysisFormat.scala deleted file mode 100644 index 74221a9c6..000000000 --- a/compile/persist/src/main/scala/sbt/inc/TextAnalysisFormat.scala +++ /dev/null @@ -1,403 +0,0 @@ -package sbt -package inc - -import java.io._ -import sbt.{ CompileSetup, Relation } -import xsbti.api.{ Compilation, Source } -import xsbti.compile.{ MultipleOutput, SingleOutput } -import javax.xml.bind.DatatypeConverter - -// Very simple timer for timing repeated code sections. -// TODO: Temporary. Remove once we've milked all available performance gains. -private[inc] object FormatTimer { - private val timers = scala.collection.mutable.Map[String, Long]() - private val printTimings = "true" == System.getProperty("sbt.analysis.debug.timing") - - def aggregate[T](key: String)(f: => T) = { - val start = System.nanoTime() - val ret = f - val elapsed = System.nanoTime() - start - timers.update(key, timers.getOrElseUpdate(key, 0) + elapsed) - ret - } - - def time[T](key: String)(f: => T) = { - val ret = aggregate(key)(f) - close(key) - ret - } - - def close(key: String): Unit = { - if (printTimings) { - println("[%s] %dms".format(key, timers.getOrElse(key, 0L) / 1000000)) - } - timers.remove(key) - } -} - -class ReadException(s: String) extends Exception(s) { - def this(expected: String, found: String) = this("Expected: %s. Found: %s.".format(expected, found)) -} - -class EOFException extends ReadException("Unexpected EOF.") - -// A text-based serialization format for Analysis objects. -// This code has been tuned for high performance, and therefore has non-idiomatic areas. -// Please refrain from making changes that significantly degrade read/write performance on large analysis files. -object TextAnalysisFormat { - // Some types are not required for external inspection/manipulation of the analysis file, - // and are complex to serialize as text. So we serialize them as base64-encoded sbinary-serialized blobs. - // TODO: This is a big performance hit. Figure out a more efficient way to serialize API objects? - import sbinary.DefaultProtocol.{ immutableMapFormat, immutableSetFormat, StringFormat, tuple2Format } - import AnalysisFormats._ - implicit val compilationF = xsbt.api.CompilationFormat - - def write(out: Writer, analysis: Analysis, setup: CompileSetup): Unit = { - VersionF.write(out) - // We start with writing compile setup which contains value of the `nameHashing` - // flag that is needed to properly deserialize relations - FormatTimer.time("write setup") { CompileSetupF.write(out, setup) } - // Next we write relations because that's the part of greatest interest to external readers, - // who can abort reading early once they're read them. - FormatTimer.time("write relations") { RelationsF.write(out, analysis.relations) } - FormatTimer.time("write stamps") { StampsF.write(out, analysis.stamps) } - FormatTimer.time("write apis") { APIsF.write(out, analysis.apis) } - FormatTimer.time("write sourceinfos") { SourceInfosF.write(out, analysis.infos) } - FormatTimer.time("write compilations") { CompilationsF.write(out, analysis.compilations) } - out.flush() - } - - def read(in: BufferedReader): (Analysis, CompileSetup) = { - VersionF.read(in) - val setup = FormatTimer.time("read setup") { CompileSetupF.read(in) } - val relations = FormatTimer.time("read relations") { RelationsF.read(in, setup.nameHashing) } - val stamps = FormatTimer.time("read stamps") { StampsF.read(in) } - val apis = FormatTimer.time("read apis") { APIsF.read(in) } - val infos = FormatTimer.time("read sourceinfos") { SourceInfosF.read(in) } - val compilations = FormatTimer.time("read compilations") { CompilationsF.read(in) } - - (Analysis.Empty.copy(stamps, apis, relations, infos, compilations), setup) - } - - private[this] object VersionF { - val currentVersion = "5" - - def write(out: Writer): Unit = { - out.write("format version: %s\n".format(currentVersion)) - } - - private val versionPattern = """format version: (\w+)""".r - def read(in: BufferedReader): Unit = { - in.readLine() match { - case versionPattern(version) => validateVersion(version) - case s: String => throw new ReadException("\"format version: \"", s) - case null => throw new EOFException - } - } - - def validateVersion(version: String): Unit = { - // TODO: Support backwards compatibility? - if (version != currentVersion) { - throw new ReadException("File uses format version %s, but we are compatible with version %s only.".format(version, currentVersion)) - } - } - } - - private[this] object RelationsF { - object Headers { - val srcProd = "products" - val binaryDep = "binary dependencies" - val directSrcDep = "direct source dependencies" - val directExternalDep = "direct external dependencies" - val internalSrcDepPI = "public inherited source dependencies" - val externalDepPI = "public inherited external dependencies" - val classes = "class names" - - val memberRefInternalDep = "member reference internal dependencies" - val memberRefExternalDep = "member reference external dependencies" - val inheritanceInternalDep = "inheritance internal dependencies" - val inheritanceExternalDep = "inheritance external dependencies" - - val usedNames = "used names" - } - - def write(out: Writer, relations: Relations): Unit = { - // This ordering is used to persist all values in order. Since all values will be - // persisted using their string representation, it makes sense to sort them using - // their string representation. - val toStringOrd = new Ordering[Any] { - def compare(a: Any, b: Any) = a.toString compare b.toString - } - def writeRelation[T](header: String, rel: Relation[File, T]): Unit = { - writeHeader(out, header) - writeSize(out, rel.size) - // We sort for ease of debugging and for more efficient reconstruction when reading. - // Note that we don't share code with writeMap. Each is implemented more efficiently - // than the shared code would be, and the difference is measurable on large analyses. - rel.forwardMap.toSeq.sortBy(_._1).foreach { - case (k, vs) => - val kStr = k.toString - vs.toSeq.sorted(toStringOrd) foreach { v => - out.write(kStr); out.write(" -> "); out.write(v.toString); out.write("\n") - } - } - } - - relations.allRelations.foreach { - case (header, rel) => writeRelation(header, rel) - } - } - - def read(in: BufferedReader, nameHashing: Boolean): Relations = { - def readRelation[T](expectedHeader: String, s2t: String => T): Relation[File, T] = { - val items = readPairs(in)(expectedHeader, new File(_), s2t).toIterator - // Reconstruct the forward map. This is more efficient than Relation.empty ++ items. - var forward: List[(File, Set[T])] = Nil - var currentItem: (File, T) = null - var currentFile: File = null - var currentVals: List[T] = Nil - def closeEntry(): Unit = { - if (currentFile != null) forward = (currentFile, currentVals.toSet) :: forward - currentFile = currentItem._1 - currentVals = currentItem._2 :: Nil - } - while (items.hasNext) { - currentItem = items.next() - if (currentItem._1 == currentFile) currentVals = currentItem._2 :: currentVals else closeEntry() - } - if (currentItem != null) closeEntry() - Relation.reconstruct(forward.toMap) - } - - val relations = Relations.existingRelations map { case (header, fun) => readRelation(header, fun) } - - Relations.construct(nameHashing, relations) - } - } - - private[this] object StampsF { - object Headers { - val products = "product stamps" - val sources = "source stamps" - val binaries = "binary stamps" - val classNames = "class names" - } - - def write(out: Writer, stamps: Stamps): Unit = { - def doWriteMap[V](header: String, m: Map[File, V]) = writeMap(out)(header, m, { v: V => v.toString }) - - doWriteMap(Headers.products, stamps.products) - doWriteMap(Headers.sources, stamps.sources) - doWriteMap(Headers.binaries, stamps.binaries) - doWriteMap(Headers.classNames, stamps.classNames) - } - - def read(in: BufferedReader): Stamps = { - def doReadMap[V](expectedHeader: String, s2v: String => V) = readMap(in)(expectedHeader, new File(_), s2v) - val products = doReadMap(Headers.products, Stamp.fromString) - val sources = doReadMap(Headers.sources, Stamp.fromString) - val binaries = doReadMap(Headers.binaries, Stamp.fromString) - val classNames = doReadMap(Headers.classNames, identity[String]) - - Stamps(products, sources, binaries, classNames) - } - } - - private[this] object APIsF { - object Headers { - val internal = "internal apis" - val external = "external apis" - } - - val stringToSource = ObjectStringifier.stringToObj[Source] _ - val sourceToString = ObjectStringifier.objToString[Source] _ - - def write(out: Writer, apis: APIs): Unit = { - writeMap(out)(Headers.internal, apis.internal, sourceToString, inlineVals = false) - writeMap(out)(Headers.external, apis.external, sourceToString, inlineVals = false) - FormatTimer.close("bytes -> base64") - FormatTimer.close("byte copy") - FormatTimer.close("sbinary write") - } - - def read(in: BufferedReader): APIs = { - val internal = readMap(in)(Headers.internal, new File(_), stringToSource) - val external = readMap(in)(Headers.external, identity[String], stringToSource) - FormatTimer.close("base64 -> bytes") - FormatTimer.close("sbinary read") - APIs(internal, external) - } - } - - private[this] object SourceInfosF { - object Headers { - val infos = "source infos" - } - - val stringToSourceInfo = ObjectStringifier.stringToObj[SourceInfo] _ - val sourceInfoToString = ObjectStringifier.objToString[SourceInfo] _ - - def write(out: Writer, infos: SourceInfos): Unit = writeMap(out)(Headers.infos, infos.allInfos, sourceInfoToString, inlineVals = false) - def read(in: BufferedReader): SourceInfos = SourceInfos.make(readMap(in)(Headers.infos, new File(_), stringToSourceInfo)) - } - - private[this] object CompilationsF { - object Headers { - val compilations = "compilations" - } - - val stringToCompilation = ObjectStringifier.stringToObj[Compilation] _ - val compilationToString = ObjectStringifier.objToString[Compilation] _ - - def write(out: Writer, compilations: Compilations): Unit = - writeSeq(out)(Headers.compilations, compilations.allCompilations, compilationToString) - - def read(in: BufferedReader): Compilations = Compilations.make( - readSeq[Compilation](in)(Headers.compilations, stringToCompilation)) - } - - private[this] object CompileSetupF { - object Headers { - val outputMode = "output mode" - val outputDir = "output directories" - val compileOptions = "compile options" - val javacOptions = "javac options" - val compilerVersion = "compiler version" - val compileOrder = "compile order" - val nameHashing = "name hashing" - } - - private[this] val singleOutputMode = "single" - private[this] val multipleOutputMode = "multiple" - private[this] val singleOutputKey = new File("output dir") - - def write(out: Writer, setup: CompileSetup): Unit = { - val (mode, outputAsMap) = setup.output match { - case s: SingleOutput => (singleOutputMode, Map(singleOutputKey -> s.outputDirectory)) - case m: MultipleOutput => (multipleOutputMode, m.outputGroups.map(x => x.sourceDirectory -> x.outputDirectory).toMap) - } - - writeSeq(out)(Headers.outputMode, mode :: Nil, identity[String]) - writeMap(out)(Headers.outputDir, outputAsMap, { f: File => f.getPath }) - writeSeq(out)(Headers.compileOptions, setup.options.options, identity[String]) - writeSeq(out)(Headers.javacOptions, setup.options.javacOptions, identity[String]) - writeSeq(out)(Headers.compilerVersion, setup.compilerVersion :: Nil, identity[String]) - writeSeq(out)(Headers.compileOrder, setup.order.name :: Nil, identity[String]) - writeSeq(out)(Headers.nameHashing, setup.nameHashing :: Nil, (b: Boolean) => b.toString) - } - - def read(in: BufferedReader): CompileSetup = { - def s2f(s: String) = new File(s) - def s2b(s: String): Boolean = s.toBoolean - val outputDirMode = readSeq(in)(Headers.outputMode, identity[String]).headOption - val outputAsMap = readMap(in)(Headers.outputDir, s2f, s2f) - val compileOptions = readSeq(in)(Headers.compileOptions, identity[String]) - val javacOptions = readSeq(in)(Headers.javacOptions, identity[String]) - val compilerVersion = readSeq(in)(Headers.compilerVersion, identity[String]).head - val compileOrder = readSeq(in)(Headers.compileOrder, identity[String]).head - val nameHashing = readSeq(in)(Headers.nameHashing, s2b).head - - val output = outputDirMode match { - case Some(s) => s match { - case `singleOutputMode` => new SingleOutput { - val outputDirectory = outputAsMap(singleOutputKey) - } - case `multipleOutputMode` => new MultipleOutput { - val outputGroups: Array[MultipleOutput.OutputGroup] = outputAsMap.toArray.map { - case (src: File, out: File) => new MultipleOutput.OutputGroup { - val sourceDirectory = src - val outputDirectory = out - override def toString = s"OutputGroup($src -> $out)" - } - } - override def toString = s"MultipleOuput($outputGroups)" - } - case str: String => throw new ReadException("Unrecognized output mode: " + str) - } - case None => throw new ReadException("No output mode specified") - } - - new CompileSetup(output, new CompileOptions(compileOptions, javacOptions), compilerVersion, - xsbti.compile.CompileOrder.valueOf(compileOrder), nameHashing) - } - } - - private[this] object ObjectStringifier { - def objToString[T](o: T)(implicit fmt: sbinary.Format[T]) = { - val baos = new ByteArrayOutputStream() - val out = new sbinary.JavaOutput(baos) - FormatTimer.aggregate("sbinary write") { try { fmt.writes(out, o) } finally { baos.close() } } - val bytes = FormatTimer.aggregate("byte copy") { baos.toByteArray } - FormatTimer.aggregate("bytes -> base64") { DatatypeConverter.printBase64Binary(bytes) } - } - - def stringToObj[T](s: String)(implicit fmt: sbinary.Format[T]) = { - val bytes = FormatTimer.aggregate("base64 -> bytes") { DatatypeConverter.parseBase64Binary(s) } - val in = new sbinary.JavaInput(new ByteArrayInputStream(bytes)) - FormatTimer.aggregate("sbinary read") { fmt.reads(in) } - } - } - - // Various helper functions. - - private[this] def writeHeader(out: Writer, header: String): Unit = out.write(header + ":\n") - - private[this] def expectHeader(in: BufferedReader, expectedHeader: String): Unit = { - val header = in.readLine() - if (header != expectedHeader + ":") throw new ReadException(expectedHeader, if (header == null) "EOF" else header) - } - - private[this] def writeSize(out: Writer, n: Int): Unit = out.write("%d items\n".format(n)) - - private val itemsPattern = """(\d+) items""".r - private[this] def readSize(in: BufferedReader): Int = { - in.readLine() match { - case itemsPattern(nStr) => Integer.parseInt(nStr) - case s: String => throw new ReadException("\" items\"", s) - case null => throw new EOFException - } - } - - private[this] def writeSeq[T](out: Writer)(header: String, s: Seq[T], t2s: T => String): Unit = { - // We write sequences as idx -> element maps, for uniformity with maps/relations. - def n = s.length - val numDigits = if (n < 2) 1 else math.log10(n - 1).toInt + 1 - val fmtStr = "%%0%dd".format(numDigits) - // We only use this for relatively short seqs, so creating this extra map won't be a performance hit. - val m: Map[String, T] = s.zipWithIndex.map(x => fmtStr.format(x._2) -> x._1).toMap - writeMap(out)(header, m, t2s) - } - - private[this] def readSeq[T](in: BufferedReader)(expectedHeader: String, s2t: String => T): Seq[T] = - (readPairs(in)(expectedHeader, identity[String], s2t).toSeq.sortBy(_._1) map (_._2)) - - private[this] def writeMap[K, V](out: Writer)(header: String, m: Map[K, V], v2s: V => String, inlineVals: Boolean = true)(implicit ord: Ordering[K]): Unit = { - writeHeader(out, header) - writeSize(out, m.size) - m.keys.toSeq.sorted foreach { k => - out.write(k.toString) - out.write(" -> ") - if (!inlineVals) out.write("\n") // Put large vals on their own line, to save string munging on read. - out.write(v2s(m(k))) - out.write("\n") - } - } - - private[this] def readPairs[K, V](in: BufferedReader)(expectedHeader: String, s2k: String => K, s2v: String => V): Traversable[(K, V)] = { - def toPair(s: String): (K, V) = { - if (s == null) throw new EOFException - val p = s.indexOf(" -> ") - val k = s2k(s.substring(0, p)) - // Pair is either "a -> b" or "a -> \nb". This saves us a lot of substring munging when b is a large blob. - val v = s2v(if (p == s.length - 4) in.readLine() else s.substring(p + 4)) - (k, v) - } - expectHeader(in, expectedHeader) - val n = readSize(in) - for (i <- 0 until n) yield toPair(in.readLine()) - } - - private[this] def readMap[K, V](in: BufferedReader)(expectedHeader: String, s2k: String => K, s2v: String => V): Map[K, V] = { - readPairs(in)(expectedHeader, s2k, s2v).toMap - } -} diff --git a/compile/persist/src/main/scala/xsbt/api/CompilationFormat.scala b/compile/persist/src/main/scala/xsbt/api/CompilationFormat.scala deleted file mode 100644 index 9ad58cc02..000000000 --- a/compile/persist/src/main/scala/xsbt/api/CompilationFormat.scala +++ /dev/null @@ -1,16 +0,0 @@ -package xsbt.api - -import xsbti.api._ -import sbinary._ - -object CompilationFormat extends Format[Compilation] { - import java.io._ - def reads(in: Input): Compilation = { - val oin = new ObjectInputStream(new InputWrapperStream(in)) - try { oin.readObject.asInstanceOf[Compilation] } finally { oin.close() } - } - def writes(out: Output, src: Compilation): Unit = { - val oout = new ObjectOutputStream(new OutputWrapperStream(out)) - try { oout.writeObject(src) } finally { oout.close() } - } -} diff --git a/compile/persist/src/main/scala/xsbt/api/SourceFormat.scala b/compile/persist/src/main/scala/xsbt/api/SourceFormat.scala deleted file mode 100644 index 6b6e29aaa..000000000 --- a/compile/persist/src/main/scala/xsbt/api/SourceFormat.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - -import xsbti.SafeLazy -import xsbti.api._ -import sbt.Using -import sbinary._ -import DefaultProtocol._ -import Operations.{ read, write } -import java.io.File -import scala.collection.mutable - -object SourceFormat extends Format[Source] { - import java.io._ - def reads(in: Input): Source = - { - val oin = new ObjectInputStream(new InputWrapperStream(in)) - try { oin.readObject.asInstanceOf[Source] } finally { oin.close() } - } - def writes(out: Output, src: Source): Unit = { - val oout = new ObjectOutputStream(new OutputWrapperStream(out)) - try { oout.writeObject(src) } finally { oout.close() } - } -} -final class InputWrapperStream(in: Input) extends java.io.InputStream { - def toInt(b: Byte) = if (b < 0) b + 256 else b.toInt - def read() = try { toInt(in.readByte) } catch { case e: sbinary.EOF => -1 } - override def read(b: Array[Byte], off: Int, len: Int) = in.readTo(b, off, len) -} -final class OutputWrapperStream(out: Output) extends java.io.OutputStream { - override def write(bs: Array[Byte], off: Int, len: Int) = out.writeAll(bs, off, len) - def write(b: Int) = out.writeByte(b.toByte) -} diff --git a/compile/persist/src/test/scala/sbt/inc/TextAnalysisFormatSpecification.scala b/compile/persist/src/test/scala/sbt/inc/TextAnalysisFormatSpecification.scala deleted file mode 100644 index 1b8c4cb15..000000000 --- a/compile/persist/src/test/scala/sbt/inc/TextAnalysisFormatSpecification.scala +++ /dev/null @@ -1,112 +0,0 @@ -package sbt -package inc - -import java.io.{ BufferedReader, File, StringReader, StringWriter } -import scala.math.abs -import org.scalacheck._ -import Gen._ -import Prop._ - -object TextAnalysisFormatTest extends Properties("TextAnalysisFormat") { - - val nameHashing = true - val dummyOutput = new xsbti.compile.SingleOutput { def outputDirectory: java.io.File = new java.io.File("dummy") } - val commonSetup = new CompileSetup(dummyOutput, new CompileOptions(Nil, Nil), "2.10.4", xsbti.compile.CompileOrder.Mixed, nameHashing) - val commonHeader = """format version: 5 - |output mode: - |1 items - |0 -> single - |output directories: - |1 items - |output dir -> dummy - |compile options: - |0 items - |javac options: - |0 items - |compiler version: - |1 items - |0 -> 2.10.4 - |compile order: - |1 items - |0 -> Mixed - |name hashing: - |1 items - |0 -> true""".stripMargin - - property("Write and read empty Analysis") = { - - val writer = new StringWriter - val analysis = Analysis.empty(nameHashing) - TextAnalysisFormat.write(writer, analysis, commonSetup) - - val result = writer.toString - - result.startsWith(commonHeader) - val reader = new BufferedReader(new StringReader(result)) - - val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader) - - analysis == readAnalysis - - } - - property("Write and read simple Analysis") = { - - import TestCaseGenerators._ - - def f(s: String) = new File(s) - val aScala = f("A.scala") - val bScala = f("B.scala") - val aSource = genSource("A" :: "A$" :: Nil).sample.get - val bSource = genSource("B" :: "B$" :: Nil).sample.get - val cSource = genSource("C" :: Nil).sample.get - val exists = new Exists(true) - val sourceInfos = SourceInfos.makeInfo(Nil, Nil) - - var analysis = Analysis.empty(nameHashing) - analysis = analysis.addProduct(aScala, f("A.class"), exists, "A") - analysis = analysis.addProduct(aScala, f("A$.class"), exists, "A$") - analysis = analysis.addSource(aScala, aSource, exists, Nil, Nil, sourceInfos) - analysis = analysis.addBinaryDep(aScala, f("x.jar"), "x", exists) - analysis = analysis.addExternalDep(aScala, "C", cSource, inherited = false) - - val writer = new StringWriter - - TextAnalysisFormat.write(writer, analysis, commonSetup) - - val result = writer.toString - - result.startsWith(commonHeader) - val reader = new BufferedReader(new StringReader(result)) - - val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader) - - compare(analysis, readAnalysis) - - } - - property("Write and read complex Analysis") = forAllNoShrink(TestCaseGenerators.genAnalysis(nameHashing)) { analysis: Analysis => - val writer = new StringWriter - - TextAnalysisFormat.write(writer, analysis, commonSetup) - - val result = writer.toString - - result.startsWith(commonHeader) - val reader = new BufferedReader(new StringReader(result)) - - val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader) - - compare(analysis, readAnalysis) - } - - // Compare two analyses with useful labelling when they aren't equal. - private[this] def compare(left: Analysis, right: Analysis): Prop = - s" LEFT: $left" |: - s"RIGHT: $right" |: - s"STAMPS EQUAL: ${left.stamps == right.stamps}" |: - s"APIS EQUAL: ${left.apis == right.apis}" |: - s"RELATIONS EQUAL: ${left.relations == right.relations}" |: - "UNEQUAL" |: - (left == right) -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/ClasspathOptions.scala b/compile/src/main/scala/sbt/ClasspathOptions.scala deleted file mode 100644 index 638f4d1fc..000000000 --- a/compile/src/main/scala/sbt/ClasspathOptions.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt - -final case class ClasspathOptions(bootLibrary: Boolean, compiler: Boolean, extra: Boolean, autoBoot: Boolean, filterLibrary: Boolean) extends xsbti.compile.ClasspathOptions -object ClasspathOptions { - def manual = ClasspathOptions(false, false, false, true, false) - def boot = ClasspathOptions(true, false, false, true, true) - def repl = auto - def javac(compiler: Boolean) = new ClasspathOptions(false, compiler, false, false, false) - def auto = ClasspathOptions(true, true, true, true, true) -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/LoggerReporter.scala b/compile/src/main/scala/sbt/LoggerReporter.scala deleted file mode 100644 index 609cdf2a0..000000000 --- a/compile/src/main/scala/sbt/LoggerReporter.scala +++ /dev/null @@ -1,135 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter, Reporter} -// Copyright 2002-2009 LAMP/EPFL -// see licenses/LICENSE_Scala -// Original author: Martin Odersky - -import xsbti.{ Maybe, Position, Problem, Reporter, Severity } -import java.io.File -import java.util.EnumMap -import scala.collection.mutable -import LoggerReporter._ -import Logger.{ m2o, o2m, position, problem } -import Severity.{ Error, Info => SInfo, Warn } - -object LoggerReporter { - final class PositionKey(pos: Position) { - def offset = pos.offset - def sourceFile = pos.sourceFile - - override def equals(o: Any) = - o match { case pk: PositionKey => equalsKey(pk); case _ => false } - - def equalsKey(o: PositionKey) = - m2o(pos.offset) == m2o(o.offset) && - m2o(pos.sourceFile) == m2o(o.sourceFile) - override def hashCode = - m2o(pos.offset).hashCode * 31 - m2o(pos.sourceFile).hashCode - } - - def countElementsAsString(n: Int, elements: String): String = - n match { - case 0 => "no " + elements + "s" - case 1 => "one " + elements - case 2 => "two " + elements + "s" - case 3 => "three " + elements + "s" - case 4 => "four " + elements + "s" - case _ => "" + n + " " + elements + "s" - } -} - -class LoggerReporter(maximumErrors: Int, log: Logger, sourcePositionMapper: Position => Position = { p => p }) extends xsbti.Reporter { - val positions = new mutable.HashMap[PositionKey, Severity] - val count = new EnumMap[Severity, Int](classOf[Severity]) - private[this] val allProblems = new mutable.ListBuffer[Problem] - - reset() - - def reset(): Unit = { - count.put(Warn, 0) - count.put(SInfo, 0) - count.put(Error, 0) - positions.clear() - allProblems.clear() - } - def hasWarnings = count.get(Warn) > 0 - def hasErrors = count.get(Error) > 0 - def problems: Array[Problem] = allProblems.toArray - def comment(pos: Position, msg: String): Unit = () - - def printSummary(): Unit = { - val warnings = count.get(Severity.Warn) - if (warnings > 0) - log.warn(countElementsAsString(warnings, "warning") + " found") - val errors = count.get(Severity.Error) - if (errors > 0) - log.error(countElementsAsString(errors, "error") + " found") - } - - def inc(sev: Severity) = count.put(sev, count.get(sev) + 1) - - def display(pos: Position, msg: String, severity: Severity): Unit = { - inc(severity) - if (severity != Error || maximumErrors <= 0 || count.get(severity) <= maximumErrors) - print(severityLogger(severity), pos, msg) - } - def severityLogger(severity: Severity): (=> String) => Unit = - m => - { - (severity match { - case Error => log.error(m) - case Warn => log.warn(m) - case SInfo => log.info(m) - }) - } - - def print(log: (=> String) => Unit, pos: Position, msg: String): Unit = { - if (pos.sourcePath.isEmpty && pos.line.isEmpty) - log(msg) - else { - val sourcePrefix = m2o(pos.sourcePath).getOrElse("") - val lineNumberString = m2o(pos.line).map(":" + _ + ":").getOrElse(":") + " " - log(sourcePrefix + lineNumberString + msg) - val lineContent = pos.lineContent - if (!lineContent.isEmpty) { - log(lineContent) - for (space <- m2o(pos.pointerSpace)) - log(space + "^") // pointer to the column position of the error/warning - } - } - } - - def log(pos: Position, msg: String, severity: Severity): Unit = - { - val mappedPos = sourcePositionMapper(pos) - allProblems += problem("", mappedPos, msg, severity) - severity match { - case Warn | Error => - { - if (!testAndLog(mappedPos, severity)) - display(mappedPos, msg, severity) - } - case _ => display(mappedPos, msg, severity) - } - } - - def testAndLog(pos: Position, severity: Severity): Boolean = - { - if (pos.offset.isEmpty || pos.sourceFile.isEmpty) - false - else { - val key = new PositionKey(pos) - if (positions.get(key).exists(_.ordinal >= severity.ordinal)) - true - else { - positions(key) = severity - false - } - } - } -} diff --git a/compile/src/main/scala/sbt/compiler/AnalyzingCompiler.scala b/compile/src/main/scala/sbt/compiler/AnalyzingCompiler.scala deleted file mode 100644 index 6f31452ff..000000000 --- a/compile/src/main/scala/sbt/compiler/AnalyzingCompiler.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt -package compiler - -import xsbti.{ AnalysisCallback, Logger => xLogger, Reporter } -import xsbti.compile.{ CachedCompiler, CachedCompilerProvider, DependencyChanges, GlobalsCache, CompileProgress, Output } -import java.io.File -import java.net.{ URL, URLClassLoader } - -/** - * Interface to the Scala compiler that uses the dependency analysis plugin. This class uses the Scala library and compiler - * provided by scalaInstance. This class requires a ComponentManager in order to obtain the interface code to scalac and - * the analysis plugin. Because these call Scala code for a different Scala version than the one used for this class, they must - * be compiled for the version of Scala being used. - */ -final class AnalyzingCompiler private (val scalaInstance: xsbti.compile.ScalaInstance, val provider: CompilerInterfaceProvider, val cp: xsbti.compile.ClasspathOptions, onArgsF: Seq[String] => Unit) extends CachedCompilerProvider { - def this(scalaInstance: xsbti.compile.ScalaInstance, provider: CompilerInterfaceProvider, cp: xsbti.compile.ClasspathOptions) = - this(scalaInstance, provider, cp, _ => ()) - def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider) = this(scalaInstance, provider, ClasspathOptions.auto) - - @deprecated("A Logger is no longer needed.", "0.13.0") - def this(scalaInstance: ScalaInstance, provider: CompilerInterfaceProvider, log: Logger) = this(scalaInstance, provider) - - @deprecated("A Logger is no longer needed.", "0.13.0") - def this(scalaInstance: xsbti.compile.ScalaInstance, provider: CompilerInterfaceProvider, cp: xsbti.compile.ClasspathOptions, log: Logger) = this(scalaInstance, provider, cp) - - def onArgs(f: Seq[String] => Unit): AnalyzingCompiler = new AnalyzingCompiler(scalaInstance, provider, cp, f) - - def apply(sources: Seq[File], changes: DependencyChanges, classpath: Seq[File], singleOutput: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, cache: GlobalsCache, log: Logger) { - val arguments = (new CompilerArguments(scalaInstance, cp))(Nil, classpath, None, options) - val output = CompileOutput(singleOutput) - compile(sources, changes, arguments, output, callback, new LoggerReporter(maximumErrors, log, p => p), cache, log, None) - } - - def compile(sources: Seq[File], changes: DependencyChanges, options: Seq[String], output: Output, callback: AnalysisCallback, reporter: Reporter, cache: GlobalsCache, log: Logger, progressOpt: Option[CompileProgress]): Unit = - { - val cached = cache(options.toArray, output, !changes.isEmpty, this, log, reporter) - val progress = progressOpt getOrElse IgnoreProgress - compile(sources, changes, callback, log, reporter, progress, cached) - } - - def compile(sources: Seq[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, reporter: Reporter, progress: CompileProgress, compiler: CachedCompiler) { - onArgsF(compiler.commandArguments(sources.toArray)) - call("xsbt.CompilerInterface", "run", log)( - classOf[Array[File]], classOf[DependencyChanges], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter], classOf[CompileProgress], classOf[CachedCompiler])( - sources.toArray, changes, callback, log, reporter, progress, compiler) - } - def newCachedCompiler(arguments: Array[String], output: Output, log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler = - newCachedCompiler(arguments: Seq[String], output, log, reporter, resident) - - def newCachedCompiler(arguments: Seq[String], output: Output, log: xLogger, reporter: Reporter, resident: Boolean): CachedCompiler = - { - call("xsbt.CompilerInterface", "newCompiler", log)( - classOf[Array[String]], classOf[Output], classOf[xLogger], classOf[Reporter], classOf[Boolean])( - arguments.toArray[String]: Array[String], output, log, reporter, resident: java.lang.Boolean). - asInstanceOf[CachedCompiler] - } - - def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger): Unit = - doc(sources, classpath, outputDirectory, options, log, new LoggerReporter(maximumErrors, log)) - def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger, reporter: Reporter): Unit = - { - val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, Some(outputDirectory), options) - onArgsF(arguments) - call("xsbt.ScaladocInterface", "run", log)(classOf[Array[String]], classOf[xLogger], classOf[Reporter])( - arguments.toArray[String]: Array[String], log, reporter) - } - def console(classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String, log: Logger)(loader: Option[ClassLoader] = None, bindings: Seq[(String, Any)] = Nil): Unit = - { - onArgsF(consoleCommandArguments(classpath, options, log)) - val (classpathString, bootClasspath) = consoleClasspaths(classpath) - val (names, values) = bindings.unzip - call("xsbt.ConsoleInterface", "run", log)( - classOf[Array[String]], classOf[String], classOf[String], classOf[String], classOf[String], classOf[ClassLoader], classOf[Array[String]], classOf[Array[Any]], classOf[xLogger])( - options.toArray[String]: Array[String], bootClasspath, classpathString, initialCommands, cleanupCommands, loader.orNull, names.toArray[String], values.toArray[Any], log) - } - - private[this] def consoleClasspaths(classpath: Seq[File]): (String, String) = - { - val arguments = new CompilerArguments(scalaInstance, cp) - val classpathString = CompilerArguments.absString(arguments.finishClasspath(classpath)) - val bootClasspath = if (cp.autoBoot) arguments.createBootClasspathFor(classpath) else "" - (classpathString, bootClasspath) - } - def consoleCommandArguments(classpath: Seq[File], options: Seq[String], log: Logger): Seq[String] = - { - val (classpathString, bootClasspath) = consoleClasspaths(classpath) - val argsObj = call("xsbt.ConsoleInterface", "commandArguments", log)( - classOf[Array[String]], classOf[String], classOf[String], classOf[xLogger])( - options.toArray[String]: Array[String], bootClasspath, classpathString, log) - argsObj.asInstanceOf[Array[String]].toSeq - } - def force(log: Logger): Unit = provider(scalaInstance, log) - private def call(interfaceClassName: String, methodName: String, log: Logger)(argTypes: Class[_]*)(args: AnyRef*): AnyRef = - { - val interfaceClass = getInterfaceClass(interfaceClassName, log) - val interface = interfaceClass.newInstance.asInstanceOf[AnyRef] - val method = interfaceClass.getMethod(methodName, argTypes: _*) - try { method.invoke(interface, args: _*) } - catch { - case e: java.lang.reflect.InvocationTargetException => - e.getCause match { - case c: xsbti.CompileFailed => throw new CompileFailed(c.arguments, c.toString, c.problems) - case t => throw t - } - } - } - private[this] def loader(log: Logger) = - { - val interfaceJar = provider(scalaInstance, log) - // this goes to scalaInstance.loader for scala classes and the loader of this class for xsbti classes - val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader) - new URLClassLoader(Array(interfaceJar.toURI.toURL), dual) - } - private[this] def getInterfaceClass(name: String, log: Logger) = Class.forName(name, true, loader(log)) - protected def createDualLoader(scalaLoader: ClassLoader, sbtLoader: ClassLoader): ClassLoader = - { - val xsbtiFilter = (name: String) => name.startsWith("xsbti.") - val notXsbtiFilter = (name: String) => !xsbtiFilter(name) - new classpath.DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false) - } - override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")" -} -object AnalyzingCompiler { - import sbt.IO.{ copy, createDirectory, zip, jars, unzip, withTemporaryDirectory } - - // Note: The Scala build now depends on some details of this method: - // https://github.com/jsuereth/scala/commit/3431860048df8d2a381fb85a526097e00154eae0 - /** - * Extract sources from source jars, compile them with the xsbti interfaces on the classpath, and package the compiled classes and - * any resources from the source jars into a final jar. - */ - def compileSources(sourceJars: Iterable[File], targetJar: File, xsbtiJars: Iterable[File], id: String, compiler: RawCompiler, log: Logger) { - val isSource = (f: File) => isSourceName(f.getName) - def keepIfSource(files: Set[File]): Set[File] = if (files.exists(isSource)) files else Set() - - withTemporaryDirectory { dir => - val extractedSources = (Set[File]() /: sourceJars) { (extracted, sourceJar) => extracted ++ keepIfSource(unzip(sourceJar, dir)) } - val (sourceFiles, resources) = extractedSources.partition(isSource) - withTemporaryDirectory { outputDirectory => - log.info("'" + id + "' not yet compiled for Scala " + compiler.scalaInstance.actualVersion + ". Compiling...") - val start = System.currentTimeMillis - try { - compiler(sourceFiles.toSeq, compiler.scalaInstance.libraryJar +: (xsbtiJars.toSeq ++ sourceJars), outputDirectory, "-nowarn" :: Nil) - log.info(" Compilation completed in " + (System.currentTimeMillis - start) / 1000.0 + " s") - } catch { case e: xsbti.CompileFailed => throw new CompileFailed(e.arguments, "Error compiling sbt component '" + id + "'", e.problems) } - import sbt.Path._ - copy(resources pair rebase(dir, outputDirectory)) - zip((outputDirectory ***) pair (relativeTo(outputDirectory), false), targetJar) - } - } - } - private def isSourceName(name: String): Boolean = name.endsWith(".scala") || name.endsWith(".java") -} - -private[this] object IgnoreProgress extends CompileProgress { - def startUnit(phase: String, unitPath: String) {} - def advance(current: Int, total: Int) = true -} diff --git a/compile/src/main/scala/sbt/compiler/CompilerArguments.scala b/compile/src/main/scala/sbt/compiler/CompilerArguments.scala deleted file mode 100644 index e383fdb56..000000000 --- a/compile/src/main/scala/sbt/compiler/CompilerArguments.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt -package compiler - -import xsbti.ArtifactInfo -import scala.util -import java.io.File -import CompilerArguments.{ abs, absString, BootClasspathOption } - -/** - * Forms the list of options that is passed to the compiler from the required inputs and other options. - * The directory containing scala-library.jar and scala-compiler.jar (scalaLibDirectory) is required in - * order to add these jars to the boot classpath. The 'scala.home' property must be unset because Scala - * puts jars in that directory on the bootclasspath. Because we use multiple Scala versions, - * this would lead to compiling against the wrong library jar. - */ -final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xsbti.compile.ClasspathOptions) { - def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: Option[File], options: Seq[String]): Seq[String] = - { - checkScalaHomeUnset() - val cpWithCompiler = finishClasspath(classpath) - // Scala compiler's treatment of empty classpath is troublesome (as of 2.9.1). - // We append a random dummy element as workaround. - val dummy = "dummy_" + Integer.toHexString(util.Random.nextInt) - val classpathOption = Seq("-classpath", if (cpWithCompiler.isEmpty) dummy else absString(cpWithCompiler)) - val outputOption = outputDirectory map { out => Seq("-d", out.getAbsolutePath) } getOrElse Seq() - options ++ outputOption ++ bootClasspathOption(hasLibrary(classpath)) ++ classpathOption ++ abs(sources) - } - def finishClasspath(classpath: Seq[File]): Seq[File] = - filterLibrary(classpath) ++ include(cp.compiler, scalaInstance.compilerJar) ++ include(cp.extra, scalaInstance.otherJars: _*) - private[this] def include(flag: Boolean, jars: File*) = if (flag) jars else Nil - private[this] def abs(files: Seq[File]) = files.map(_.getAbsolutePath).sortWith(_ < _) - private[this] def checkScalaHomeUnset(): Unit = { - val scalaHome = System.getProperty("scala.home") - assert((scalaHome eq null) || scalaHome.isEmpty, "'scala.home' should not be set (was " + scalaHome + ")") - } - def createBootClasspathFor(classpath: Seq[File]) = createBootClasspath(hasLibrary(classpath) || cp.compiler || cp.extra) - - /** Add the correct Scala library jar to the boot classpath if `addLibrary` is true.*/ - def createBootClasspath(addLibrary: Boolean) = - { - val originalBoot = System.getProperty("sun.boot.class.path", "") - if (addLibrary) { - val newBootPrefix = if (originalBoot.isEmpty) "" else originalBoot + File.pathSeparator - newBootPrefix + scalaInstance.libraryJar.getAbsolutePath - } else - originalBoot - } - def filterLibrary(classpath: Seq[File]) = if (cp.filterLibrary) classpath filterNot isScalaLibrary else classpath - def hasLibrary(classpath: Seq[File]) = classpath exists isScalaLibrary - private[this] val isScalaLibrary: File => Boolean = file => { - val name = file.getName - (name contains ArtifactInfo.ScalaLibraryID) || file.getName == scalaInstance.libraryJar.getName - } - def bootClasspathOption(addLibrary: Boolean) = if (cp.autoBoot) Seq(BootClasspathOption, createBootClasspath(addLibrary)) else Nil - def bootClasspath(addLibrary: Boolean) = if (cp.autoBoot) IO.parseClasspath(createBootClasspath(addLibrary)) else Nil - def bootClasspathFor(classpath: Seq[File]) = bootClasspath(hasLibrary(classpath)) - - import Path._ - def extClasspath: Seq[File] = (IO.parseClasspath(System.getProperty("java.ext.dirs")) * "*.jar").get -} -object CompilerArguments { - val BootClasspathOption = "-bootclasspath" - def abs(files: Seq[File]): Seq[String] = files.map(_.getAbsolutePath) - def abs(files: Set[File]): Seq[String] = abs(files.toSeq) - def absString(files: Seq[File]): String = abs(files).mkString(File.pathSeparator) - def absString(files: Set[File]): String = absString(files.toSeq) -} diff --git a/compile/src/main/scala/sbt/compiler/CompilerCache.scala b/compile/src/main/scala/sbt/compiler/CompilerCache.scala deleted file mode 100644 index 59e2aae80..000000000 --- a/compile/src/main/scala/sbt/compiler/CompilerCache.scala +++ /dev/null @@ -1,49 +0,0 @@ -package sbt -package compiler - -import xsbti.{ Logger => xLogger, Reporter } -import xsbti.compile.{ CachedCompiler, CachedCompilerProvider, GlobalsCache, Output } -import Logger.f0 -import java.io.File -import java.util.{ LinkedHashMap, Map } - -private final class CompilerCache(val maxInstances: Int) extends GlobalsCache { - private[this] val cache = lru[CompilerKey, CachedCompiler](maxInstances) - private[this] def lru[A, B](max: Int) = new LinkedHashMap[A, B](8, 0.75f, true) { - override def removeEldestEntry(eldest: Map.Entry[A, B]): Boolean = size > max - } - def apply(args: Array[String], output: Output, forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler = synchronized { - val key = CompilerKey(dropSources(args.toList), c.scalaInstance.actualVersion) - if (forceNew) cache.remove(key) - cache.get(key) match { - case null => - log.debug(f0("Compiler cache miss. " + key.toString)) - put(key, c.newCachedCompiler(args, output, log, reporter, /* resident = */ !forceNew)) - case cc => - log.debug(f0("Compiler cache hit (" + cc.hashCode.toHexString + "). " + key.toString)) - cc - } - } - def clear(): Unit = synchronized { cache.clear() } - - private[this] def dropSources(args: Seq[String]): Seq[String] = - args.filterNot(arg => arg.endsWith(".scala") || arg.endsWith(".java")) - - private[this] def put(key: CompilerKey, cc: CachedCompiler): CachedCompiler = - { - cache.put(key, cc) - cc - } - private[this] final case class CompilerKey(args: Seq[String], scalaVersion: String) { - override def toString = "scala " + scalaVersion + ", args: " + args.mkString(" ") - } -} -object CompilerCache { - def apply(maxInstances: Int): GlobalsCache = new CompilerCache(maxInstances) - - val fresh: GlobalsCache = new GlobalsCache { - def clear(): Unit = () - def apply(args: Array[String], output: Output, forceNew: Boolean, c: CachedCompilerProvider, log: xLogger, reporter: Reporter): CachedCompiler = - c.newCachedCompiler(args, output, log, reporter, /*resident = */ false) - } -} diff --git a/compile/src/main/scala/sbt/compiler/CompilerInterfaceProvider.scala b/compile/src/main/scala/sbt/compiler/CompilerInterfaceProvider.scala deleted file mode 100644 index 1beee441c..000000000 --- a/compile/src/main/scala/sbt/compiler/CompilerInterfaceProvider.scala +++ /dev/null @@ -1,13 +0,0 @@ -package sbt -package compiler - -import java.io.File - -trait CompilerInterfaceProvider { - def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File -} -object CompilerInterfaceProvider { - def constant(file: File): CompilerInterfaceProvider = new CompilerInterfaceProvider { - def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = file - } -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/compiler/CompilerOutput.scala b/compile/src/main/scala/sbt/compiler/CompilerOutput.scala deleted file mode 100755 index a69164450..000000000 --- a/compile/src/main/scala/sbt/compiler/CompilerOutput.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2012 Eugene Vigdorchik - */ - -package sbt -package compiler - -import xsbti.compile.{ Output, SingleOutput, MultipleOutput } -import java.io.File - -/** Constructor for the `Output` ADT for incremental compiler. Can either take groups (src -> out) or a single output. */ -object CompileOutput { - def apply(dir: File): Output = new SingleOutput { - def outputDirectory = dir - override def toString = s"SingleOutput($outputDirectory)" - } - - def apply(groups: (File, File)*): Output = new MultipleOutput { - def outputGroups = groups.toArray map { - case (src, out) => new MultipleOutput.OutputGroup { - def sourceDirectory = src - def outputDirectory = out - override def toString = s"OutputGroup($src -> $out)" - } - } - override def toString = s"MultiOutput($outputGroups)" - } -} diff --git a/compile/src/main/scala/sbt/compiler/JavaCompiler.scala b/compile/src/main/scala/sbt/compiler/JavaCompiler.scala deleted file mode 100644 index 1f031848a..000000000 --- a/compile/src/main/scala/sbt/compiler/JavaCompiler.scala +++ /dev/null @@ -1,158 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah, Seth Tisue - */ -package sbt -package compiler - -import java.io.{ File, PrintWriter } - -import xsbti.{ Severity, Reporter } -import xsbti.compile.Output - -@deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") -abstract class JavacContract(val name: String, val clazz: String) { - def exec(args: Array[String], writer: PrintWriter): Int -} -/** An interface we use to call the Java compiler. */ -@deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") -trait JavaCompiler extends xsbti.compile.JavaCompiler { - /** - * Runs the java compiler - * - * @param sources The source files to compile - * @param classpath The classpath for the compiler - * @param outputDirectory The output directory for class files - * @param options The arguments to pass into Javac - * @param log A log in which we write all the output from Javac. - */ - def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger): Unit - - def compile(sources: Array[File], classpath: Array[File], output: xsbti.compile.Output, options: Array[String], log: xsbti.Logger): Unit = { - val outputDirectory = output match { - case single: xsbti.compile.SingleOutput => single.outputDirectory - case _ => throw new RuntimeException("Javac doesn't support multiple output directories") - } - apply(sources, classpath, outputDirectory, options)(log) - } - - // TODO - Fix this so that the reporter is actually used. - def compileWithReporter(sources: Array[File], classpath: Array[File], output: Output, options: Array[String], reporter: Reporter, log: xsbti.Logger): Unit = { - compile(sources, classpath, output, options, log) - } - - def onArgs(f: Seq[String] => Unit): JavaCompiler -} -@deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") -trait Javadoc { - def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger) - - def onArgs(f: Seq[String] => Unit): Javadoc -} -@deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") -trait JavaTool extends Javadoc with JavaCompiler { - def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger) = - compile(JavaCompiler.javac, sources, classpath, outputDirectory, options)(log) - - def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger) = - compile(JavaCompiler.javadoc, sources, classpath, outputDirectory, options)(log) - - def compile(contract: JavacContract, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger): Unit - - def onArgs(f: Seq[String] => Unit): JavaTool -} -@deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") -object JavaCompiler { - @deprecated("Please use the new set of compilers in sbt.compilers.javac", "0.13.8") - type Fork = (JavacContract, Seq[String], Logger) => Int - - val javac = new JavacContract("javac", "com.sun.tools.javac.Main") { - def exec(args: Array[String], writer: PrintWriter) = { - val m = Class.forName(clazz).getDeclaredMethod("compile", classOf[Array[String]], classOf[PrintWriter]) - m.invoke(null, args, writer).asInstanceOf[java.lang.Integer].intValue - } - } - val javadoc = new JavacContract("javadoc", "com.sun.tools.javadoc.Main") { - def exec(args: Array[String], writer: PrintWriter) = { - val m = Class.forName(clazz).getDeclaredMethod("execute", classOf[String], classOf[PrintWriter], classOf[PrintWriter], classOf[PrintWriter], classOf[String], classOf[Array[String]]) - m.invoke(null, name, writer, writer, writer, "com.sun.tools.doclets.standard.Standard", args).asInstanceOf[java.lang.Integer].intValue - } - } - - def construct(f: Fork, cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaTool = new JavaTool0(f, cp, scalaInstance, _ => ()) - - /** The actual implementation of a JavaTool (javadoc + javac). */ - private[this] class JavaTool0(f: Fork, cp: ClasspathOptions, scalaInstance: ScalaInstance, onArgsF: Seq[String] => Unit) extends JavaTool { - def onArgs(g: Seq[String] => Unit): JavaTool = new JavaTool0(f, cp, scalaInstance, g) - def commandArguments(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger): Seq[String] = - { - val augmentedClasspath = if (cp.autoBoot) classpath ++ Seq(scalaInstance.libraryJar) else classpath - val javaCp = ClasspathOptions.javac(cp.compiler) - (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, Some(outputDirectory), options) - } - def compile(contract: JavacContract, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger): Unit = { - val arguments = commandArguments(sources, classpath, outputDirectory, options, log) - onArgsF(arguments) - val code: Int = f(contract, arguments, log) - log.debug(contract.name + " returned exit code: " + code) - if (code != 0) throw new CompileFailed(arguments.toArray, contract.name + " returned nonzero exit code", Array()) - } - } - def directOrFork(cp: ClasspathOptions, scalaInstance: ScalaInstance)(implicit doFork: Fork): JavaTool = - construct(directOrForkJavac, cp, scalaInstance) - - def direct(cp: ClasspathOptions, scalaInstance: ScalaInstance): JavaTool = - construct(directJavac, cp, scalaInstance) - - def fork(cp: ClasspathOptions, scalaInstance: ScalaInstance)(implicit doFork: Fork): JavaTool = - construct(forkJavac, cp, scalaInstance) - - def directOrForkJavac(implicit doFork: Fork) = (contract: JavacContract, arguments: Seq[String], log: Logger) => - try { directJavac(contract, arguments, log) } - catch { - case e @ (_: ClassNotFoundException | _: NoSuchMethodException) => - log.debug(contract.clazz + " not found with appropriate method signature; forking " + contract.name + " instead") - forkJavac(doFork)(contract, arguments, log) - } - - /** `doFork` should be a function that forks javac with the provided arguments and sends output to the given Logger.*/ - def forkJavac(implicit doFork: Fork) = (contract: JavacContract, arguments: Seq[String], log: Logger) => - { - val (jArgs, nonJArgs) = arguments.partition(_.startsWith("-J")) - def externalJavac(argFile: File) = - doFork(contract, jArgs :+ ("@" + normalizeSlash(argFile.getAbsolutePath)), log) - withArgumentFile(nonJArgs)(externalJavac) - } - val directJavac = (contract: JavacContract, arguments: Seq[String], log: Logger) => - { - val logger = new LoggerWriter(log) - val writer = new PrintWriter(logger) - val argsArray = arguments.toArray - log.debug("Attempting to call " + contract.name + " directly...") - - var exitCode = -1 - try { exitCode = contract.exec(argsArray, writer) } - finally { logger.flushLines(if (exitCode == 0) Level.Warn else Level.Error) } - exitCode - } - - /** - * Helper method to create an argument file that we pass to Javac. Gets over the windows - * command line length limitation. - * @param args The string arguments to pass to Javac. - * @param f A function which is passed the arg file. - * @tparam T The return type. - * @return The result of using the argument file. - */ - def withArgumentFile[T](args: Seq[String])(f: File => T): T = - { - import IO.{ Newline, withTemporaryDirectory, write } - withTemporaryDirectory { tmp => - val argFile = new File(tmp, "argfile") - write(argFile, args.map(escapeSpaces).mkString(Newline)) - f(argFile) - } - } - // javac's argument file seems to allow naive space escaping with quotes. escaping a quote with a backslash does not work - def escapeSpaces(s: String): String = '\"' + normalizeSlash(s) + '\"' - def normalizeSlash(s: String) = s.replace(File.separatorChar, '/') -} diff --git a/compile/src/main/scala/sbt/compiler/RawCompiler.scala b/compile/src/main/scala/sbt/compiler/RawCompiler.scala deleted file mode 100644 index 74f89a7e1..000000000 --- a/compile/src/main/scala/sbt/compiler/RawCompiler.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt -package compiler - -import java.io.File - -/** - * A basic interface to the compiler. It is called in the same virtual machine, but no dependency analysis is done. This - * is used, for example, to compile the interface/plugin code. - * If `explicitClasspath` is true, the bootclasspath and classpath are not augmented. If it is false, - * the scala-library.jar from `scalaInstance` is put on bootclasspath and the scala-compiler jar goes on the classpath. - */ -class RawCompiler(val scalaInstance: xsbti.compile.ScalaInstance, cp: ClasspathOptions, log: Logger) { - def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String]): Unit = { - // reflection is required for binary compatibility - // The following import ensures there is a compile error if the identifiers change, - // but should not be otherwise directly referenced - import scala.tools.nsc.Main.{ process => _ } - - val arguments = compilerArguments(sources, classpath, Some(outputDirectory), options) - log.debug("Plain interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", "")) - val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader) - val process = mainClass.getMethod("process", classOf[Array[String]]) - process.invoke(null, arguments.toArray) - checkForFailure(mainClass, arguments.toArray) - } - def compilerArguments = new CompilerArguments(scalaInstance, cp) - protected def checkForFailure(mainClass: Class[_], args: Array[String]): Unit = { - val reporter = mainClass.getMethod("reporter").invoke(null) - val failed = reporter.getClass.getMethod("hasErrors").invoke(reporter).asInstanceOf[Boolean] - if (failed) throw new CompileFailed(args, "Plain compile failed", Array()) - } -} -class CompileFailed(val arguments: Array[String], override val toString: String, val problems: Array[xsbti.Problem]) extends xsbti.CompileFailed with FeedbackProvidedException diff --git a/compile/src/main/scala/sbt/compiler/javac/DiagnosticsReporter.scala b/compile/src/main/scala/sbt/compiler/javac/DiagnosticsReporter.scala deleted file mode 100644 index 49c3fb54a..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/DiagnosticsReporter.scala +++ /dev/null @@ -1,114 +0,0 @@ -package sbt.compiler.javac - -import java.io.File -import javax.tools.{ Diagnostic, JavaFileObject, DiagnosticListener } - -import sbt.Logger -import xsbti.{ Severity, Reporter, Maybe } -import javax.tools.Diagnostic.NOPOS - -/** - * A diagnostics listener that feeds all messages into the given reporter. - * @param reporter - */ -final class DiagnosticsReporter(reporter: Reporter) extends DiagnosticListener[JavaFileObject] { - val END_OF_LINE_MATCHER = "(\r\n)|[\r]|[\n]" - val EOL = System.getProperty("line.separator") - private def fixedDiagnosticMessage(d: Diagnostic[_ <: JavaFileObject]): String = { - def getRawMessage = d.getMessage(null) - def fixWarnOrErrorMessage = { - val tmp = getRawMessage - // we fragment off the line/source/type report from the message. - // NOTE - End of line handling may be off. - val lines: Seq[String] = - tmp.split(END_OF_LINE_MATCHER) match { - case Array(head, tail @ _*) => - val newHead = head.split(":").last - newHead +: tail - case Array(head) => - head.split(":").last :: Nil - case Array() => Seq.empty[String] - } - lines.mkString(EOL) - } - d.getKind match { - case Diagnostic.Kind.ERROR | Diagnostic.Kind.WARNING | Diagnostic.Kind.MANDATORY_WARNING => fixWarnOrErrorMessage - case _ => getRawMessage - } - } - private def fixSource[T <: JavaFileObject](source: T): Option[String] = { - try Option(source).map(_.toUri.normalize).map(new File(_)).map(_.getAbsolutePath) - catch { - case t: IllegalArgumentException => - // Oracle JDK6 has a super dumb notion of what a URI is. In fact, it's not even a legimitate URL, but a dump - // of the filename in a "I hope this works to toString it" kind of way. This appears to work in practice - // but we may need to re-evaluate. - Option(source).map(_.toUri.toString) - } - } - override def report(d: Diagnostic[_ <: JavaFileObject]): Unit = { - val severity = - d.getKind match { - case Diagnostic.Kind.ERROR => Severity.Error - case Diagnostic.Kind.WARNING | Diagnostic.Kind.MANDATORY_WARNING => Severity.Warn - case _ => Severity.Info - } - val msg = fixedDiagnosticMessage(d) - val pos: xsbti.Position = - new xsbti.Position { - // https://docs.oracle.com/javase/7/docs/api/javax/tools/Diagnostic.html - // Negative values (except NOPOS) and 0 are not valid line or column numbers. - private[this] def checkNoPos(n: Long): Option[Long] = - n match { - case NOPOS => None - case x if x <= 0 => sys.error(s"Invalid position: $x") - case x => Option(x) - } - - override val line: Maybe[Integer] = Logger.o2m(checkNoPos(d.getLineNumber) map { x => new Integer(x.toInt) }) - def startPosition: Option[Long] = checkNoPos(d.getStartPosition) - def endPosition: Option[Long] = checkNoPos(d.getEndPosition) - override val offset: Maybe[Integer] = Logger.o2m(checkNoPos(d.getPosition) map { x => new Integer(x.toInt) }) - override def lineContent: String = { - def getDiagnosticLine: Option[String] = - try { - // See com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper - val diagnostic = d.getClass.getField("d").get(d) - // See com.sun.tools.javac.util.JCDiagnostic#getDiagnosticSource - val getDiagnosticSourceMethod = diagnostic.getClass.getDeclaredMethod("getDiagnosticSource") - Option(getDiagnosticSourceMethod.invoke(diagnostic)) match { - case Some(diagnosticSource) => - // See com.sun.tools.javac.util.DiagnosticSource - val getLineMethod = diagnosticSource.getClass.getMethod("getLine", Integer.TYPE) - Option(getLineMethod.invoke(diagnosticSource, line.get())).map(_.toString) - case _ => None - } - } catch { - // TODO - catch ReflectiveOperationException once sbt is migrated to JDK7 - case ignored: Throwable => None - } - - def getExpression: String = - Option(d.getSource) match { - case Some(source: JavaFileObject) => - (Option(source.getCharContent(true)), startPosition, endPosition) match { - case (Some(cc), Some(start), Some(end)) => cc.subSequence(start.toInt, end.toInt).toString - case _ => "" - } - case _ => "" - } - - getDiagnosticLine.getOrElse(getExpression) - } - private val sourceUri = fixSource(d.getSource) - override val sourcePath = Logger.o2m(sourceUri) - override val sourceFile = Logger.o2m(sourceUri.map(new File(_))) - override val pointer = Logger.o2m(Option.empty[Integer]) - override val pointerSpace = Logger.o2m(Option.empty[String]) - override def toString = - if (sourceUri.isDefined) s"${sourceUri.get}:${if (line.isDefined) line.get else -1}" - else "" - } - reporter.log(pos, msg, severity) - } -} diff --git a/compile/src/main/scala/sbt/compiler/javac/ForkedJava.scala b/compile/src/main/scala/sbt/compiler/javac/ForkedJava.scala deleted file mode 100644 index 4797d08e8..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/ForkedJava.scala +++ /dev/null @@ -1,73 +0,0 @@ -package sbt.compiler.javac - -import java.io.File - -import sbt.IO._ -import sbt.{ IO, Process, Logger } -import xsbti.Reporter -import xsbti.compile.{ ClasspathOptions, ScalaInstance } - -/** Helper methods for running the java toolchain by forking. */ -object ForkedJava { - /** Helper method to launch programs. */ - private[javac] def launch(javaHome: Option[File], program: String, sources: Seq[File], options: Seq[String], log: Logger, reporter: Reporter): Boolean = { - val (jArgs, nonJArgs) = options.partition(_.startsWith("-J")) - val allArguments = nonJArgs ++ sources.map(_.getAbsolutePath) - - withArgumentFile(allArguments) { argsFile => - val forkArgs = jArgs :+ s"@${normalizeSlash(argsFile.getAbsolutePath)}" - val exe = getJavaExecutable(javaHome, program) - val cwd = new File(new File(".").getAbsolutePath).getCanonicalFile - val javacLogger = new JavacLogger(log, reporter, cwd) - var exitCode = -1 - try { - exitCode = Process(exe +: forkArgs, cwd) ! javacLogger - } finally { - javacLogger.flush(exitCode) - } - // We return true or false, depending on success. - exitCode == 0 - } - } - - /** - * Helper method to create an argument file that we pass to Javac. Gets over the windows - * command line length limitation. - * @param args The string arguments to pass to Javac. - * @param f A function which is passed the arg file. - * @tparam T The return type. - * @return The result of using the argument file. - */ - def withArgumentFile[T](args: Seq[String])(f: File => T): T = - { - import IO.{ Newline, withTemporaryDirectory, write } - withTemporaryDirectory { tmp => - val argFile = new File(tmp, "argfile") - write(argFile, args.map(escapeSpaces).mkString(Newline)) - f(argFile) - } - } - // javac's argument file seems to allow naive space escaping with quotes. escaping a quote with a backslash does not work - private def escapeSpaces(s: String): String = '\"' + normalizeSlash(s) + '\"' - private def normalizeSlash(s: String) = s.replace(File.separatorChar, '/') - - import sbt.Path._ - /** create the executable name for java */ - private[javac] def getJavaExecutable(javaHome: Option[File], name: String): String = - javaHome match { - case None => name - case Some(jh) => - // TODO - Was there any hackery for windows before? - (jh / "bin" / name).getAbsolutePath - } -} - -/** An implementation of compiling java which forks a Javac instance. */ -final class ForkedJavaCompiler(javaHome: Option[File]) extends JavaCompiler { - def run(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - ForkedJava.launch(javaHome, "javac", sources, options, log, reporter) -} -final class ForkedJavadoc(javaHome: Option[File]) extends Javadoc { - def run(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - ForkedJava.launch(javaHome, "javadoc", sources, options, log, reporter) -} diff --git a/compile/src/main/scala/sbt/compiler/javac/JavaCompiler.scala b/compile/src/main/scala/sbt/compiler/javac/JavaCompiler.scala deleted file mode 100644 index 8ea1be070..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/JavaCompiler.scala +++ /dev/null @@ -1,151 +0,0 @@ -package sbt.compiler.javac - -import sbt.ClasspathOptions -import sbt.{ ClasspathOptions => _, _ } -import sbt.compiler._ -import java.io.{ PrintWriter, File } - -import javax.tools.{ DiagnosticListener, Diagnostic, JavaFileObject, DiagnosticCollector } -import xsbti.compile.ScalaInstance -import xsbti.compile._ -import xsbti.{ Severity, Reporter } - -/** - * An interface to the toolchain of Java. - * - * Specifically, access to run javadoc + javac. - */ -sealed trait JavaTools { - /** The raw interface of the java compiler for direct access. */ - def compiler: JavaTool - /** - * This will run a java compiler. - * - * - * @param sources The list of java source files to compile. - * @param options The set of options to pass to the java compiler (includes the classpath). - * @param log The logger to dump output into. - * @param reporter The reporter for semantic error messages. - * @return true if no errors, false otherwise. - */ - def compile(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean - /** - * This will run a java compiler. - * - * - * @param sources The list of java source files to compile. - * @param options The set of options to pass to the java compiler (includes the classpath). - * @param log The logger to dump output into. - * @param reporter The reporter for semantic error messages. - * @return true if no errors, false otherwise. - */ - def doc(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean -} - -/** - * An extension of the JavaTools trait that also includes interfaces specific to running - * the java compiler inside of the incremental comppiler. - */ -sealed trait IncrementalCompilerJavaTools extends JavaTools { - /** An instance of the java Compiler for use with incremental compilation. */ - def xsbtiCompiler: xsbti.compile.JavaCompiler -} -/** Factory methods for getting a java toolchain. */ -object JavaTools { - /** Create a new aggregate tool from existing tools. */ - def apply(c: JavaCompiler, docgen: Javadoc): JavaTools = - new JavaTools { - override def compiler = c - def compile(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - c.run(sources, options) - def doc(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - docgen.run(sources, options) - } - - /** - * Constructs a new set of java toolchain for incremental compilation. - * - * @param instance - * The scalaInstance being used in this incremental compile. Used if we need to append - * scala to the classpath (yeah.... the classpath doesn't already have it). - * @param cpOptions - * Classpath options configured for this incremental compiler. Basically, should we append scala or not. - * @param javaHome - * If this is defined, the location where we should look for javac when we run. - * @return - * A new set of the Java toolchain that also includes and instance of xsbti.compile.JavaCompiler - */ - def directOrFork(instance: xsbti.compile.ScalaInstance, cpOptions: xsbti.compile.ClasspathOptions, javaHome: Option[File]): IncrementalCompilerJavaTools = { - val (compiler, doc) = javaHome match { - case Some(_) => (JavaCompiler.fork(javaHome), Javadoc.fork(javaHome)) - case _ => - val c = JavaCompiler.local.getOrElse(JavaCompiler.fork(None)) - val d = Javadoc.local.getOrElse(Javadoc.fork()) - (c, d) - } - val delegate = apply(compiler, doc) - new IncrementalCompilerJavaTools { - val xsbtiCompiler = new JavaCompilerAdapter(delegate.compiler, instance, cpOptions) - def compiler = delegate.compiler - def compile(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - delegate.compile(sources, options) - def doc(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = - delegate.doc(sources, options) - } - } -} - -/** - * An interface for on of the tools in the java tool chain. - * - * We assume the following is true of tools: - * - The all take sources and options and log error messages - * - They return success or failure. - */ -sealed trait JavaTool { - /** - * This will run a java compiler / or other like tool (e.g. javadoc). - * - * - * @param sources The list of java source files to compile. - * @param options The set of options to pass to the java compiler (includes the classpath). - * @param log The logger to dump output into. - * @param reporter The reporter for semantic error messages. - * @return true if no errors, false otherwise. - */ - def run(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean -} - -/** Interface we use to compile java code. This is mostly a tag over the raw JavaTool interface. */ -trait JavaCompiler extends JavaTool {} -/** Factory methods for constructing a java compiler. */ -object JavaCompiler { - /** Returns a local compiler, if the current runtime supports it. */ - def local: Option[JavaCompiler] = - for { - compiler <- Option(javax.tools.ToolProvider.getSystemJavaCompiler) - } yield new LocalJavaCompiler(compiler) - - /** Returns a local compiler that will fork javac when needed. */ - def fork(javaHome: Option[File] = None): JavaCompiler = - new ForkedJavaCompiler(javaHome) - -} - -/** Interface we use to document java code. This is a tag over the raw JavaTool interface. */ -trait Javadoc extends JavaTool {} -/** Factory methods for constructing a javadoc. */ -object Javadoc { - /** Returns a local compiler, if the current runtime supports it. */ - def local: Option[Javadoc] = - // TODO - javax doc tool not supported in JDK6 - //Option(javax.tools.ToolProvider.getSystemDocumentationTool) - if (LocalJava.hasLocalJavadoc) Some(new LocalJavadoc) - else None - - /** Returns a local compiler that will fork javac when needed. */ - def fork(javaHome: Option[File] = None): Javadoc = - new ForkedJavadoc(javaHome) - -} - diff --git a/compile/src/main/scala/sbt/compiler/javac/JavaCompilerAdapter.scala b/compile/src/main/scala/sbt/compiler/javac/JavaCompilerAdapter.scala deleted file mode 100644 index 7ee67543a..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/JavaCompilerAdapter.scala +++ /dev/null @@ -1,44 +0,0 @@ -package sbt.compiler.javac - -import java.io.File - -import sbt.compiler.{ CompileFailed, CompilerArguments } -import sbt.{ ClasspathOptions, Logger, LoggerReporter } -import xsbti.Reporter -import xsbti.compile.{ MultipleOutput, SingleOutput, Output } - -/** - * This class adapts the new java compiler with the classpath/argument option hackery needed to handle scala. - * - * The xsbti.Compiler interface is used by the IncrementalCompiler classes, so this lets us adapt a more generic - * wrapper around running Javac (forked or direct) into the interfaces used by incremental compiler. - * - */ -class JavaCompilerAdapter(delegate: JavaTool, scalaInstance: xsbti.compile.ScalaInstance, cpOptions: xsbti.compile.ClasspathOptions) extends xsbti.compile.JavaCompiler { - override final def compile(sources: Array[File], classpath: Array[File], output: Output, options: Array[String], log: xsbti.Logger): Unit = { - // TODO - 5 max errors ok? We're not expecting this code path to be called, ever. This is only for clients who try to use the xsbti.compile.JavaCompiler interface - // outside of the incremental compiler, for some reason. - val reporter = new LoggerReporter(5, log) - compileWithReporter(sources, classpath, output, options, reporter, log) - } - override final def compileWithReporter(sources: Array[File], classpath: Array[File], output: Output, options: Array[String], reporter: Reporter, log: xsbti.Logger): Unit = { - val target = output match { - case so: SingleOutput => so.outputDirectory - case mo: MultipleOutput => throw new RuntimeException("Javac doesn't support multiple output directories") - } - val args = commandArguments(Seq(), classpath, target, options, log) - // We sort the sources for deterministic results. - val success = delegate.run(sources.sortBy(_.getAbsolutePath), args)(log, reporter) - if (!success) { - // TODO - Will the reporter have problems from Scalac? It appears like it does not, only from the most recent run. - // This is because the incremental compiler will not run javac if scalac fails. - throw new CompileFailed(args.toArray, "javac returned nonzero exit code", reporter.problems()) - } - } - private[this] def commandArguments(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger): Seq[String] = - { - val augmentedClasspath = if (cpOptions.autoBoot) classpath ++ Seq(scalaInstance.libraryJar) else classpath - val javaCp = ClasspathOptions.javac(cpOptions.compiler) - (new CompilerArguments(scalaInstance, javaCp))(sources, augmentedClasspath, Some(outputDirectory), options) - } -} diff --git a/compile/src/main/scala/sbt/compiler/javac/JavaErrorParser.scala b/compile/src/main/scala/sbt/compiler/javac/JavaErrorParser.scala deleted file mode 100644 index 69218f159..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/JavaErrorParser.scala +++ /dev/null @@ -1,199 +0,0 @@ -package sbt.compiler.javac - -import java.io.File - -import sbt.Logger.o2m -import xsbti.{ Problem, Severity, Maybe, Position } - -/** A wrapper around xsbti.Position so we can pass in Java input. */ -final case class JavaPosition(_sourceFilePath: String, _line: Int, _contents: String) extends Position { - def line: Maybe[Integer] = o2m(Option(Integer.valueOf(_line))) - def lineContent: String = _contents - def offset: Maybe[Integer] = o2m(None) - def pointer: Maybe[Integer] = o2m(None) - def pointerSpace: Maybe[String] = o2m(None) - def sourcePath: Maybe[String] = o2m(Option(_sourceFilePath)) - def sourceFile: Maybe[File] = o2m(Option(new File(_sourceFilePath))) - override def toString = s"${_sourceFilePath}:${_line}" -} - -/** A position which has no information, because there is none. */ -object JavaNoPosition extends Position { - def line: Maybe[Integer] = o2m(None) - def lineContent: String = "" - def offset: Maybe[Integer] = o2m(None) - def pointer: Maybe[Integer] = o2m(None) - def pointerSpace: Maybe[String] = o2m(None) - def sourcePath: Maybe[String] = o2m(None) - def sourceFile: Maybe[File] = o2m(None) - override def toString = "NoPosition" -} - -/** A wrapper around xsbti.Problem with java-specific options. */ -final case class JavaProblem(position: Position, severity: Severity, message: String) extends xsbti.Problem { - override def category: String = "javac" // TODO - what is this even supposed to be? For now it appears unused. - override def toString = s"$severity @ $position - $message" -} - -/** A parser that is able to parse java's error output successfully. */ -class JavaErrorParser(relativeDir: File = new File(new File(".").getAbsolutePath).getCanonicalFile) extends util.parsing.combinator.RegexParsers { - // Here we track special handlers to catch "Note:" and "Warning:" lines. - private val NOTE_LINE_PREFIXES = Array("Note: ", "\u6ce8: ", "\u6ce8\u610f\uff1a ") - private val WARNING_PREFIXES = Array("warning", "\u8b66\u544a", "\u8b66\u544a\uff1a") - private val END_OF_LINE = System.getProperty("line.separator") - - override val skipWhitespace = false - - val CHARAT: Parser[String] = literal("^") - val SEMICOLON: Parser[String] = literal(":") | literal("\uff1a") - val SYMBOL: Parser[String] = allUntilChar(':') // We ignore whether it actually says "symbol" for i18n - val LOCATION: Parser[String] = allUntilChar(':') // We ignore whether it actually says "location" for i18n. - val WARNING: Parser[String] = allUntilChar(':') ^? { - case x if WARNING_PREFIXES.exists(x.trim.startsWith) => x - } - // Parses the rest of an input line. - val restOfLine: Parser[String] = - // TODO - Can we use END_OF_LINE here without issues? - allUntilChars(Array('\n', '\r')) ~ "[\r]?[\n]?".r ^^ { - case msg ~ _ => msg - } - val NOTE: Parser[String] = restOfLine ^? { - case x if NOTE_LINE_PREFIXES exists x.startsWith => x - } - - // Parses ALL characters until an expected character is met. - def allUntilChar(c: Char): Parser[String] = allUntilChars(Array(c)) - def allUntilChars(chars: Array[Char]): Parser[String] = new Parser[String] { - def isStopChar(c: Char): Boolean = { - var i = 0 - while (i < chars.length) { - if (c == chars(i)) return true - i += 1 - } - false - } - - def apply(in: Input) = { - val source = in.source - val offset = in.offset - val start = handleWhiteSpace(source, offset) - var i = start - while (i < source.length && !isStopChar(source.charAt(i))) { - i += 1 - } - Success(source.subSequence(start, i).toString, in.drop(i - offset)) - } - } - - // Helper to extract an integer from a string - private object ParsedInteger { - def unapply(s: String): Option[Int] = try Some(Integer.parseInt(s)) catch { case e: NumberFormatException => None } - } - // Parses a line number - val line: Parser[Int] = allUntilChar(':') ^? { - case ParsedInteger(x) => x - } - - // Parses the file + lineno output of javac. - val fileAndLineNo: Parser[(String, Int)] = { - val linuxFile = allUntilChar(':') ^^ { _.trim() } - val windowsRootFile = linuxFile ~ SEMICOLON ~ linuxFile ^^ { case root ~ _ ~ path => s"$root:$path" } - val linuxOption = linuxFile ~ SEMICOLON ~ line ^^ { case f ~ _ ~ l => (f, l) } - val windowsOption = windowsRootFile ~ SEMICOLON ~ line ^^ { case f ~ _ ~ l => (f, l) } - (linuxOption | windowsOption) - } - - val allUntilCharat: Parser[String] = allUntilChar('^') - - // Helper method to try to handle relative vs. absolute file pathing.... - // NOTE - this is probably wrong... - private def findFileSource(f: String): String = { - // If a file looks like an absolute path, leave it as is. - def isAbsolute(f: String) = - (f startsWith "/") || (f matches """[^\\]+:\\.*""") - // TODO - we used to use existence checks, that may be the right way to go - if (isAbsolute(f)) f - else (new File(relativeDir, f)).getAbsolutePath - } - - /** Parses an error message (not this WILL parse warning messages as error messages if used incorrectly. */ - val errorMessage: Parser[Problem] = { - val fileLineMessage = fileAndLineNo ~ SEMICOLON ~ restOfLine ^^ { - case (file, line) ~ _ ~ msg => (file, line, msg) - } - fileLineMessage ~ allUntilCharat ~ restOfLine ^^ { - case (file, line, msg) ~ contents ~ _ => - new JavaProblem( - new JavaPosition( - findFileSource(file), - line, - contents + '^' // TODO - Actually parse charat position out of here. - ), - Severity.Error, - msg - ) - } - } - - /** Parses javac warning messages. */ - val warningMessage: Parser[Problem] = { - val fileLineMessage = fileAndLineNo ~ SEMICOLON ~ WARNING ~ SEMICOLON ~ restOfLine ^^ { - case (file, line) ~ _ ~ _ ~ _ ~ msg => (file, line, msg) - } - fileLineMessage ~ allUntilCharat ~ restOfLine ^^ { - case (file, line, msg) ~ contents ~ _ => - new JavaProblem( - new JavaPosition( - findFileSource(file), - line, - contents + "^" - ), - Severity.Warn, - msg - ) - } - } - val noteMessage: Parser[Problem] = - NOTE ^^ { msg => - new JavaProblem( - JavaNoPosition, - Severity.Info, - msg - ) - } - - val potentialProblem: Parser[Problem] = warningMessage | errorMessage | noteMessage - - val javacOutput: Parser[Seq[Problem]] = rep(potentialProblem) - /** - * Example: - * - * Test.java:4: cannot find symbol - * symbol : method baz() - * location: class Foo - * return baz(); - * ^ - * - * Test.java:8: warning: [deprecation] RMISecurityException(java.lang.String) in java.rmi.RMISecurityException has been deprecated - * throw new java.rmi.RMISecurityException("O NOES"); - * ^ - */ - - final def parseProblems(in: String, logger: sbt.Logger): Seq[Problem] = - parse(javacOutput, in) match { - case Success(result, _) => result - case Failure(msg, n) => - logger.warn("Unexpected javac output at:${n.pos.longString}. Please report to sbt-dev@googlegroups.com.") - Seq.empty - case Error(msg, n) => - logger.warn("Unexpected javac output at:${n.pos.longString}. Please report to sbt-dev@googlegroups.com.") - Seq.empty - } - -} - -object JavaErrorParser { - def main(args: Array[String]): Unit = { - - } -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/compiler/javac/JavacProcessLogger.scala b/compile/src/main/scala/sbt/compiler/javac/JavacProcessLogger.scala deleted file mode 100644 index 86c7920b1..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/JavacProcessLogger.scala +++ /dev/null @@ -1,62 +0,0 @@ -package sbt -package compiler -package javac - -import java.util.StringTokenizer - -import xsbti._ -import java.io.File - -/** - * An adapted process logger which can feed semantic error events from Javac as well as just - * dump logs. - * - * - * @param log The logger where all input will go. - * @param reporter A reporter for semantic Javac error messages. - * @param cwd The current working directory of the Javac process, used when parsing Filenames. - */ -final class JavacLogger(log: sbt.Logger, reporter: Reporter, cwd: File) extends ProcessLogger { - import scala.collection.mutable.ListBuffer - import Level.{ Info, Warn, Error, Value => LogLevel } - - private val msgs: ListBuffer[(LogLevel, String)] = new ListBuffer() - - def info(s: => String): Unit = - synchronized { msgs += ((Info, s)) } - - def error(s: => String): Unit = - synchronized { msgs += ((Error, s)) } - - def buffer[T](f: => T): T = f - - private def print(desiredLevel: LogLevel)(t: (LogLevel, String)) = t match { - case (Info, msg) => log.info(msg) - case (Error, msg) => log.log(desiredLevel, msg) - } - - // Helper method to dump all semantic errors. - private def parseAndDumpSemanticErrors(): Unit = { - val input = - msgs collect { - case (Error, msg) => msg - } mkString "\n" - val parser = new JavaErrorParser(cwd) - parser.parseProblems(input, log) foreach { e => - reporter.log(e.position, e.message, e.severity) - } - } - - def flush(exitCode: Int): Unit = { - parseAndDumpSemanticErrors() - val level = if (exitCode == 0) Warn else Error - // Here we only display things that wouldn't otherwise be output by the error reporter. - // TODO - NOTES may not be displayed correctly! - msgs collect { - case (Info, msg) => msg - } foreach { msg => - log.info(msg) - } - msgs.clear() - } -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/compiler/javac/LocalJava.scala b/compile/src/main/scala/sbt/compiler/javac/LocalJava.scala deleted file mode 100644 index 81f4e1293..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/LocalJava.scala +++ /dev/null @@ -1,71 +0,0 @@ -package sbt.compiler.javac - -import java.io.{ File, PrintWriter } - -import sbt.{ LoggerWriter, Level, Logger } -import xsbti.Reporter -import xsbti.compile.{ ScalaInstance, ClasspathOptions } - -/** - * Helper methods for trying to run the java toolchain out of our own classloaders. - */ -object LocalJava { - private[this] val javadocClass = "com.sun.tools.javadoc.Main" - - private[this] def javadocMethod = - try { - Option(Class.forName(javadocClass).getDeclaredMethod("execute", classOf[String], classOf[PrintWriter], classOf[PrintWriter], classOf[PrintWriter], classOf[String], classOf[Array[String]])) - } catch { - case e @ (_: ClassNotFoundException | _: NoSuchMethodException) => None - } - - /** True if we can call a forked Javadoc. */ - def hasLocalJavadoc: Boolean = javadocMethod.isDefined - - /** A mechanism to call the javadoc tool via reflection. */ - private[javac] def unsafeJavadoc(args: Array[String], err: PrintWriter, warn: PrintWriter, notice: PrintWriter): Int = { - javadocMethod match { - case Some(m) => - System.err.println("Running javadoc tool!") - m.invoke(null, "javadoc", err, warn, notice, "com.sun.tools.doclets.standard.Standard", args).asInstanceOf[java.lang.Integer].intValue - case _ => - System.err.println("Unable to reflectively invoke javadoc, cannot find it on the current classloader!") - -1 - } - } -} -/** Implementation of javadoc tool which attempts to run it locally (in-class). */ -final class LocalJavadoc() extends Javadoc { - override def run(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = { - val cwd = new File(new File(".").getAbsolutePath).getCanonicalFile - val (jArgs, nonJArgs) = options.partition(_.startsWith("-J")) - val allArguments = nonJArgs ++ sources.map(_.getAbsolutePath) - val javacLogger = new JavacLogger(log, reporter, cwd) - val warnOrError = new PrintWriter(new ProcessLoggerWriter(javacLogger, Level.Error)) - val infoWriter = new PrintWriter(new ProcessLoggerWriter(javacLogger, Level.Info)) - var exitCode = -1 - try { - exitCode = LocalJava.unsafeJavadoc(allArguments.toArray, warnOrError, warnOrError, infoWriter) - } finally { - warnOrError.close() - infoWriter.close() - javacLogger.flush(exitCode) - } - // We return true or false, depending on success. - exitCode == 0 - } -} - -/** An implementation of compiling java which delegates to the JVM resident java compiler. */ -final class LocalJavaCompiler(compiler: javax.tools.JavaCompiler) extends JavaCompiler { - override def run(sources: Seq[File], options: Seq[String])(implicit log: Logger, reporter: Reporter): Boolean = { - import collection.JavaConverters._ - val logger = new LoggerWriter(log) - val logWriter = new PrintWriter(logger) - log.debug("Attempting to call " + compiler + " directly...") - val diagnostics = new DiagnosticsReporter(reporter) - val fileManager = compiler.getStandardFileManager(diagnostics, null, null) - val jfiles = fileManager.getJavaFileObjectsFromFiles(sources.asJava) - compiler.getTask(logWriter, fileManager, diagnostics, options.asJava, null, jfiles).call() - } -} \ No newline at end of file diff --git a/compile/src/main/scala/sbt/compiler/javac/ProcessLoggerWriter.scala b/compile/src/main/scala/sbt/compiler/javac/ProcessLoggerWriter.scala deleted file mode 100644 index 645ec96ff..000000000 --- a/compile/src/main/scala/sbt/compiler/javac/ProcessLoggerWriter.scala +++ /dev/null @@ -1,34 +0,0 @@ -package sbt.compiler.javac - -import sbt.{ Level, ProcessLogger } - -/** Delegates a stream into a process logger. Mimics LoggerWriter, but for the ProcessLogger interface which differs. */ -private class ProcessLoggerWriter(delegate: ProcessLogger, level: Level.Value, nl: String = System.getProperty("line.separator")) extends java.io.Writer { - private[this] val buffer = new StringBuilder - override def close() = flush() - override def flush(): Unit = - synchronized { - if (buffer.nonEmpty) { - log(buffer.toString) - buffer.clear() - } - } - override def write(content: Array[Char], offset: Int, length: Int): Unit = - synchronized { - buffer.appendAll(content, offset, length) - process() - } - - private[this] def process(): Unit = { - val i = buffer.indexOf(nl) - if (i >= 0) { - log(buffer.substring(0, i)) - buffer.delete(0, i + nl.length) - process() - } - } - private[this] def log(s: String): Unit = level match { - case Level.Warn | Level.Error => delegate.error(s) - case Level.Info => delegate.info(s) - } -} diff --git a/compile/src/test/resources/sbt/compiler/javac/good.java b/compile/src/test/resources/sbt/compiler/javac/good.java deleted file mode 100644 index d4004211a..000000000 --- a/compile/src/test/resources/sbt/compiler/javac/good.java +++ /dev/null @@ -1,7 +0,0 @@ - - -public class good { - public static String test() { - return "Hello"; - } -} \ No newline at end of file diff --git a/compile/src/test/resources/sbt/compiler/javac/hasstaticfinal.java b/compile/src/test/resources/sbt/compiler/javac/hasstaticfinal.java deleted file mode 100644 index 818ca5954..000000000 --- a/compile/src/test/resources/sbt/compiler/javac/hasstaticfinal.java +++ /dev/null @@ -1,4 +0,0 @@ -public class hasstaticfinal { - // the `TYPE` and `VALUE` strings are replaced with various values during tests - public static final TYPE HELLO = VALUE; -} diff --git a/compile/src/test/resources/sbt/compiler/javac/test1.java b/compile/src/test/resources/sbt/compiler/javac/test1.java deleted file mode 100644 index 66da263b5..000000000 --- a/compile/src/test/resources/sbt/compiler/javac/test1.java +++ /dev/null @@ -1,9 +0,0 @@ -import java.rmi.RMISecurityException; - -public class Test { - public NotFound foo() { return 5; } - - public String warning() { - throw new RMISecurityException("O NOES"); - } -} \ No newline at end of file diff --git a/compile/src/test/scala/sbt/compiler/javac/JavaCompilerSpec.scala b/compile/src/test/scala/sbt/compiler/javac/JavaCompilerSpec.scala deleted file mode 100644 index a2bd1141c..000000000 --- a/compile/src/test/scala/sbt/compiler/javac/JavaCompilerSpec.scala +++ /dev/null @@ -1,190 +0,0 @@ -package sbt.compiler.javac - -import java.io.File -import java.net.URLClassLoader - -import sbt._ -import org.specs2.Specification -import org.specs2.matcher.MatchResult -import xsbt.api.{ SameAPI, DefaultShowAPI } -import xsbti.api.SourceAPI -import xsbti.{ Severity, Problem } - -object JavaCompilerSpec extends Specification { - def is = s2""" - - This is a specification for forking + inline-running of the java compiler, and catching Error messages - - - Compiling a java file with local javac should - compile a java file ${works(local)} - issue errors and warnings ${findsErrors(local)} - - Compiling a file with forked javac should - compile a java file ${works(forked)} - issue errors and warnings ${findsErrors(forked)} - yield the same errors as local javac $forkSameAsLocal - - Documenting a file with forked javadoc should - document a java file ${docWorks(forked)} - find errors in a java file ${findsDocErrors(forked)} - - Analyzing classes generated by javac should result in - matching APIs for stable static-final fields ${analyzeStaticDifference("String", "\"A\"", "\"A\"")} - different APIs for static-final fields with changed values ${analyzeStaticDifference("String", "\"A\"", "\"B\"")} - different APIs for static-final fields with changed types ${analyzeStaticDifference("String", "\"1\"", "int", "1")} - "safe" singleton type names ${analyzeStaticDifference("float", "0.123456789f", "0.123456789f")} - """ - - def docWorks(compiler: JavaTools) = IO.withTemporaryDirectory { out => - val (result, problems) = doc(compiler, Seq(knownSampleGoodFile), Seq("-d", out.getAbsolutePath)) - val compiled = result must beTrue - val indexExists = (new File(out, "index.html")).exists must beTrue setMessage ("index.html does not exist!") - val classExists = (new File(out, "good.html")).exists must beTrue setMessage ("good.html does not exist!") - compiled and classExists and indexExists - } - - def works(compiler: JavaTools) = IO.withTemporaryDirectory { out => - val (result, problems) = compile(compiler, Seq(knownSampleGoodFile), Seq("-deprecation", "-d", out.getAbsolutePath)) - val compiled = result must beTrue - val classExists = (new File(out, "good.class")).exists must beTrue - val cl = new URLClassLoader(Array(out.toURI.toURL)) - val clazzz = cl.loadClass("good") - val mthd = clazzz.getDeclaredMethod("test") - val testResult = mthd.invoke(null) - val canRun = mthd.invoke(null) must equalTo("Hello") - compiled and classExists and canRun - } - - def findsErrors(compiler: JavaTools) = { - val (result, problems) = compile(compiler, Seq(knownSampleErrorFile), Seq("-deprecation")) - val errored = result must beFalse - val foundErrorAndWarning = problems must haveSize(5) - val importWarn = warnOnLine(lineno = 1, lineContent = Some("import java.rmi.RMISecurityException;")) - val hasKnownErrors = problems.toSeq must contain(importWarn, errorOnLine(3), warnOnLine(7)) - errored and foundErrorAndWarning and hasKnownErrors - } - - def findsDocErrors(compiler: JavaTools) = IO.withTemporaryDirectory { out => - val (result, problems) = doc(compiler, Seq(knownSampleErrorFile), Seq("-d", out.getAbsolutePath)) - val errored = result must beTrue - val foundErrorAndWarning = problems must haveSize(2) - val hasKnownErrors = problems.toSeq must contain(errorOnLine(3), errorOnLine(4)) - errored and foundErrorAndWarning and hasKnownErrors - } - - /** - * Compiles with the given constant values, and confirms that if the strings mismatch, then the - * the APIs mismatch. - */ - def analyzeStaticDifference(typeName: String, left: String, right: String): MatchResult[Boolean] = - analyzeStaticDifference(typeName, left, typeName, right) - - def analyzeStaticDifference(leftType: String, left: String, rightType: String, right: String): MatchResult[Boolean] = { - def compileWithPrimitive(templateType: String, templateValue: String) = - IO.withTemporaryDirectory { out => - // copy the input file to a temporary location and change the templateValue - val input = new File(out, hasStaticFinalFile.getName()) - IO.writeLines( - input, - IO.readLines(hasStaticFinalFile).map { line => - line.replace("TYPE", templateType).replace("VALUE", templateValue) - } - ) - - // then compile it - val (result, problems) = compile(local, Seq(input), Seq("-d", out.getAbsolutePath)) - val origCompiled = result must beTrue - val clazzz = new URLClassLoader(Array(out.toURI.toURL)).loadClass("hasstaticfinal") - (origCompiled, ClassToAPI(Seq(clazzz))) - } - - // compile with two different primitive values, and confirm that they match if their - // values match - val (leftCompiled, leftAPI) = compileWithPrimitive(leftType, left) - val (rightCompiled, rightAPI) = compileWithPrimitive(rightType, right) - val apisExpectedMatch = SameAPI(leftAPI, rightAPI) must beEqualTo(left == right) - - leftCompiled and rightCompiled and apisExpectedMatch - } - - def lineMatches(p: Problem, lineno: Int, lineContent: Option[String] = None): Boolean = { - def lineContentCheck = - lineContent match { - case Some(content) => content.equalsIgnoreCase(p.position.lineContent()) - case _ => true - } - def lineNumberCheck = p.position.line.isDefined && (p.position.line.get == lineno) - lineNumberCheck && lineContentCheck - } - - def isError(p: Problem): Boolean = p.severity == Severity.Error - def isWarn(p: Problem): Boolean = p.severity == Severity.Warn - - def errorOnLine(lineno: Int, lineContent: Option[String] = None) = - beLike[Problem]({ - case p if lineMatches(p, lineno, lineContent) && isError(p) => ok - case _ => ko - }) - def warnOnLine(lineno: Int, lineContent: Option[String] = None) = - beLike[Problem]({ - case p if lineMatches(p, lineno, lineContent) && isWarn(p) => ok - case _ => ko - }) - - def forkSameAsLocal = { - val (fresult, fproblems) = compile(forked, Seq(knownSampleErrorFile), Seq("-deprecation")) - val (lresult, lproblems) = compile(local, Seq(knownSampleErrorFile), Seq("-deprecation")) - val sameResult = fresult must beEqualTo(lresult) - - val pResults = for ((f, l) <- fproblems zip lproblems) yield { - val sourceIsSame = - if (f.position.sourcePath.isDefined) (f.position.sourcePath.get must beEqualTo(l.position.sourcePath.get)).setMessage(s"${f.position} != ${l.position}") - else l.position.sourcePath.isDefined must beFalse - val lineIsSame = - if (f.position.line.isDefined) f.position.line.get must beEqualTo(l.position.line.get) - else l.position.line.isDefined must beFalse - val severityIsSame = f.severity must beEqualTo(l.severity) - // TODO - We should check to see if the levenshtein distance of the messages is close... - sourceIsSame and lineIsSame and severityIsSame - } - val errorsAreTheSame = pResults.reduce(_ and _) - sameResult and errorsAreTheSame - } - - def compile(c: JavaTools, sources: Seq[File], args: Seq[String]): (Boolean, Array[Problem]) = { - val log = Logger.Null - val reporter = new LoggerReporter(10, log) - val result = c.compile(sources, args)(log, reporter) - (result, reporter.problems) - } - - def doc(c: JavaTools, sources: Seq[File], args: Seq[String]): (Boolean, Array[Problem]) = { - val log = Logger.Null - val reporter = new LoggerReporter(10, log) - val result = c.doc(sources, args)(log, reporter) - (result, reporter.problems) - } - - // TODO - Create one with known JAVA HOME. - def forked = JavaTools(JavaCompiler.fork(), Javadoc.fork()) - - def local = - JavaTools( - JavaCompiler.local.getOrElse(sys.error("This test cannot be run on a JRE, but only a JDK.")), - Javadoc.local.getOrElse(Javadoc.fork()) - ) - - def cwd = - (new File(new File(".").getAbsolutePath)).getCanonicalFile - - def knownSampleErrorFile = - new java.io.File(getClass.getResource("test1.java").toURI) - - def knownSampleGoodFile = - new java.io.File(getClass.getResource("good.java").toURI) - - def hasStaticFinalFile = - new java.io.File(getClass.getResource("hasstaticfinal.java").toURI) - -} diff --git a/compile/src/test/scala/sbt/compiler/javac/javaErrorParserSpec.scala b/compile/src/test/scala/sbt/compiler/javac/javaErrorParserSpec.scala deleted file mode 100644 index aa3fa24c4..000000000 --- a/compile/src/test/scala/sbt/compiler/javac/javaErrorParserSpec.scala +++ /dev/null @@ -1,66 +0,0 @@ -package sbt.compiler.javac - -import java.io.File - -import org.specs2.matcher.MatchResult -import sbt.Logger -import org.specs2.Specification - -object JavaErrorParserSpec extends Specification { - def is = s2""" - - This is a specification for parsing of java error messages. - - The JavaErrorParser should - be able to parse linux errors $parseSampleLinux - be able to parse windows file names $parseWindowsFile - be able to parse windows errors $parseSampleWindows - """ - - def parseSampleLinux = { - val parser = new JavaErrorParser() - val logger = Logger.Null - val problems = parser.parseProblems(sampleLinuxMessage, logger) - def rightSize = problems must haveSize(1) - def rightFile = problems(0).position.sourcePath.get must beEqualTo("/home/me/projects/sample/src/main/Test.java") - rightSize and rightFile - } - - def parseSampleWindows = { - val parser = new JavaErrorParser() - val logger = Logger.Null - val problems = parser.parseProblems(sampleWindowsMessage, logger) - def rightSize = problems must haveSize(1) - def rightFile = problems(0).position.sourcePath.get must beEqualTo(windowsFile) - rightSize and rightFile - } - - def parseWindowsFile: MatchResult[_] = { - val parser = new JavaErrorParser() - def failure = false must beTrue - parser.parse(parser.fileAndLineNo, sampleWindowsMessage) match { - case parser.Success((file, line), rest) => file must beEqualTo(windowsFile) - case parser.Error(msg, next) => failure.setMessage(s"Error to parse: $msg, ${next.pos.longString}") - case parser.Failure(msg, next) => failure.setMessage(s"Failed to parse: $msg, ${next.pos.longString}") - } - } - - def sampleLinuxMessage = - """ - |/home/me/projects/sample/src/main/Test.java:4: cannot find symbol - |symbol : method baz() - |location: class Foo - |return baz(); - """.stripMargin - - def sampleWindowsMessage = - s""" - |$windowsFile:4: cannot find symbol - |symbol : method baz() - |location: class Foo - |return baz(); - """.stripMargin - - def windowsFile = """C:\Projects\sample\src\main\java\Test.java""" - def windowsFileAndLine = s"""$windowsFile:4""" -} diff --git a/interface/definition b/interface/definition deleted file mode 100644 index dadd41adb..000000000 --- a/interface/definition +++ /dev/null @@ -1,25 +0,0 @@ -Definition - name: String - access: Access - modifiers: Modifiers - annotations: Annotation* - FieldLike - tpe : Type - Val - Var - ParameterizedDefinition - typeParameters: TypeParameter* - Def - valueParameters: ParameterList* - returnType: Type - ClassLike - definitionType: DefinitionType - selfType: ~Type - structure: ~Structure - savedAnnotations: String* - TypeMember - TypeAlias - tpe: Type - TypeDeclaration - lowerBound: Type - upperBound: Type diff --git a/interface/other b/interface/other deleted file mode 100644 index 68e4c3a50..000000000 --- a/interface/other +++ /dev/null @@ -1,81 +0,0 @@ -Source - compilation: Compilation - hash: Byte* - api: SourceAPI - apiHash: Int - _internalOnly_nameHashes: _internalOnly_NameHashes - hasMacro: Boolean - -_internalOnly_NameHashes - regularMembers: _internalOnly_NameHash* - implicitMembers: _internalOnly_NameHash* - -_internalOnly_NameHash - name: String - hash: Int - -SourceAPI - packages : Package* - definitions: Definition* - -OutputSetting - sourceDirectory: String - outputDirectory: String - -Compilation - startTime: Long - outputs: OutputSetting* - -Package - name: String - -Access - Public - Qualified - qualifier: Qualifier - Protected - Private - -Qualifier - Unqualified - ThisQualifier - IdQualifier - value: String - -ParameterList - parameters: MethodParameter* - isImplicit: Boolean -MethodParameter - name: String - tpe: Type - hasDefault: Boolean - modifier: ParameterModifier - -TypeParameter - id: String - annotations: Annotation* - typeParameters : TypeParameter* - variance: Variance - lowerBound: Type - upperBound: Type - -Annotation - base: Type - arguments: AnnotationArgument* -AnnotationArgument - name: String - value: String - -enum Variance : Contravariant, Covariant, Invariant -enum ParameterModifier : Repeated, Plain, ByName -enum DefinitionType : Trait, ClassDef, Module, PackageModule - -Path - components: PathComponent* - -PathComponent - Super - qualifier: Path - This - Id - id: String \ No newline at end of file diff --git a/interface/src/main/java/xsbti/AnalysisCallback.java b/interface/src/main/java/xsbti/AnalysisCallback.java deleted file mode 100644 index a51628f15..000000000 --- a/interface/src/main/java/xsbti/AnalysisCallback.java +++ /dev/null @@ -1,62 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbti; - -import java.io.File; - -public interface AnalysisCallback -{ - /** Called to indicate that the source file source depends on the source file - * dependsOn. Note that only source files included in the current compilation will - * passed to this method. Dependencies on classes generated by sources not in the current compilation will - * be passed as class dependencies to the classDependency method. - * If publicInherited is true, this dependency is a result of inheritance by a - * template accessible outside of the source file. - * @deprecated Use `sourceDependency(File dependsOn, File source, DependencyContext context)` instead. */ - @Deprecated - void sourceDependency(File dependsOn, File source, boolean publicInherited); - /** Called to indicate that the source file source depends on the source file - * dependsOn. Note that only source files included in the current compilation will - * passed to this method. Dependencies on classes generated by sources not in the current compilation will - * be passed as class dependencies to the classDependency method. - * context gives information about the context in which this dependency has been extracted. - * See xsbti.DependencyContext for the list of existing dependency contexts. */ - void sourceDependency(File dependsOn, File source, DependencyContext context); - /** Called to indicate that the source file source depends on the top-level - * class named name from class or jar file binary. - * If publicInherited is true, this dependency is a result of inheritance by a - * template accessible outside of the source file. - * @deprecated Use `binaryDependency(File binary, String name, File source, DependencyContext context)` instead. */ - @Deprecated - void binaryDependency(File binary, String name, File source, boolean publicInherited); - /** Called to indicate that the source file source depends on the top-level - * class named name from class or jar file binary. - * context gives information about the context in which this dependency has been extracted. - * See xsbti.DependencyContext for the list of existing dependency contexts. */ - void binaryDependency(File binary, String name, File source, DependencyContext context); - /** Called to indicate that the source file source produces a class file at - * module contain class name.*/ - void generatedClass(File source, File module, String name); - /** Called when the public API of a source file is extracted. */ - void api(File sourceFile, xsbti.api.SourceAPI source); - void usedName(File sourceFile, String names); - /** Provides problems discovered during compilation. These may be reported (logged) or unreported. - * Unreported problems are usually unreported because reporting was not enabled via a command line switch. */ - void problem(String what, Position pos, String msg, Severity severity, boolean reported); - /** - * Determines whether method calls through this interface should be interpreted as serving - * name hashing algorithm needs in given compiler run. - * - * In particular, it indicates whether member reference and inheritance dependencies should be - * extracted. - * - * As the signature suggests, this method's implementation is meant to be side-effect free. It's added - * to AnalysisCallback because it indicates how other callback calls should be interpreted by both - * implementation of AnalysisCallback and it's clients. - * - * NOTE: This method is an implementation detail and can be removed at any point without deprecation. - * Do not depend on it, please. - */ - boolean nameHashing(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/ArtifactInfo.java b/interface/src/main/java/xsbti/ArtifactInfo.java deleted file mode 100644 index 6f2eedae5..000000000 --- a/interface/src/main/java/xsbti/ArtifactInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package xsbti; - -public final class ArtifactInfo -{ - public static final String ScalaOrganization = "org.scala-lang"; - public static final String ScalaLibraryID = "scala-library"; - public static final String ScalaCompilerID = "scala-compiler"; - public static final String SbtOrganization = "org.scala-sbt"; -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/CompileCancelled.java b/interface/src/main/java/xsbti/CompileCancelled.java deleted file mode 100644 index bcd3695dd..000000000 --- a/interface/src/main/java/xsbti/CompileCancelled.java +++ /dev/null @@ -1,9 +0,0 @@ -package xsbti; - -/** - * An exception thrown when compilation cancellation has been requested during - * Scala compiler run. - */ -public abstract class CompileCancelled extends RuntimeException { - public abstract String[] arguments(); -} diff --git a/interface/src/main/java/xsbti/CompileFailed.java b/interface/src/main/java/xsbti/CompileFailed.java deleted file mode 100644 index f1cbbc61b..000000000 --- a/interface/src/main/java/xsbti/CompileFailed.java +++ /dev/null @@ -1,7 +0,0 @@ -package xsbti; - -public abstract class CompileFailed extends RuntimeException -{ - public abstract String[] arguments(); - public abstract Problem[] problems(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/DependencyContext.java b/interface/src/main/java/xsbti/DependencyContext.java deleted file mode 100644 index 15cfa76d1..000000000 --- a/interface/src/main/java/xsbti/DependencyContext.java +++ /dev/null @@ -1,22 +0,0 @@ -package xsbti; - -/** - * Enumeration of existing dependency contexts. - * Dependency contexts represent the various kind of dependencies that - * can exist between symbols. - */ -public enum DependencyContext { - /** - * Represents a direct dependency between two symbols : - * object Foo - * object Bar { def foo = Foo } - */ - DependencyByMemberRef, - - /** - * Represents an inheritance dependency between two symbols : - * class A - * class B extends A - */ - DependencyByInheritance -} diff --git a/interface/src/main/java/xsbti/F0.java b/interface/src/main/java/xsbti/F0.java deleted file mode 100644 index b0091b186..000000000 --- a/interface/src/main/java/xsbti/F0.java +++ /dev/null @@ -1,9 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbti; - -public interface F0 -{ - T apply(); -} diff --git a/interface/src/main/java/xsbti/Logger.java b/interface/src/main/java/xsbti/Logger.java deleted file mode 100644 index 60dfab7b5..000000000 --- a/interface/src/main/java/xsbti/Logger.java +++ /dev/null @@ -1,13 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbti; - -public interface Logger -{ - void error(F0 msg); - void warn(F0 msg); - void info(F0 msg); - void debug(F0 msg); - void trace(F0 exception); -} diff --git a/interface/src/main/java/xsbti/Maybe.java b/interface/src/main/java/xsbti/Maybe.java deleted file mode 100644 index f730ef918..000000000 --- a/interface/src/main/java/xsbti/Maybe.java +++ /dev/null @@ -1,30 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbti; - -/** Intended as a lightweight carrier for scala.Option. */ -public abstract class Maybe -{ - // private pending Scala bug #3642 - protected Maybe() {} - - public static Maybe just(final s v) - { - return new Maybe() { - public boolean isDefined() { return true; } - public s get() { return v; } - }; - } - public static Maybe nothing() - { - return new Maybe() { - public boolean isDefined() { return false; } - public s get() { throw new UnsupportedOperationException("nothing.get"); } - }; - } - - public final boolean isEmpty() { return !isDefined(); } - public abstract boolean isDefined(); - public abstract t get(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Position.java b/interface/src/main/java/xsbti/Position.java deleted file mode 100644 index 96c60ebb2..000000000 --- a/interface/src/main/java/xsbti/Position.java +++ /dev/null @@ -1,18 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbti; - -public interface Position -{ - Maybe line(); - String lineContent(); - Maybe offset(); - - // pointer to the column position of the error/warning - Maybe pointer(); - Maybe pointerSpace(); - - Maybe sourcePath(); - Maybe sourceFile(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Problem.java b/interface/src/main/java/xsbti/Problem.java deleted file mode 100644 index db7f67b22..000000000 --- a/interface/src/main/java/xsbti/Problem.java +++ /dev/null @@ -1,12 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbti; - -public interface Problem -{ - String category(); - Severity severity(); - String message(); - Position position(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Reporter.java b/interface/src/main/java/xsbti/Reporter.java deleted file mode 100644 index d76be8ea6..000000000 --- a/interface/src/main/java/xsbti/Reporter.java +++ /dev/null @@ -1,22 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbti; - -public interface Reporter -{ - /** Resets logging, including any accumulated errors, warnings, messages, and counts.*/ - void reset(); - /** Returns true if this logger has seen any errors since the last call to reset.*/ - boolean hasErrors(); - /** Returns true if this logger has seen any warnings since the last call to reset.*/ - boolean hasWarnings(); - /** Logs a summary of logging since the last reset.*/ - void printSummary(); - /** Returns a list of warnings and errors since the last reset.*/ - Problem[] problems(); - /** Logs a message.*/ - void log(Position pos, String msg, Severity sev); - /** Reports a comment. */ - void comment(Position pos, String msg); -} diff --git a/interface/src/main/java/xsbti/Severity.java b/interface/src/main/java/xsbti/Severity.java deleted file mode 100644 index 09aed574b..000000000 --- a/interface/src/main/java/xsbti/Severity.java +++ /dev/null @@ -1,9 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbti; - -public enum Severity -{ - Info, Warn, Error -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/api/AbstractLazy.java b/interface/src/main/java/xsbti/api/AbstractLazy.java deleted file mode 100644 index bd21f166f..000000000 --- a/interface/src/main/java/xsbti/api/AbstractLazy.java +++ /dev/null @@ -1,26 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbti.api; - - import java.io.ObjectStreamException; - -public abstract class AbstractLazy implements Lazy, java.io.Serializable -{ - private Object writeReplace() throws ObjectStreamException - { - return new StrictLazy(get()); - } - private static final class StrictLazy implements Lazy, java.io.Serializable - { - private final T value; - StrictLazy(T t) - { - value = t; - } - public T get() - { - return value; - } - } -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/api/Lazy.java b/interface/src/main/java/xsbti/api/Lazy.java deleted file mode 100644 index 1ee29b013..000000000 --- a/interface/src/main/java/xsbti/api/Lazy.java +++ /dev/null @@ -1,9 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbti.api; - -public interface Lazy -{ - T get(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/api/Modifiers.java b/interface/src/main/java/xsbti/api/Modifiers.java deleted file mode 100644 index 5e103c7ec..000000000 --- a/interface/src/main/java/xsbti/api/Modifiers.java +++ /dev/null @@ -1,83 +0,0 @@ -package xsbti.api; - -public final class Modifiers implements java.io.Serializable -{ - private static final int AbstractBit = 0; - private static final int OverrideBit = 1; - private static final int FinalBit = 2; - private static final int SealedBit = 3; - private static final int ImplicitBit = 4; - private static final int LazyBit = 5; - private static final int MacroBit = 6; - - private static int flag(boolean set, int bit) - { - return set ? (1 << bit) : 0; - } - - public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro) - { - this.flags = (byte)( - flag(isAbstract, AbstractBit) | - flag(isOverride, OverrideBit) | - flag(isFinal, FinalBit) | - flag(isSealed, SealedBit) | - flag(isImplicit, ImplicitBit) | - flag(isLazy, LazyBit) | - flag(isMacro, MacroBit) - ); - } - - private final byte flags; - - private boolean flag(int bit) - { - return (flags & (1 << bit)) != 0; - } - - public final byte raw() - { - return flags; - } - - public final boolean isAbstract() - { - return flag(AbstractBit); - } - public final boolean isOverride() - { - return flag(OverrideBit); - } - public final boolean isFinal() - { - return flag(FinalBit); - } - public final boolean isSealed() - { - return flag(SealedBit); - } - public final boolean isImplicit() - { - return flag(ImplicitBit); - } - public final boolean isLazy() - { - return flag(LazyBit); - } - public final boolean isMacro() - { - return flag(MacroBit); - } - public boolean equals(Object o) - { - return (o instanceof Modifiers) && flags == ((Modifiers)o).flags; - } - public int hashCode() - { - return flags; - } - public String toString() - { - return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ")"; - } -} diff --git a/interface/src/main/java/xsbti/compile/CachedCompiler.java b/interface/src/main/java/xsbti/compile/CachedCompiler.java deleted file mode 100644 index 1d37f0883..000000000 --- a/interface/src/main/java/xsbti/compile/CachedCompiler.java +++ /dev/null @@ -1,13 +0,0 @@ -package xsbti.compile; - -import xsbti.AnalysisCallback; -import xsbti.Logger; -import xsbti.Reporter; -import java.io.File; - -public interface CachedCompiler -{ - /** Returns an array of arguments representing the nearest command line equivalent of a call to run but without the command name itself.*/ - String[] commandArguments(File[] sources); - void run(File[] sources, DependencyChanges cpChanges, AnalysisCallback callback, Logger log, Reporter delegate, CompileProgress progress); -} diff --git a/interface/src/main/java/xsbti/compile/CachedCompilerProvider.java b/interface/src/main/java/xsbti/compile/CachedCompilerProvider.java deleted file mode 100644 index 313f27505..000000000 --- a/interface/src/main/java/xsbti/compile/CachedCompilerProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package xsbti.compile; - -import xsbti.Logger; -import xsbti.Reporter; - -public interface CachedCompilerProvider -{ - ScalaInstance scalaInstance(); - CachedCompiler newCachedCompiler(String[] arguments, Output output, Logger log, Reporter reporter, boolean resident); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/ClasspathOptions.java b/interface/src/main/java/xsbti/compile/ClasspathOptions.java deleted file mode 100644 index e4aba32b7..000000000 --- a/interface/src/main/java/xsbti/compile/ClasspathOptions.java +++ /dev/null @@ -1,29 +0,0 @@ -package xsbti.compile; - -/** -* Configures modifications to the classpath based on the Scala instance used for compilation. -* This is typically used for the Scala compiler only and all values set to false for the Java compiler. -*/ -public interface ClasspathOptions -{ - /** If true, includes the Scala library on the boot classpath. This should usually be true.*/ - boolean bootLibrary(); - - /** If true, includes the Scala compiler on the standard classpath. - * This is typically false and is instead managed by the build tool or environment. - */ - boolean compiler(); - - /** If true, includes extra jars from the Scala instance on the standard classpath. - * This is typically false and is instead managed by the build tool or environment. - */ - boolean extra(); - - /** If true, automatically configures the boot classpath. This should usually be true.*/ - boolean autoBoot(); - - /** If true, the Scala library jar is filtered from the standard classpath. - * This should usually be true because the library should be included on the boot classpath of the Scala compiler and not the standard classpath. - */ - boolean filterLibrary(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/CompileOrder.java b/interface/src/main/java/xsbti/compile/CompileOrder.java deleted file mode 100644 index 5683f75d9..000000000 --- a/interface/src/main/java/xsbti/compile/CompileOrder.java +++ /dev/null @@ -1,34 +0,0 @@ -package xsbti.compile; - -/** -* Defines the order in which Scala and Java sources are compiled when compiling a set of sources with both Java and Scala sources. -* This setting has no effect if only Java sources or only Scala sources are being compiled. -* It is generally more efficient to use JavaThenScala or ScalaThenJava when mixed compilation is not required. -*/ -public enum CompileOrder -{ - /** - * Allows Scala sources to depend on Java sources and allows Java sources to depend on Scala sources. - * - * In this mode, both Java and Scala sources are passed to the Scala compiler, which generates class files for the Scala sources. - * Then, Java sources are passed to the Java compiler, which generates class files for the Java sources. - * The Scala classes compiled in the first step are included on the classpath to the Java compiler. - */ - Mixed, - /** - * Allows Scala sources to depend on the Java sources in the compilation, but does not allow Java sources to depend on Scala sources. - * - * In this mode, both Java and Scala sources are passed to the Scala compiler, which generates class files for the Scala sources. - * Then, Java sources are passed to the Java compiler, which generates class files for the Java sources. - * The Scala classes compiled in the first step are included on the classpath to the Java compiler. - */ - JavaThenScala, - /** - * Allows Java sources to depend on the Scala sources in the compilation, but does not allow Scala sources to depend on Java sources. - * - * In this mode, both Java and Scala sources are passed to the Scala compiler, which generates class files for the Scala sources. - * Then, Java sources are passed to the Java compiler, which generates class files for the Java sources. - * The Scala classes compiled in the first step are included on the classpath to the Java compiler. - */ - ScalaThenJava -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/CompileProgress.java b/interface/src/main/java/xsbti/compile/CompileProgress.java deleted file mode 100755 index 17174ff6a..000000000 --- a/interface/src/main/java/xsbti/compile/CompileProgress.java +++ /dev/null @@ -1,12 +0,0 @@ -package xsbti.compile; - -/** - * An API for reporting when files are being compiled. - * - * Note; This is tied VERY SPECIFICALLY to scala. - */ -public interface CompileProgress { - void startUnit(String phase, String unitPath); - - boolean advance(int current, int total); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/Compilers.java b/interface/src/main/java/xsbti/compile/Compilers.java deleted file mode 100644 index 0bb194534..000000000 --- a/interface/src/main/java/xsbti/compile/Compilers.java +++ /dev/null @@ -1,8 +0,0 @@ -package xsbti.compile; - -public interface Compilers -{ - JavaCompiler javac(); - // should be cached by client if desired - ScalaCompiler scalac(); -} diff --git a/interface/src/main/java/xsbti/compile/DefinesClass.java b/interface/src/main/java/xsbti/compile/DefinesClass.java deleted file mode 100644 index 261c6ca22..000000000 --- a/interface/src/main/java/xsbti/compile/DefinesClass.java +++ /dev/null @@ -1,12 +0,0 @@ -package xsbti.compile; - -/** -* Determines if an entry on a classpath contains a class. -*/ -public interface DefinesClass -{ - /** - * Returns true if the classpath entry contains the requested class. - */ - boolean apply(String className); -} diff --git a/interface/src/main/java/xsbti/compile/DependencyChanges.java b/interface/src/main/java/xsbti/compile/DependencyChanges.java deleted file mode 100644 index 4f6bda55a..000000000 --- a/interface/src/main/java/xsbti/compile/DependencyChanges.java +++ /dev/null @@ -1,13 +0,0 @@ -package xsbti.compile; - - import java.io.File; - -// only includes changes to dependencies outside of the project -public interface DependencyChanges -{ - boolean isEmpty(); - // class files or jar files - File[] modifiedBinaries(); - // class names - String[] modifiedClasses(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/GlobalsCache.java b/interface/src/main/java/xsbti/compile/GlobalsCache.java deleted file mode 100644 index c8540e2d2..000000000 --- a/interface/src/main/java/xsbti/compile/GlobalsCache.java +++ /dev/null @@ -1,13 +0,0 @@ -package xsbti.compile; - -import xsbti.Logger; -import xsbti.Reporter; - -/** - * An interface which lets us know how to retrieve cached compiler instances form the current JVM. - */ -public interface GlobalsCache -{ - CachedCompiler apply(String[] args, Output output, boolean forceNew, CachedCompilerProvider provider, Logger log, Reporter reporter); - void clear(); -} diff --git a/interface/src/main/java/xsbti/compile/IncrementalCompiler.java b/interface/src/main/java/xsbti/compile/IncrementalCompiler.java deleted file mode 100644 index c98263e7f..000000000 --- a/interface/src/main/java/xsbti/compile/IncrementalCompiler.java +++ /dev/null @@ -1,71 +0,0 @@ -package xsbti.compile; - -import xsbti.Logger; -import java.io.File; - -/* -* This API is subject to change. -* -* It is the client's responsibility to: -* 1. Manage class loaders. Usually the client will want to: -* i. Keep the class loader used by the ScalaInstance warm. -* ii. Keep the class loader of the incremental recompilation classes (xsbti.compile) warm. -* iii. Share the class loader for Scala classes between the incremental compiler implementation and the ScalaInstance where possible (must be binary compatible) -* 2. Manage the compiler interface jar. The interface must be compiled against the exact Scala version used for compilation and a compatible Java version. -* 3. Manage compilation order between different compilations. -* i. Execute a compilation for each dependency, obtaining an Analysis for each. -* ii. Provide the Analysis from previous compilations to dependent compilations in the analysis map. -* 4. Provide an implementation of JavaCompiler for compiling Java sources. -* 5. Define a function that determines if a classpath entry contains a class (Setup.definesClass). -* i. This is provided by the client so that the client can cache this information across compilations when compiling multiple sets of sources. -* ii. The cache should be cleared for each new compilation run or else recompilation will not properly account for changes to the classpath. -* 6. Provide a cache directory. -* i. This directory is used by IncrementalCompiler to persist data between compilations. -* ii. It should be a different directory for each set of sources being compiled. -* 7. Manage parallel execution. -* i. Each compilation may be performed in a different thread as long as the dependencies have been compiled already. -* ii. Implementations of all types should be immutable and arrays treated as immutable. -* 8. Ensure general invariants: -* i. The implementations of all types are immutable, except for the already discussed Setup.definesClass. -* ii. Arrays are treated as immutable. -* iii. No value is ever null. -*/ -public interface IncrementalCompiler -{ - /** - * Performs an incremental compilation as configured by `in`. - * The returned Analysis should be provided to compilations depending on the classes from this compilation. - */ - Analysis compile(Inputs in, Logger log); - - /** - * Creates a compiler instance that can be used by the `compile` method. - * - * @param instance The Scala version to use - * @param interfaceJar The compiler interface jar compiled for the Scala version being used - * @param options Configures how arguments to the underlying Scala compiler will be built. - * - */ - @Deprecated - ScalaCompiler newScalaCompiler(ScalaInstance instance, File interfaceJar, ClasspathOptions options, Logger log); - /** - * Creates a compiler instance that can be used by the `compile` method. - * - * @param instance The Scala version to use - * @param interfaceJar The compiler interface jar compiled for the Scala version being used - * @param options Configures how arguments to the underlying Scala compiler will be built. - */ - ScalaCompiler newScalaCompiler(ScalaInstance instance, File interfaceJar, ClasspathOptions options); - - /** - * Compiles the source interface for a Scala version. The resulting jar can then be used by the `newScalaCompiler` method - * to create a ScalaCompiler for incremental compilation. It is the client's responsibility to manage compiled jars for - * different Scala versions. - * - * @param label A brief name describing the source component for use in error messages - * @param sourceJar The jar file containing the compiler interface sources. These are published as sbt's compiler-interface-src module. - * @param targetJar Where to create the output jar file containing the compiled classes. - * @param instance The ScalaInstance to compile the compiler interface for. - * @param log The logger to use during compilation. */ - void compileInterfaceJar(String label, File sourceJar, File targetJar, File interfaceJar, ScalaInstance instance, Logger log); -} diff --git a/interface/src/main/java/xsbti/compile/Inputs.java b/interface/src/main/java/xsbti/compile/Inputs.java deleted file mode 100644 index 5c9ded425..000000000 --- a/interface/src/main/java/xsbti/compile/Inputs.java +++ /dev/null @@ -1,14 +0,0 @@ -package xsbti.compile; - -/** Configures a single compilation of a single set of sources.*/ -public interface Inputs -{ - /** The Scala and Java compilers to use for compilation.*/ - Compilers compilers(); - - /** Standard compilation options, such as the sources and classpath to use. */ - Options options(); - - /** Configures incremental compilation.*/ - Setup setup(); -} diff --git a/interface/src/main/java/xsbti/compile/JavaCompiler.java b/interface/src/main/java/xsbti/compile/JavaCompiler.java deleted file mode 100644 index 18b3f5bea..000000000 --- a/interface/src/main/java/xsbti/compile/JavaCompiler.java +++ /dev/null @@ -1,27 +0,0 @@ -package xsbti.compile; - -import java.io.File; -import xsbti.Logger; -import xsbti.Reporter; - -/** -* Interface to a Java compiler. -*/ -public interface JavaCompiler -{ - /** Compiles Java sources using the provided classpath, output directory, and additional options. - * Output should be sent to the provided logger. - * - * @deprecated 0.13.8 - Use compileWithReporter instead - */ - @Deprecated - void compile(File[] sources, File[] classpath, Output output, String[] options, Logger log); - - /** - * Compiles java sources using the provided classpath, output directory and additional options. - * - * Output should be sent to the provided logger. - * Failures should be passed to the provided Reporter. - */ - void compileWithReporter(File[] sources, File[] classpath, Output output, String[] options, Reporter reporter, Logger log); -} diff --git a/interface/src/main/java/xsbti/compile/MultipleOutput.java b/interface/src/main/java/xsbti/compile/MultipleOutput.java deleted file mode 100755 index 6ba3479e6..000000000 --- a/interface/src/main/java/xsbti/compile/MultipleOutput.java +++ /dev/null @@ -1,20 +0,0 @@ -package xsbti.compile; - -import java.io.File; - -public interface MultipleOutput extends Output { - - interface OutputGroup { - /** The directory where source files are stored for this group. - * Source directories should uniquely identify the group for a source file. */ - File sourceDirectory(); - - /** The directory where class files should be generated. - * Incremental compilation will manage the class files in this directory. - * In particular, outdated class files will be deleted before compilation. - * It is important that this directory is exclusively used for one set of sources. */ - File outputDirectory(); - } - - OutputGroup[] outputGroups(); -} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/compile/Options.java b/interface/src/main/java/xsbti/compile/Options.java deleted file mode 100644 index 78643202d..000000000 --- a/interface/src/main/java/xsbti/compile/Options.java +++ /dev/null @@ -1,27 +0,0 @@ -package xsbti.compile; - -import java.io.File; - -/** Standard compilation options.*/ -public interface Options -{ - /** The classpath to use for compilation. - * This will be modified according to the ClasspathOptions used to configure the ScalaCompiler.*/ - File[] classpath(); - - /** All sources that should be recompiled. - * This should include Scala and Java sources, which are identified by their extension. */ - File[] sources(); - - /** Output for the compilation. */ - Output output(); - - /** The options to pass to the Scala compiler other than the sources and classpath to use. */ - String[] options(); - - /** The options to pass to the Java compiler other than the sources and classpath to use. */ - String[] javacOptions(); - - /** Controls the order in which Java and Scala sources are compiled.*/ - CompileOrder order(); -} diff --git a/interface/src/main/java/xsbti/compile/Output.java b/interface/src/main/java/xsbti/compile/Output.java deleted file mode 100755 index 4f785884e..000000000 --- a/interface/src/main/java/xsbti/compile/Output.java +++ /dev/null @@ -1,7 +0,0 @@ -package xsbti.compile; -/** Abstract interface denoting the output of the compilation. Inheritors are SingleOutput with a global output directory and - * MultipleOutput that specifies the output directory per source file. - */ -public interface Output -{ -} diff --git a/interface/src/main/java/xsbti/compile/ScalaInstance.java b/interface/src/main/java/xsbti/compile/ScalaInstance.java deleted file mode 100644 index c7f3984e3..000000000 --- a/interface/src/main/java/xsbti/compile/ScalaInstance.java +++ /dev/null @@ -1,37 +0,0 @@ -package xsbti.compile; - -import java.io.File; - -/** -* Defines Scala instance, which is a reference version String, a unique version String, a set of jars, and a class loader for a Scala version. -* -* Note that in this API a 'jar' can actually be any valid classpath entry. -*/ -public interface ScalaInstance -{ - /** The version used to refer to this Scala version. - * It need not be unique and can be a dynamic version like 2.10.0-SNAPSHOT. - */ - String version(); - - /** A class loader providing access to the classes and resources in the library and compiler jars. */ - ClassLoader loader(); - - /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ - @Deprecated - File libraryJar(); - - /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ - @Deprecated - File compilerJar(); - - /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ - @Deprecated - File[] otherJars(); - - /** All jar files provided by this Scala instance.*/ - File[] allJars(); - - /** The unique identifier for this Scala instance. An implementation should usually obtain this from the compiler.properties file in the compiler jar. */ - String actualVersion(); -} diff --git a/interface/src/main/java/xsbti/compile/Setup.java b/interface/src/main/java/xsbti/compile/Setup.java deleted file mode 100644 index edf250b8b..000000000 --- a/interface/src/main/java/xsbti/compile/Setup.java +++ /dev/null @@ -1,47 +0,0 @@ -package xsbti.compile; - -import java.io.File; -import java.util.Map; - -import xsbti.Maybe; -import xsbti.Reporter; - -/** Configures incremental recompilation. */ -public interface Setup -{ - /** Provides the Analysis for the given classpath entry.*/ - Maybe analysisMap(File file); - - /** Provides a function to determine if classpath entry `file` contains a given class. - * The returned function should generally cache information about `file`, such as the list of entries in a jar. - */ - DefinesClass definesClass(File file); - - /** If true, no sources are actually compiled and the Analysis from the previous compilation is returned.*/ - boolean skip(); - - /** The file used to cache information across compilations. - * This file can be removed to force a full recompilation. - * The file should be unique and not shared between compilations. */ - File cacheFile(); - - GlobalsCache cache(); - - /** If returned, the progress that should be used to report scala compilation to. */ - Maybe progress(); - - /** The reporter that should be used to report scala compilation to. */ - Reporter reporter(); - - /** - * Returns incremental compiler options. - * - * @see sbt.inc.IncOptions for details - * - * You can get default options by calling sbt.inc.IncOptions.toStringMap(sbt.inc.IncOptions.Default). - * - * In the future, we'll extend API in xsbti to provide factory methods that would allow to obtain - * defaults values so one can depend on xsbti package only. - **/ - Map incrementalCompilerOptions(); -} diff --git a/interface/src/main/java/xsbti/compile/SingleOutput.java b/interface/src/main/java/xsbti/compile/SingleOutput.java deleted file mode 100755 index cb200c9b7..000000000 --- a/interface/src/main/java/xsbti/compile/SingleOutput.java +++ /dev/null @@ -1,12 +0,0 @@ -package xsbti.compile; - -import java.io.File; - -public interface SingleOutput extends Output { - - /** The directory where class files should be generated. - * Incremental compilation will manage the class files in this directory. - * In particular, outdated class files will be deleted before compilation. - * It is important that this directory is exclusively used for one set of sources. */ - File outputDirectory(); -} \ No newline at end of file diff --git a/interface/src/test/scala/xsbti/TestCallback.scala b/interface/src/test/scala/xsbti/TestCallback.scala deleted file mode 100644 index f0658597b..000000000 --- a/interface/src/test/scala/xsbti/TestCallback.scala +++ /dev/null @@ -1,34 +0,0 @@ -package xsbti - -import java.io.File -import scala.collection.mutable.ArrayBuffer -import xsbti.api.SourceAPI -import xsbti.DependencyContext._ - -class TestCallback(override val nameHashing: Boolean = false) extends AnalysisCallback -{ - val sourceDependencies = new ArrayBuffer[(File, File, DependencyContext)] - val binaryDependencies = new ArrayBuffer[(File, String, File, DependencyContext)] - val products = new ArrayBuffer[(File, File, String)] - val usedNames = scala.collection.mutable.Map.empty[File, Set[String]].withDefaultValue(Set.empty) - val apis: scala.collection.mutable.Map[File, SourceAPI] = scala.collection.mutable.Map.empty - - def sourceDependency(dependsOn: File, source: File, inherited: Boolean): Unit = { - val context = if(inherited) DependencyByInheritance else DependencyByMemberRef - sourceDependency(dependsOn, source, context) - } - def sourceDependency(dependsOn: File, source: File, context: DependencyContext): Unit = { sourceDependencies += ((dependsOn, source, context)) } - def binaryDependency(binary: File, name: String, source: File, inherited: Boolean): Unit = { - val context = if(inherited) DependencyByInheritance else DependencyByMemberRef - binaryDependency(binary, name, source, context) - } - def binaryDependency(binary: File, name: String, source: File, context: DependencyContext): Unit = { binaryDependencies += ((binary, name, source, context)) } - def generatedClass(source: File, module: File, name: String): Unit = { products += ((source, module, name)) } - - def usedName(source: File, name: String): Unit = { usedNames(source) += name } - def api(source: File, sourceAPI: SourceAPI): Unit = { - assert(!apis.contains(source), s"The `api` method should be called once per source file: $source") - apis(source) = sourceAPI - } - def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean): Unit = () -} diff --git a/interface/type b/interface/type deleted file mode 100644 index dbb393dd6..000000000 --- a/interface/type +++ /dev/null @@ -1,30 +0,0 @@ - -Type - SimpleType - Projection - prefix : SimpleType - id: String - ParameterRef - id: String - Singleton - path: Path - EmptyType - Parameterized - baseType : SimpleType - typeArguments: Type* - Constant - baseType: Type - value: String - Annotated - baseType : Type - annotations : Annotation* - Structure - parents : ~Type* - declared: ~Definition* - inherited: ~Definition* - Existential - baseType : Type - clause: TypeParameter* - Polymorphic - baseType: Type - parameters: TypeParameter* diff --git a/ivy/NOTICE b/ivy/NOTICE deleted file mode 100644 index dd0df7b5a..000000000 --- a/ivy/NOTICE +++ /dev/null @@ -1,28 +0,0 @@ -Simple Build Tool: Ivy Interface Component -Copyright 2008, 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) - -Portions based on Apache Ivy, -licensed under the Apache License, Version 2.0 (see licenses/LICENSE_Apache) - -It requires the following notice: - -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). - -Portions of Ivy were originally developed by -Jayasoft SARL (http://www.jayasoft.fr/) -and are licensed to the Apache Software Foundation under the -"Software Grant License Agreement" - - -THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ivy/src/main/java/sbt/ResolverAdapter.java b/ivy/src/main/java/sbt/ResolverAdapter.java deleted file mode 100644 index 4247c97e7..000000000 --- a/ivy/src/main/java/sbt/ResolverAdapter.java +++ /dev/null @@ -1,13 +0,0 @@ -package sbt; - - import java.util.Map; - import org.apache.ivy.plugins.resolver.DependencyResolver; - -// implements the methods with raw types -@SuppressWarnings("rawtypes") -public abstract class ResolverAdapter implements DependencyResolver -{ - public String[] listTokenValues(String token, Map otherTokenValues) { return new String[0]; } - - public Map[] listTokenValues(String[] tokens, Map criteria) { return new Map[0]; } -} \ No newline at end of file diff --git a/ivy/src/main/java/sbt/mavenint/SbtPomExtraProperties.java b/ivy/src/main/java/sbt/mavenint/SbtPomExtraProperties.java deleted file mode 100644 index fde6af15b..000000000 --- a/ivy/src/main/java/sbt/mavenint/SbtPomExtraProperties.java +++ /dev/null @@ -1,25 +0,0 @@ -package sbt.mavenint; - -/** - * Extra properties we dump from Aether into the properties list. - */ -public class SbtPomExtraProperties { - - public static final String MAVEN_PACKAGING_KEY = "sbt.pom.packaging"; - public static final String SCALA_VERSION_KEY = "sbt.pom.scalaversion"; - public static final String SBT_VERSION_KEY = "sbt.pom.sbtversion"; - - public static final String POM_INFO_KEY_PREFIX = "info."; - public static final String POM_SCALA_VERSION = "scalaVersion"; - public static final String POM_SBT_VERSION = "sbtVersion"; - public static final String POM_API_KEY = "info.apiURL"; - - public static final String LICENSE_COUNT_KEY = "license.count"; - - public static String makeLicenseName(int i) { - return "license." + i + ".name"; - } - public static String makeLicenseUrl(int i) { - return "license." + i + ".url"; - } -} diff --git a/ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala b/ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala deleted file mode 100644 index e59bd174e..000000000 --- a/ivy/src/main/scala/org/apache/ivy/plugins/parser/m2/ReplaceMavenConfigurationMappings.scala +++ /dev/null @@ -1,118 +0,0 @@ -package org.apache.ivy.plugins.parser.m2 - -import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor; - -/** - * It turns out there was a very subtle, and evil, issue sitting the Ivy/maven configuration, and it - * related to dependency mapping. A mapping of `foo->bar(*)` means that the local configuration - * `foo` depends on the remote configuration `bar`, if it exists, or *ALL CONFIGURATIONS* if `bar` - * does not exist. Since the default Ivy configuration mapping was using the random `master` - * configuration, which AFAICT is NEVER specified, just an assumed default, this would cause leaks - * between maven + ivy projects. - * - * i.e. if a maven POM depends on a module denoted by an ivy.xml file, then you'd wind up accidentally - * bleeding ALL the ivy module's configurations into the maven module's configurations. - * - * This fix works around the issue, by assuming that if there is no `master` configuration, than the - * maven default of `compile` is intended. As sbt forces generated `ivy.xml` files to abide by - * maven conventions, this works in all of our test cases. The only scenario where it wouldn't work - * is those who have custom ivy.xml files *and* have pom.xml files which rely on those custom ivy.xml files, - * a very unlikely situation where the workaround is: "define a master configuration". - * - * Also see: http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html - * and: http://svn.apache.org/repos/asf/ant/ivy/core/tags/2.3.0/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java - * - * - */ -object ReplaceMavenConfigurationMappings { - - def addMappings(dd: DefaultDependencyDescriptor, scope: String, isOptional: Boolean) = { - val mapping = ReplaceMavenConfigurationMappings.REPLACEMENT_MAVEN_MAPPINGS.get(scope) - mapping.addMappingConfs(dd, isOptional) - } - - val REPLACEMENT_MAVEN_MAPPINGS = { - // Here we copy paste from Ivy - val REPLACEMENT_MAPPINGS = new java.util.HashMap[String, PomModuleDescriptorBuilder.ConfMapper] - - // NOTE - This code is copied from org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder - // except with altered default configurations... - REPLACEMENT_MAPPINGS.put("compile", new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("compile", "compile(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("compile", "master(compile)") - dd.addDependencyConfiguration("runtime", "runtime(*)") - } - } - }) - REPLACEMENT_MAPPINGS.put("provided", new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - dd.addDependencyConfiguration("optional", "provided(*)") - dd.addDependencyConfiguration("optional", "runtime(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("provided", "compile(*)") - dd.addDependencyConfiguration("provided", "provided(*)") - dd.addDependencyConfiguration("provided", "runtime(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("provided", "master(compile)") - } - } - }) - - REPLACEMENT_MAPPINGS.put("runtime", new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - if (isOptional) { - dd.addDependencyConfiguration("optional", "compile(*)") - dd.addDependencyConfiguration("optional", "provided(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("optional", "master(compile)") - } else { - dd.addDependencyConfiguration("runtime", "compile(*)") - dd.addDependencyConfiguration("runtime", "runtime(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("runtime", "master(compile)") - } - } - }) - - REPLACEMENT_MAPPINGS.put("test", new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - dd.addDependencyConfiguration("test", "runtime(*)") - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("test", "master(compile)") - } - }) - - REPLACEMENT_MAPPINGS.put("system", new PomModuleDescriptorBuilder.ConfMapper { - def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean): Unit = { - // FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there. - dd.addDependencyConfiguration("system", "master(compile)") - } - }) - - REPLACEMENT_MAPPINGS - } - - def init(): Unit = { - // Here we mutate a static final field, because we have to AND because it's evil. - try { - val map = PomModuleDescriptorBuilder.MAVEN2_CONF_MAPPING.asInstanceOf[java.util.Map[String, PomModuleDescriptorBuilder.ConfMapper]] - map.clear() - map.putAll(REPLACEMENT_MAVEN_MAPPINGS) - } catch { - case e: Exception => - // TODO - Log that Ivy may not be configured correctly and you could have maven/ivy issues. - throw new RuntimeException("FAILURE to install Ivy maven hooks. Your ivy-maven interaction may suffer resolution errors", e) - } - } -} diff --git a/ivy/src/main/scala/sbt/Artifact.scala b/ivy/src/main/scala/sbt/Artifact.scala deleted file mode 100644 index 4cafb0e67..000000000 --- a/ivy/src/main/scala/sbt/Artifact.scala +++ /dev/null @@ -1,144 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import java.net.URL -import sbt.serialization._ - -final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String, String]) { - def extra(attributes: (String, String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes)) -} - -import Configurations.{ config, Docs, Optional, Pom, Sources, Test } - -object Artifact { - def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None) - def apply(name: String, extra: Map[String, String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None, extra) - def apply(name: String, classifier: String): Artifact = Artifact(name, DefaultType, DefaultExtension, Some(classifier), Nil, None) - def apply(name: String, `type`: String, extension: String): Artifact = Artifact(name, `type`, extension, None, Nil, None) - def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Nil, None) - def apply(name: String, url: URL): Artifact = Artifact(name, extract(url, DefaultType), extract(url, DefaultExtension), None, Nil, Some(url)) - def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL]): Artifact = - Artifact(name, `type`, extension, classifier, configurations, url, Map.empty) - - val DefaultExtension = "jar" - val DefaultType = "jar" - - def sources(name: String) = classified(name, SourceClassifier) - def javadoc(name: String) = classified(name, DocClassifier) - def pom(name: String) = Artifact(name, PomType, PomType, None, Pom :: Nil, None) - - val DocClassifier = "javadoc" - val SourceClassifier = "sources" - val DocType = "doc" - val SourceType = "src" - val PomType = "pom" - val TestsClassifier = "tests" - - def extract(url: URL, default: String): String = extract(url.toString, default) - def extract(name: String, default: String): String = - { - val i = name.lastIndexOf('.') - if (i >= 0) - name.substring(i + 1) - else - default - } - def defaultArtifact(file: File) = - { - val name = file.getName - val i = name.lastIndexOf('.') - val base = if (i >= 0) name.substring(0, i) else name - Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL)) - } - def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = - { - import artifact._ - val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } - val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) - val base = CrossVersion.applyCross(artifact.name, cross) - base + "-" + module.revision + classifierStr + "." + artifact.extension - } - - val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs) - val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) - def classifierConf(classifier: String): Configuration = - if (classifier.startsWith(TestsClassifier)) - Test - else - classifierConfMap.getOrElse(classifier, Optional) - def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) - def classified(name: String, classifier: String): Artifact = - Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) - - private val optStringPickler = implicitly[Pickler[Option[String]]] - private val optStringUnpickler = implicitly[Unpickler[Option[String]]] - private val vectorConfigurationPickler = implicitly[Pickler[Vector[Configuration]]] - private val vectorConfigurationUnpickler = implicitly[Unpickler[Vector[Configuration]]] - private val stringStringMapPickler = implicitly[Pickler[Map[String, String]]] - private val stringStringMapUnpickler = implicitly[Unpickler[Map[String, String]]] - - implicit val pickler: Pickler[Artifact] = new Pickler[Artifact] { - val tag = implicitly[FastTypeTag[Artifact]] - val stringTag = implicitly[FastTypeTag[String]] - val optionStringTag = implicitly[FastTypeTag[Option[String]]] - val vectorConfigurationTag = implicitly[FastTypeTag[Vector[Configuration]]] - val stringStringMapTag = implicitly[FastTypeTag[Map[String, String]]] - def pickle(a: Artifact, builder: PBuilder): Unit = { - builder.pushHints() - builder.hintTag(tag) - builder.beginEntry(a) - builder.putField("name", { b => - b.hintTag(stringTag) - stringPickler.pickle(a.name, b) - }) - builder.putField("type", { b => - b.hintTag(stringTag) - stringPickler.pickle(a.`type`, b) - }) - builder.putField("extension", { b => - b.hintTag(stringTag) - stringPickler.pickle(a.extension, b) - }) - builder.putField("classifier", { b => - b.hintTag(optionStringTag) - optStringPickler.pickle(a.classifier, b) - }) - builder.putField("configurations", { b => - b.hintTag(vectorConfigurationTag) - vectorConfigurationPickler.pickle(a.configurations.toVector, b) - }) - builder.putField("url", { b => - b.hintTag(optionStringTag) - optStringPickler.pickle(a.url map { _.toString }, b) - }) - builder.putField("extraAttributes", { b => - b.hintTag(stringStringMapTag) - stringStringMapPickler.pickle(a.extraAttributes, b) - }) - builder.endEntry() - builder.popHints() - } - } - implicit val unpickler: Unpickler[Artifact] = new Unpickler[Artifact] { - val tag = implicitly[FastTypeTag[Artifact]] - def unpickle(tpe: String, reader: PReader): Any = { - reader.pushHints() - // reader.hintTag(tag) - reader.beginEntry() - val name = stringPickler.unpickleEntry(reader.readField("name")).asInstanceOf[String] - val tp = stringPickler.unpickleEntry(reader.readField("type")).asInstanceOf[String] - val extension = stringPickler.unpickleEntry(reader.readField("extension")).asInstanceOf[String] - val classifier = optStringUnpickler.unpickleEntry(reader.readField("classifier")).asInstanceOf[Option[String]] - val configurations = vectorConfigurationUnpickler.unpickleEntry(reader.readField("configurations")).asInstanceOf[Vector[Configuration]] - val u = optStringUnpickler.unpickleEntry(reader.readField("url")).asInstanceOf[Option[String]] map { new URL(_) } - val extraAttributes = stringStringMapUnpickler.unpickleEntry(reader.readField("extraAttributes")).asInstanceOf[Map[String, String]] - val result = Artifact(name, tp, extension, classifier, configurations, u, extraAttributes) - reader.endEntry() - reader.popHints() - result - } - } -} diff --git a/ivy/src/main/scala/sbt/CircularDependencyLevel.scala b/ivy/src/main/scala/sbt/CircularDependencyLevel.scala deleted file mode 100644 index 3c2f62bc6..000000000 --- a/ivy/src/main/scala/sbt/CircularDependencyLevel.scala +++ /dev/null @@ -1,27 +0,0 @@ -package sbt - -import org.apache.ivy.plugins.circular.{ CircularDependencyStrategy, WarnCircularDependencyStrategy, IgnoreCircularDependencyStrategy, ErrorCircularDependencyStrategy } - -/** - * Wrapper around circular dependency strategy. - */ -sealed trait CircularDependencyLevel { - private[sbt] def ivyStrategy: CircularDependencyStrategy - private[sbt] def name: String - override def toString: String = name -} - -object CircularDependencyLevel { - val Warn: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = WarnCircularDependencyStrategy.getInstance - def name: String = "warn" - } - val Ignore: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = IgnoreCircularDependencyStrategy.getInstance - def name: String = "ignore" - } - val Error: CircularDependencyLevel = new CircularDependencyLevel { - def ivyStrategy: CircularDependencyStrategy = ErrorCircularDependencyStrategy.getInstance - def name: String = "error" - } -} diff --git a/ivy/src/main/scala/sbt/ComponentManager.scala b/ivy/src/main/scala/sbt/ComponentManager.scala deleted file mode 100644 index bdf78b9ec..000000000 --- a/ivy/src/main/scala/sbt/ComponentManager.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.{ File, FileOutputStream } -import java.util.concurrent.Callable - -/** - * A component manager provides access to the pieces of xsbt that are distributed as components. - * There are two types of components. The first type is compiled subproject jars with their dependencies. - * The second type is a subproject distributed as a source jar so that it can be compiled against a specific - * version of Scala. - * - * The component manager provides services to install and retrieve components to the local repository. - * This is used for compiled source jars so that the compilation need not be repeated for other projects on the same - * machine. - */ -class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, ivyHome: Option[File], val log: Logger) { - private[this] val ivyCache = new IvyCache(ivyHome) - /** Get all of the files for component 'id', throwing an exception if no files exist for the component. */ - def files(id: String)(ifMissing: IfMissing): Iterable[File] = - { - def fromGlobal = - lockGlobalCache { - try { update(id); getOrElse(createAndCache) } - catch { case e: NotInCache => createAndCache } - } - def getOrElse(orElse: => Iterable[File]): Iterable[File] = - { - val existing = provider.component(id) - if (existing.isEmpty) orElse else existing - } - def notFound = invalid("Could not find required component '" + id + "'") - def createAndCache = - ifMissing match { - case IfMissing.Fail => notFound - case d: IfMissing.Define => - d() - if (d.cache) cache(id) - getOrElse(notFound) - case f: IfMissing.Fallback => - f() - } - - lockLocalCache { getOrElse(fromGlobal) } - } - /** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */ - private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)(action) - /** This is used to ensure atomic access to components in the global Ivy cache.*/ - private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)(action) - private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action }) - /** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */ - def file(id: String)(ifMissing: IfMissing): File = - files(id)(ifMissing).toList match { - case x :: Nil => x - case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", ")) - } - private def invalid(msg: String) = throw new InvalidComponent(msg) - private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e) - - def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(id, files.toSeq.toArray) } - /** Retrieve the file for component 'id' from the local repository. */ - private def update(id: String): Unit = ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar))) - - private def sbtModuleID(id: String) = ModuleID(SbtArtifacts.Organization, id, ComponentManager.stampedVersion) - /** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */ - def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log) - def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) } -} -class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) { - def this(msg: String) = this(msg, null) -} -sealed trait IfMissing extends NotNull -object IfMissing { - object Fail extends IfMissing - final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define } - final class Fallback(f: => Iterable[File]) extends IfMissing { def apply() = f } -} -object ComponentManager { - lazy val (version, timestamp) = - { - val properties = new java.util.Properties - val propertiesStream = versionResource.openStream - try { properties.load(propertiesStream) } finally { propertiesStream.close() } - (properties.getProperty("version"), properties.getProperty("timestamp")) - } - lazy val stampedVersion = version + "_" + timestamp - - import java.net.URL - private def versionResource: URL = getClass.getResource("/xsbt.version.properties") -} diff --git a/ivy/src/main/scala/sbt/Configuration.scala b/ivy/src/main/scala/sbt/Configuration.scala deleted file mode 100644 index d2307b89d..000000000 --- a/ivy/src/main/scala/sbt/Configuration.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import sbt.serialization._ - -object Configurations { - def config(name: String) = new Configuration(name) - def default: Seq[Configuration] = defaultMavenConfigurations - def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional) - def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal) - def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom) - def names(cs: Seq[Configuration]) = cs.map(_.name) - - lazy val RuntimeInternal = optionalInternal(Runtime) - lazy val TestInternal = fullInternal(Test) - lazy val IntegrationTestInternal = fullInternal(IntegrationTest) - lazy val CompileInternal = fullInternal(Compile) - - def internalMap(c: Configuration) = c match { - case Compile => CompileInternal - case Test => TestInternal - case Runtime => RuntimeInternal - case IntegrationTest => IntegrationTestInternal - case _ => c - } - - def internal(base: Configuration, ext: Configuration*) = config(base.name + "-internal") extend (ext: _*) hide - def fullInternal(base: Configuration): Configuration = internal(base, base, Optional, Provided) - def optionalInternal(base: Configuration): Configuration = internal(base, base, Optional) - - lazy val Default = config("default") - lazy val Compile = config("compile") - lazy val IntegrationTest = config("it") extend (Runtime) - lazy val Provided = config("provided") - lazy val Docs = config("docs") - lazy val Runtime = config("runtime") extend (Compile) - lazy val Test = config("test") extend (Runtime) - lazy val Sources = config("sources") - lazy val System = config("system") - lazy val Optional = config("optional") - lazy val Pom = config("pom") - - lazy val ScalaTool = config("scala-tool") hide - lazy val CompilerPlugin = config("plugin") hide - lazy val Component = config("component") hide - - private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true) - private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false) - private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration - private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) Configurations.Compile else Configurations.Default - private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*) -} -/** Represents an Ivy configuration. */ -final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) { - require(name != null && !name.isEmpty) - require(description != null) - def this(name: String) = this(name, "", true, Nil, true) - def describedAs(newDescription: String) = Configuration(name, newDescription, isPublic, extendsConfigs, transitive) - def extend(configs: Configuration*) = Configuration(name, description, isPublic, configs.toList ::: extendsConfigs, transitive) - def notTransitive = intransitive - def intransitive = Configuration(name, description, isPublic, extendsConfigs, false) - def hide = Configuration(name, description, false, extendsConfigs, transitive) - override def toString = name -} -object Configuration { - implicit val pickler: Pickler[Configuration] with Unpickler[Configuration] = PicklerUnpickler.generate[Configuration] -} diff --git a/ivy/src/main/scala/sbt/ConflictWarning.scala b/ivy/src/main/scala/sbt/ConflictWarning.scala deleted file mode 100644 index a4e011736..000000000 --- a/ivy/src/main/scala/sbt/ConflictWarning.scala +++ /dev/null @@ -1,79 +0,0 @@ -package sbt - -import DependencyFilter._ - -/** - * Provide warnings for cross version conflicts. - * A library foo_2.10 and foo_2.11 can potentially be both included on the - * library dependency graph by mistake, but it won't be caught by eviction. - */ -final case class ConflictWarning(label: String, level: Level.Value, failOnConflict: Boolean) { - @deprecated("`filter` is no longer used", "0.13.0") - val filter: ModuleFilter = (_: ModuleID) => false - @deprecated("`group` is no longer used", "0.13.0") - val group: ModuleID => String = ConflictWarning.org -} -object ConflictWarning { - @deprecated("`group` and `filter` are no longer used. Use a standard Ivy conflict manager.", "0.13.0") - def apply(label: String, filter: ModuleFilter, group: ModuleID => String, level: Level.Value, failOnConflict: Boolean): ConflictWarning = - ConflictWarning(label, level, failOnConflict) - - def disable: ConflictWarning = ConflictWarning("", Level.Debug, false) - - private def org = (_: ModuleID).organization - private[this] def idString(org: String, name: String) = s"$org:$name" - - def default(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true) - - @deprecated("Warning on evicted modules is no longer done, so this is the same as `default`. Use a standard Ivy conflict manager.", "0.13.0") - def strict(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true) - - def apply(config: ConflictWarning, report: UpdateReport, log: Logger): Unit = { - processCrossVersioned(config, report, log) - } - private[this] def processCrossVersioned(config: ConflictWarning, report: UpdateReport, log: Logger): Unit = { - val crossMismatches = crossVersionMismatches(report) - if (crossMismatches.nonEmpty) { - val pre = s"Modules were resolved with conflicting cross-version suffixes in ${config.label}:\n " - val conflictMsgs = - for (((org, rawName), fullNames) <- crossMismatches) yield { - val suffixes = fullNames.map(getCrossSuffix).mkString(", ") - s"${idString(org, rawName)} $suffixes" - } - log.log(config.level, conflictMsgs.mkString(pre, "\n ", "")) - if (config.failOnConflict) { - val summary = crossMismatches.map { case ((org, raw), _) => idString(org, raw) }.mkString(", ") - sys.error("Conflicting cross-version suffixes in: " + summary) - } - } - } - - /** Map from (organization, rawName) to set of multiple full names. */ - def crossVersionMismatches(report: UpdateReport): Map[(String, String), Set[String]] = - { - val mismatches = report.configurations.flatMap { confReport => - groupByRawName(confReport.allModules).mapValues { modules => - val differentFullNames = modules.map(_.name).toSet - if (differentFullNames.size > 1) differentFullNames else Set.empty[String] - } - } - (Map.empty[(String, String), Set[String]] /: mismatches)(merge) - } - private[this] def merge[A, B](m: Map[A, Set[B]], b: (A, Set[B])): Map[A, Set[B]] = - if (b._2.isEmpty) m else - m.updated(b._1, m.getOrElse(b._1, Set.empty) ++ b._2) - - private[this] def groupByRawName(ms: Seq[ModuleID]): Map[(String, String), Seq[ModuleID]] = - ms.groupBy(m => (m.organization, dropCrossSuffix(m.name))) - - private[this] val CrossSuffixPattern = """(.+)_(\d+\.\d+(?:\.\d+)?(?:-.+)?)""".r - private[this] def dropCrossSuffix(s: String): String = s match { - case CrossSuffixPattern(raw, _) => raw - case _ => s - } - private[this] def getCrossSuffix(s: String): String = s match { - case CrossSuffixPattern(_, v) => "_" + v - case _ => "" - } - -} diff --git a/ivy/src/main/scala/sbt/ConvertResolver.scala b/ivy/src/main/scala/sbt/ConvertResolver.scala deleted file mode 100644 index 408c0b92f..000000000 --- a/ivy/src/main/scala/sbt/ConvertResolver.scala +++ /dev/null @@ -1,282 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.net.URL -import java.util.Collections -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.module.descriptor.DependencyDescriptor -import org.apache.ivy.core.resolve.ResolveData -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.repository.{ RepositoryCopyProgressListener, TransferEvent } -import org.apache.ivy.plugins.resolver.{ BasicResolver, DependencyResolver, IBiblioResolver, RepositoryResolver } -import org.apache.ivy.plugins.resolver.{ AbstractPatternsBasedResolver, AbstractSshBasedResolver, FileSystemResolver, SFTPResolver, SshResolver, URLResolver } -import org.apache.ivy.plugins.repository.url.{ URLRepository => URLRepo } -import org.apache.ivy.plugins.repository.file.{ FileRepository => FileRepo, FileResource } -import java.io.{ IOException, File } -import org.apache.ivy.util.{ FileUtil, ChecksumHelper } -import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact } - -private[sbt] object ConvertResolver { - import UpdateOptions.ResolverConverter - - /** - * This class contains all the reflective lookups used in the - * checksum-friendly URL publishing shim. - */ - private object ChecksumFriendlyURLResolver { - // TODO - When we dump JDK6 support we can remove this hackery - // import java.lang.reflect.AccessibleObject - type AccessibleObject = { - def setAccessible(value: Boolean): Unit - } - private def reflectiveLookup[A <: AccessibleObject](f: Class[_] => A): Option[A] = - try { - val cls = classOf[RepositoryResolver] - val thing = f(cls) - import scala.language.reflectiveCalls - thing.setAccessible(true) - Some(thing) - } catch { - case (_: java.lang.NoSuchFieldException) | - (_: java.lang.SecurityException) | - (_: java.lang.NoSuchMethodException) => None - } - private val signerNameField: Option[java.lang.reflect.Field] = - reflectiveLookup(_.getDeclaredField("signerName")) - private val putChecksumMethod: Option[java.lang.reflect.Method] = - reflectiveLookup(_.getDeclaredMethod("putChecksum", - classOf[IArtifact], classOf[File], classOf[String], - classOf[Boolean], classOf[String])) - private val putSignatureMethod: Option[java.lang.reflect.Method] = - reflectiveLookup(_.getDeclaredMethod("putSignature", - classOf[IArtifact], classOf[File], classOf[String], - classOf[Boolean])) - } - /** - * The default behavior of ivy's overwrite flags ignores the fact that a lot of repositories - * will autogenerate checksums *for* an artifact if it doesn't already exist. Therefore - * if we succeed in publishing an artifact, we need to just blast the checksums in place. - * This acts as a "shim" on RepositoryResolvers so that we can hook our methods into - * both the IBiblioResolver + URLResolver without having to duplicate the code in two - * places. However, this does mean our use of reflection is awesome. - * - * TODO - See about contributing back to ivy. - */ - private trait ChecksumFriendlyURLResolver extends RepositoryResolver { - import ChecksumFriendlyURLResolver._ - private def signerName: String = signerNameField match { - case Some(field) => field.get(this).asInstanceOf[String] - case None => null - } - override protected def put(artifact: IArtifact, src: File, dest: String, overwrite: Boolean): Unit = { - // verify the checksum algorithms before uploading artifacts! - val checksums = getChecksumAlgorithms() - val repository = getRepository() - for { - checksum <- checksums - if !ChecksumHelper.isKnownAlgorithm(checksum) - } throw new IllegalArgumentException("Unknown checksum algorithm: " + checksum) - repository.put(artifact, src, dest, overwrite); - // Fix for sbt#1156 - Artifactory will auto-generate MD5/sha1 files, so - // we need to overwrite what it has. - for (checksum <- checksums) { - putChecksumMethod match { - case Some(method) => method.invoke(this, artifact, src, dest, true: java.lang.Boolean, checksum) - case None => // TODO - issue warning? - } - } - if (signerName != null) { - putSignatureMethod match { - case None => () - case Some(method) => method.invoke(artifact, src, dest, true: java.lang.Boolean) - } - } - } - } - - /** Converts the given sbt resolver into an Ivy resolver. */ - @deprecated("Use the variant with updateOptions", "0.13.8") - def apply(r: Resolver, settings: IvySettings, log: Logger): DependencyResolver = - apply(r, settings, UpdateOptions(), log) - - /** Converts the given sbt resolver into an Ivy resolver. */ - def apply(r: Resolver, settings: IvySettings, updateOptions: UpdateOptions, log: Logger): DependencyResolver = - (updateOptions.resolverConverter orElse defaultConvert)((r, settings, log)) - - /** The default implementation of converter. */ - lazy val defaultConvert: ResolverConverter = { - case (r, settings, log) => - r match { - case repo: MavenRepository => - { - val pattern = Collections.singletonList(Resolver.resolvePattern(repo.root, Resolver.mavenStyleBasePattern)) - final class PluginCapableResolver extends IBiblioResolver with ChecksumFriendlyURLResolver with DescriptorRequired { - def setPatterns(): Unit = { - // done this way for access to protected methods. - setArtifactPatterns(pattern) - setIvyPatterns(pattern) - } - } - val resolver = new PluginCapableResolver - if (repo.localIfFile) resolver.setRepository(new LocalIfFileRepo) - initializeMavenStyle(resolver, repo.name, repo.root) - resolver.setPatterns() // has to be done after initializeMavenStyle, which calls methods that overwrite the patterns - resolver - } - // TODO: HTTP repository is no longer recommended. #1541 - // Remove `JavaNet1Repository` when we bump up the API. - case r: JavaNet1Repository => - { - // Thanks to Matthias Pfau for posting how to use the Maven 1 repository on java.net with Ivy: - // http://www.nabble.com/Using-gradle-Ivy-with-special-maven-repositories-td23775489.html - val resolver = new IBiblioResolver with DescriptorRequired { override def convertM2IdForResourceSearch(mrid: ModuleRevisionId) = mrid } - initializeMavenStyle(resolver, JavaNet1Repository.name, "http://download.java.net/maven/1/") - resolver.setPattern("[organisation]/[ext]s/[module]-[revision](-[classifier]).[ext]") - resolver - } - case repo: SshRepository => - { - val resolver = new SshResolver with DescriptorRequired - initializeSSHResolver(resolver, repo, settings) - repo.publishPermissions.foreach(perm => resolver.setPublishPermissions(perm)) - resolver - } - case repo: SftpRepository => - { - val resolver = new SFTPResolver - initializeSSHResolver(resolver, repo, settings) - resolver - } - case repo: FileRepository => - { - val resolver = new FileSystemResolver with DescriptorRequired { - // Workaround for #1156 - // Temporarily in sbt 0.13.x we deprecate overwriting - // in local files for non-changing revisions. - // This will be fully enforced in sbt 1.0. - setRepository(new WarnOnOverwriteFileRepo()) - } - resolver.setName(repo.name) - initializePatterns(resolver, repo.patterns, settings) - import repo.configuration.{ isLocal, isTransactional } - resolver.setLocal(isLocal) - isTransactional.foreach(value => resolver.setTransactional(value.toString)) - resolver - } - case repo: URLRepository => - { - val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired - resolver.setName(repo.name) - initializePatterns(resolver, repo.patterns, settings) - resolver - } - case repo: ChainedResolver => IvySbt.resolverChain(repo.name, repo.resolvers, false, settings, log) - case repo: RawRepository => repo.resolver - } - } - - private sealed trait DescriptorRequired extends BasicResolver { - override def getDependency(dd: DependencyDescriptor, data: ResolveData) = - { - val prev = descriptorString(isAllownomd) - setDescriptor(descriptorString(hasExplicitURL(dd))) - try super.getDependency(dd, data) finally setDescriptor(prev) - } - def descriptorString(optional: Boolean) = - if (optional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED - def hasExplicitURL(dd: DependencyDescriptor): Boolean = - dd.getAllDependencyArtifacts.exists(_.getUrl != null) - } - private def initializeMavenStyle(resolver: IBiblioResolver, name: String, root: String): Unit = { - resolver.setName(name) - resolver.setM2compatible(true) - resolver.setRoot(root) - } - private def initializeSSHResolver(resolver: AbstractSshBasedResolver, repo: SshBasedRepository, settings: IvySettings): Unit = { - resolver.setName(repo.name) - resolver.setPassfile(null) - initializePatterns(resolver, repo.patterns, settings) - initializeConnection(resolver, repo.connection) - } - private def initializeConnection(resolver: AbstractSshBasedResolver, connection: RepositoryHelpers.SshConnection): Unit = { - import resolver._ - import connection._ - hostname.foreach(setHost) - port.foreach(setPort) - authentication foreach - { - case RepositoryHelpers.PasswordAuthentication(user, password) => - setUser(user) - password.foreach(setUserPassword) - case RepositoryHelpers.KeyFileAuthentication(user, file, password) => - setKeyFile(file) - password.foreach(setKeyFilePassword) - setUser(user) - } - } - private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: Patterns, settings: IvySettings): Unit = { - resolver.setM2compatible(patterns.isMavenCompatible) - resolver.setDescriptor(if (patterns.descriptorOptional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED) - resolver.setCheckconsistency(!patterns.skipConsistencyCheck) - patterns.ivyPatterns.foreach(p => resolver.addIvyPattern(settings substitute p)) - patterns.artifactPatterns.foreach(p => resolver.addArtifactPattern(settings substitute p)) - } - /** - * A custom Ivy URLRepository that returns FileResources for file URLs. - * This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache. - */ - private[this] final class LocalIfFileRepo extends URLRepo { - private[this] val repo = new WarnOnOverwriteFileRepo() - private[this] val progress = new RepositoryCopyProgressListener(this); - override def getResource(source: String) = { - val url = new URL(source) - if (url.getProtocol == IO.FileScheme) - new FileResource(repo, IO.toFile(url)) - else - super.getResource(source) - } - - override def put(source: File, destination: String, overwrite: Boolean): Unit = { - val url = new URL(destination) - if (url.getProtocol != IO.FileScheme) super.put(source, destination, overwrite) - else { - // Here we duplicate the put method for files so we don't just bail on trying ot use Http handler - val resource = getResource(destination) - if (!overwrite && resource.exists()) { - throw new IOException("destination file exists and overwrite == false"); - } - fireTransferInitiated(resource, TransferEvent.REQUEST_PUT); - try { - var totalLength = source.length - if (totalLength > 0) { - progress.setTotalLength(totalLength); - } - FileUtil.copy(source, new java.io.File(url.toURI), progress, overwrite) - } catch { - case ex: IOException => - fireTransferError(ex) - throw ex - case ex: RuntimeException => - fireTransferError(ex) - throw ex - } finally { - progress.setTotalLength(null); - } - } - } - } - - private[this] final class WarnOnOverwriteFileRepo extends FileRepo() { - override def put(source: java.io.File, destination: String, overwrite: Boolean): Unit = { - try super.put(source, destination, overwrite) - catch { - case e: java.io.IOException if e.getMessage.contains("destination already exists") => - import org.apache.ivy.util.Message - Message.warn(s"Attempting to overwrite $destination\n\tThis usage is deprecated and will be removed in sbt 1.0.") - super.put(source, destination, true) - } - } - } -} diff --git a/ivy/src/main/scala/sbt/Credentials.scala b/ivy/src/main/scala/sbt/Credentials.scala deleted file mode 100644 index 464d5de8e..000000000 --- a/ivy/src/main/scala/sbt/Credentials.scala +++ /dev/null @@ -1,71 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.File -import org.apache.ivy.util.url.CredentialsStore - -object Credentials { - def apply(realm: String, host: String, userName: String, passwd: String): Credentials = - new DirectCredentials(realm, host, userName, passwd) - def apply(file: File): Credentials = - new FileCredentials(file) - - /** Add the provided credentials to Ivy's credentials cache.*/ - def add(realm: String, host: String, userName: String, passwd: String): Unit = - CredentialsStore.INSTANCE.addCredentials(realm, host, userName, passwd) - /** Load credentials from the given file into Ivy's credentials cache.*/ - def add(path: File, log: Logger): Unit = - loadCredentials(path) match { - case Left(err) => log.warn(err) - case Right(dc) => add(dc.realm, dc.host, dc.userName, dc.passwd) - } - - def forHost(sc: Seq[Credentials], host: String) = allDirect(sc) find { _.host == host } - def allDirect(sc: Seq[Credentials]): Seq[DirectCredentials] = sc map toDirect - def toDirect(c: Credentials): DirectCredentials = c match { - case dc: DirectCredentials => dc - case fc: FileCredentials => loadCredentials(fc.path) match { - case Left(err) => sys.error(err) - case Right(dc) => dc - } - } - - def loadCredentials(path: File): Either[String, DirectCredentials] = - if (path.exists) { - val properties = read(path) - def get(keys: List[String]) = keys.flatMap(properties.get).headOption.toRight(keys.head + " not specified in credentials file: " + path) - - IvyUtil.separate(List(RealmKeys, HostKeys, UserKeys, PasswordKeys).map(get)) match { - case (Nil, List(realm, host, user, pass)) => Right(new DirectCredentials(realm, host, user, pass)) - case (errors, _) => Left(errors.mkString("\n")) - } - } else - Left("Credentials file " + path + " does not exist") - - def register(cs: Seq[Credentials], log: Logger): Unit = - cs foreach { - case f: FileCredentials => add(f.path, log) - case d: DirectCredentials => add(d.realm, d.host, d.userName, d.passwd) - } - - private[this] val RealmKeys = List("realm") - private[this] val HostKeys = List("host", "hostname") - private[this] val UserKeys = List("user", "user.name", "username") - private[this] val PasswordKeys = List("password", "pwd", "pass", "passwd") - - import collection.JavaConversions._ - private[this] def read(from: File): Map[String, String] = - { - val properties = new java.util.Properties - IO.load(properties, from) - properties map { case (k, v) => (k.toString, v.toString.trim) } toMap; - } -} - -sealed trait Credentials -final class FileCredentials(val path: File) extends Credentials { - override def toString = "FileCredentials('" + path + "')" -} -final class DirectCredentials(val realm: String, val host: String, val userName: String, val passwd: String) extends Credentials diff --git a/ivy/src/main/scala/sbt/CrossVersion.scala b/ivy/src/main/scala/sbt/CrossVersion.scala deleted file mode 100644 index b17ac525c..000000000 --- a/ivy/src/main/scala/sbt/CrossVersion.scala +++ /dev/null @@ -1,209 +0,0 @@ -package sbt - -import cross.CrossVersionUtil -import sbt.serialization._ - -final case class ScalaVersion(full: String, binary: String) - -/** Configures how a module will be cross-versioned. */ -sealed trait CrossVersion - -object CrossVersion { - /** The first `major.minor` Scala version that the Scala binary version should be used for cross-versioning instead of the full version. */ - val TransitionScalaVersion = CrossVersionUtil.TransitionScalaVersion - - /** The first `major.minor` sbt version that the sbt binary version should be used for cross-versioning instead of the full version. */ - val TransitionSbtVersion = CrossVersionUtil.TransitionSbtVersion - - /** Disables cross versioning for a module.*/ - object Disabled extends CrossVersion { override def toString = "disabled" } - - /** - * Cross-versions a module using the result of applying `remapVersion` to the binary version. - * For example, if `remapVersion = v => "2.10"` and the binary version is "2.9.2" or "2.10", - * the module is cross-versioned with "2.10". - */ - final class Binary(val remapVersion: String => String) extends CrossVersion { - override def toString = "Binary" - } - - /** - * Cross-versions a module with the result of applying `remapVersion` to the full version. - * For example, if `remapVersion = v => "2.10"` and the full version is "2.9.2" or "2.10.3", - * the module is cross-versioned with "2.10". - */ - final class Full(val remapVersion: String => String) extends CrossVersion { - override def toString = "Full" - } - - private val disabledTag = implicitly[FastTypeTag[Disabled.type]] - private val binaryTag = implicitly[FastTypeTag[Binary]] - private val fullTag = implicitly[FastTypeTag[Full]] - implicit val pickler: Pickler[CrossVersion] = new Pickler[CrossVersion] { - val tag = implicitly[FastTypeTag[CrossVersion]] - def pickle(a: CrossVersion, builder: PBuilder): Unit = { - builder.pushHints() - builder.hintTag(a match { - case Disabled => disabledTag - case x: Binary => binaryTag - case x: Full => fullTag - }) - builder.beginEntry(a) - builder.endEntry() - builder.popHints() - } - } - implicit val unpickler: Unpickler[CrossVersion] = new Unpickler[CrossVersion] { - val tag = implicitly[FastTypeTag[CrossVersion]] - def unpickle(tpe: String, reader: PReader): Any = { - reader.pushHints() - reader.hintTag(tag) - val tpeStr = reader.beginEntry() - val tpe = scala.pickling.FastTypeTag(tpeStr) - // sys.error(tpe.toString) - val result = tpe match { - case t if t == disabledTag => Disabled - case t if t == binaryTag => binary - case t if t == fullTag => full - } - reader.endEntry() - reader.popHints() - result - } - } - - /** Cross-versions a module with the full version (typically the full Scala version). */ - def full: CrossVersion = new Full(idFun) - - /** - * Cross-versions a module with the result of applying `remapVersion` to the full version - * (typically the full Scala version). See also [[sbt.CrossVersion.Full]]. - */ - def fullMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion) - - /** Cross-versions a module with the binary version (typically the binary Scala version). */ - def binary: CrossVersion = new Binary(idFun) - - /** - * Cross-versions a module with the result of applying `remapVersion` to the binary version - * (typically the binary Scala version). See also [[sbt.CrossVersion.Binary]]. - */ - def binaryMapped(remapVersion: String => String): CrossVersion = new Binary(remapVersion) - - private[this] def idFun[T]: T => T = x => x - - @deprecated("Will be made private.", "0.13.1") - def append(s: String): Option[String => String] = Some(x => crossName(x, s)) - - /** - * Construct a cross-versioning function given cross-versioning configuration `cross`, - * full version `fullVersion` and binary version `binaryVersion`. The behavior of the - * constructed function is as documented for the [[sbt.CrossVersion]] datatypes. - */ - def apply(cross: CrossVersion, fullVersion: String, binaryVersion: String): Option[String => String] = - cross match { - case Disabled => None - case b: Binary => append(b.remapVersion(binaryVersion)) - case f: Full => append(f.remapVersion(fullVersion)) - } - - /** Constructs the cross-version function defined by `module` and `is`, if one is configured. */ - def apply(module: ModuleID, is: IvyScala): Option[String => String] = - CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion) - - /** Constructs the cross-version function defined by `module` and `is`, if one is configured. */ - def apply(module: ModuleID, is: Option[IvyScala]): Option[String => String] = - is flatMap { i => apply(module, i) } - - /** Cross-version each `Artifact` in `artifacts` according to cross-version function `cross`. */ - def substituteCross(artifacts: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] = - cross match { - case None => artifacts - case Some(is) => substituteCrossA(artifacts, cross) - } - - @deprecated("Will be made private.", "0.13.1") - def applyCross(s: String, fopt: Option[String => String]): String = - fopt match { - case None => s - case Some(fopt) => fopt(s) - } - - @deprecated("Will be made private.", "0.13.1") - def crossName(name: String, cross: String): String = - name + "_" + cross - - /** Cross-versions `exclude` according to its `crossVersion`. */ - private[sbt] def substituteCross(exclude: SbtExclusionRule, is: Option[IvyScala]): SbtExclusionRule = { - val fopt: Option[String => String] = - is flatMap { i => CrossVersion(exclude.crossVersion, i.scalaFullVersion, i.scalaBinaryVersion) } - exclude.copy(name = applyCross(exclude.name, fopt)) - } - - /** Cross-versions `a` according to cross-version function `cross`. */ - def substituteCross(a: Artifact, cross: Option[String => String]): Artifact = - a.copy(name = applyCross(a.name, cross)) - - @deprecated("Will be made private.", "0.13.1") - def substituteCrossA(as: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] = - as.map(art => substituteCross(art, cross)) - - /** - * Constructs a function that will cross-version a ModuleID - * for the given full and binary Scala versions `scalaFullVersion` and `scalaBinaryVersion` - * according to the ModuleID's cross-versioning setting. - */ - def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m => - { - val cross = apply(m.crossVersion, scalaFullVersion, scalaBinaryVersion) - if (cross.isDefined) - m.copy(name = applyCross(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross)) - else - m - } - - @deprecated("Use CrossVersion.isScalaApiCompatible or CrossVersion.isSbtApiCompatible", "0.13.0") - def isStable(v: String): Boolean = isScalaApiCompatible(v) - - @deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0") - def selectVersion(full: String, binary: String): String = if (isStable(full)) binary else full - - def isSbtApiCompatible(v: String): Boolean = CrossVersionUtil.isSbtApiCompatible(v) - - /** - * Returns sbt binary interface x.y API compatible with the given version string v. - * RCs for x.y.0 are considered API compatible. - * Compatibile versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12). - */ - def sbtApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.sbtApiVersion(v) - - def isScalaApiCompatible(v: String): Boolean = CrossVersionUtil.isScalaApiCompatible(v) - - /** - * Returns Scala binary interface x.y API compatible with the given version string v. - * Compatibile versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1. - */ - def scalaApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.scalaApiVersion(v) - - /** Regular expression that extracts the major and minor components of a version into matched groups 1 and 2.*/ - val PartialVersion = CrossVersionUtil.PartialVersion - - /** Extracts the major and minor components of a version string `s` or returns `None` if the version is improperly formatted. */ - def partialVersion(s: String): Option[(Int, Int)] = CrossVersionUtil.partialVersion(s) - - /** - * Computes the binary Scala version from the `full` version. - * Full Scala versions earlier than [[sbt.CrossVersion.TransitionScalaVersion]] are returned as is. - */ - def binaryScalaVersion(full: String): String = CrossVersionUtil.binaryScalaVersion(full) - - /** - * Computes the binary sbt version from the `full` version. - * Full sbt versions earlier than [[sbt.CrossVersion.TransitionSbtVersion]] are returned as is. - */ - def binarySbtVersion(full: String): String = CrossVersionUtil.binarySbtVersion(full) - - @deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0") - def binaryVersion(full: String, cutoff: String): String = CrossVersionUtil.binaryVersion(full, cutoff) - -} diff --git a/ivy/src/main/scala/sbt/CustomPomParser.scala b/ivy/src/main/scala/sbt/CustomPomParser.scala deleted file mode 100644 index be98bfe25..000000000 --- a/ivy/src/main/scala/sbt/CustomPomParser.scala +++ /dev/null @@ -1,217 +0,0 @@ -package sbt - -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.module.descriptor.{ DefaultArtifact, DefaultExtendsDescriptor, DefaultModuleDescriptor, ModuleDescriptor } -import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DependencyDescriptor } -import org.apache.ivy.plugins.parser.{ ModuleDescriptorParser, ModuleDescriptorParserRegistry, ParserSettings } -import org.apache.ivy.plugins.parser.m2.{ ReplaceMavenConfigurationMappings, PomModuleDescriptorBuilder, PomModuleDescriptorParser } -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.namespace.NamespaceTransformer -import org.apache.ivy.util.extendable.ExtendableItem - -import java.io.{ File, InputStream } -import java.net.URL -import java.util.regex.Pattern -import sbt.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties } - -@deprecated("We now use an Aether-based pom parser.", "0.13.8") -final class CustomPomParser(delegate: ModuleDescriptorParser, transform: (ModuleDescriptorParser, ModuleDescriptor) => ModuleDescriptor) extends ModuleDescriptorParser { - override def parseDescriptor(ivySettings: ParserSettings, descriptorURL: URL, validate: Boolean) = - transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, validate)) - - override def parseDescriptor(ivySettings: ParserSettings, descriptorURL: URL, res: Resource, validate: Boolean) = - transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, res, validate)) - - override def toIvyFile(is: InputStream, res: Resource, destFile: File, md: ModuleDescriptor) = delegate.toIvyFile(is, res, destFile, md) - - override def accept(res: Resource) = delegate.accept(res) - override def getType() = delegate.getType() - override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res) -} -@deprecated("We now use an Aether-based pom parser.", "0.13.8") -object CustomPomParser { - - // Evil hackery to override the default maven pom mappings. - ReplaceMavenConfigurationMappings.init() - - /** The key prefix that indicates that this is used only to store extra information and is not intended for dependency resolution.*/ - val InfoKeyPrefix = SbtPomExtraProperties.POM_INFO_KEY_PREFIX - val ApiURLKey = SbtPomExtraProperties.POM_API_KEY - - val SbtVersionKey = PomExtraDependencyAttributes.SbtVersionKey - val ScalaVersionKey = PomExtraDependencyAttributes.ScalaVersionKey - val ExtraAttributesKey = PomExtraDependencyAttributes.ExtraAttributesKey - private[this] val unqualifiedKeys = Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey) - - // packagings that should be jars, but that Ivy doesn't handle as jars - // TODO - move this elsewhere. - val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar") - val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform) - - private[this] val TransformedHashKey = "e:sbtTransformHash" - // A hash of the parameters transformation is based on. - // If a descriptor has a different hash, we need to retransform it. - private[this] def makeCoords(mrid: ModuleRevisionId): String = s"${mrid.getOrganisation}:${mrid.getName}:${mrid.getRevision}" - - // We now include the ModuleID in a hash, to ensure that parent-pom transformations don't corrupt child poms. - private[this] def MakeTransformHash(md: ModuleDescriptor): String = { - val coords: String = makeCoords(md.getModuleRevisionId) - - hash((unqualifiedKeys ++ JarPackagings ++ Set(coords)).toSeq.sorted) - } - - private[this] def hash(ss: Seq[String]): String = Hash.toHex(Hash(ss.flatMap(_ getBytes "UTF-8").toArray)) - - // Unfortunately, ModuleDescriptorParserRegistry is add-only and is a singleton instance. - lazy val registerDefault: Unit = ModuleDescriptorParserRegistry.getInstance.addParser(default) - - def defaultTransform(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor = - if (transformedByThisVersion(md)) md - else defaultTransformImpl(parser, md) - - private[this] def transformedByThisVersion(md: ModuleDescriptor): Boolean = - { - val oldTransformedHashKey = "sbtTransformHash" - val extraInfo = md.getExtraInfo - val MyHash = MakeTransformHash(md) - // sbt 0.13.1 used "sbtTransformHash" instead of "e:sbtTransformHash" until #1192 so read both - Option(extraInfo).isDefined && - ((Option(extraInfo get TransformedHashKey) orElse Option(extraInfo get oldTransformedHashKey)) match { - case Some(MyHash) => true - case _ => false - }) - } - - private[this] def defaultTransformImpl(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor = - { - val properties = getPomProperties(md) - - // Extracts extra attributes (currently, sbt and Scala versions) stored in the element of the pom. - // These are attached to the module itself. - val filtered = shouldBeUnqualified(properties) - - // Extracts extra attributes for the dependencies. - // Because the tag in pom.xml cannot include additional metadata, - // sbt includes extra attributes in a 'extraDependencyAttributes' property. - // This is read/written from/to a pure string (no element structure) because Ivy only - // parses the immediate text nodes of the property. - val extraDepAttributes = getDependencyExtra(filtered) - - // Fixes up the detected extension in some cases missed by Ivy. - val convertArtifacts = artifactExtIncorrect(md) - - // Merges artifact sections for duplicate dependency definitions - val mergeDuplicates = IvySbt.hasDuplicateDependencies(md.getDependencies) - - val unqualify = toUnqualify(filtered) - - // Here we always add extra attributes. There's a scenario where parent-pom information corrupts child-poms with "e:" namespaced xml elements - // and we have to force the every generated xml file to have the appropriate xml namespace - addExtra(unqualify, extraDepAttributes, parser, md) - } - // The element of the pom is used to store additional metadata, such as for sbt plugins or for the base URL for API docs. - // This is done because the pom XSD does not appear to allow extra metadata anywhere else. - // The extra sbt plugin metadata in pom.xml does not need to be readable by maven, but the other information may be. - // However, the pom.xml needs to be valid in all cases because other tools like repository managers may read the pom.xml. - private[sbt] def getPomProperties(md: ModuleDescriptor): Map[String, String] = - { - import collection.JavaConverters._ - PomModuleDescriptorBuilder.extractPomProperties(md.getExtraInfo).asInstanceOf[java.util.Map[String, String]].asScala.toMap - } - private[sbt] def toUnqualify(propertyAttributes: Map[String, String]): Map[String, String] = - (propertyAttributes - ExtraAttributesKey) map { case (k, v) => ("e:" + k, v) } - - private[this] def artifactExtIncorrect(md: ModuleDescriptor): Boolean = - md.getConfigurations.exists(conf => md.getArtifacts(conf.getName).exists(art => JarPackagings(art.getExt))) - private[this] def shouldBeUnqualified(m: Map[String, String]): Map[String, String] = m.filterKeys(unqualifiedKeys) - - private[this] def condAddExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId = - if (properties.isEmpty) id else addExtra(properties, id) - private[this] def addExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId = - { - import collection.JavaConverters._ - val oldExtra = qualifiedExtra(id) - val newExtra = (oldExtra ++ properties).asJava - ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, newExtra) - } - - private[this] def getDependencyExtra(m: Map[String, String]): Map[ModuleRevisionId, Map[String, String]] = - PomExtraDependencyAttributes.getDependencyExtra(m) - - def qualifiedExtra(item: ExtendableItem): Map[String, String] = PomExtraDependencyAttributes.qualifiedExtra(item) - def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] = - (qualifiedExtra(item) filterKeys { k => qualifiedIsExtra(k) == include }) - - def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] = - PomExtraDependencyAttributes.writeDependencyExtra(s) - - // parses the sequence of dependencies with extra attribute information, with one dependency per line - def readDependencyExtra(s: String): Seq[ModuleRevisionId] = PomExtraDependencyAttributes.readDependencyExtra(s) - - def qualifiedIsExtra(k: String): Boolean = PomExtraDependencyAttributes.qualifiedIsExtra(k) - - // Reduces the id to exclude custom extra attributes - // This makes the id suitable as a key to associate a dependency parsed from a element - // with the extra attributes from the section - def simplify(id: ModuleRevisionId): ModuleRevisionId = PomExtraDependencyAttributes.simplify(id) - - private[this] def addExtra(dep: DependencyDescriptor, extra: Map[ModuleRevisionId, Map[String, String]]): DependencyDescriptor = - { - val extras = if (extra.isEmpty) None else extra get simplify(dep.getDependencyRevisionId) - extras match { - case None => dep - case Some(extraAttrs) => transform(dep, revId => addExtra(extraAttrs, revId)) - } - } - private[this] def transform(dep: DependencyDescriptor, f: ModuleRevisionId => ModuleRevisionId): DependencyDescriptor = - DefaultDependencyDescriptor.transformInstance(dep, namespaceTransformer(dep.getDependencyRevisionId, f), false) - private[this] def extraTransformer(txId: ModuleRevisionId, extra: Map[String, String]): NamespaceTransformer = - namespaceTransformer(txId, revId => addExtra(extra, revId)) - - private[this] def namespaceTransformer(txId: ModuleRevisionId, f: ModuleRevisionId => ModuleRevisionId): NamespaceTransformer = - new NamespaceTransformer { - def transform(revId: ModuleRevisionId): ModuleRevisionId = if (revId == txId) f(revId) else revId - def isIdentity = false - } - - import collection.JavaConverters._ - def addExtra(properties: Map[String, String], dependencyExtra: Map[ModuleRevisionId, Map[String, String]], parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor = - { - val dmd = new DefaultModuleDescriptor(parser, md.getResource) - - val mrid = addExtra(properties, md.getModuleRevisionId) - val resolvedMrid = addExtra(properties, md.getResolvedModuleRevisionId) - dmd.setModuleRevisionId(mrid) - dmd.setResolvedModuleRevisionId(resolvedMrid) - - dmd.setDefault(md.isDefault) - dmd.setHomePage(md.getHomePage) - dmd.setDescription(md.getDescription) - dmd.setLastModified(md.getLastModified) - dmd.setStatus(md.getStatus()) - dmd.setPublicationDate(md.getPublicationDate()) - dmd.setResolvedPublicationDate(md.getResolvedPublicationDate()) - - for (l <- md.getLicenses) dmd.addLicense(l) - for ((key, value) <- md.getExtraInfo.asInstanceOf[java.util.Map[String, String]].asScala) dmd.addExtraInfo(key, value) - dmd.addExtraInfo(TransformedHashKey, MakeTransformHash(md)) // mark as transformed by this version, so we don't need to do it again - for ((key, value) <- md.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String, String]].asScala) dmd.addExtraAttributeNamespace(key, value) - IvySbt.addExtraNamespace(dmd) - - val withExtra = md.getDependencies map { dd => addExtra(dd, dependencyExtra) } - val unique = IvySbt.mergeDuplicateDefinitions(withExtra) - unique foreach dmd.addDependency - - for (ed <- md.getInheritedDescriptors) dmd.addInheritedDescriptor(new DefaultExtendsDescriptor(md, ed.getLocation, ed.getExtendsTypes)) - for (conf <- md.getConfigurations) { - dmd.addConfiguration(conf) - for (art <- md.getArtifacts(conf.getName)) { - val ext = art.getExt - val newExt = if (JarPackagings(ext)) "jar" else ext - val nart = new DefaultArtifact(mrid, art.getPublicationDate, art.getName, art.getType, newExt, art.getUrl, art.getQualifiedExtraAttributes) - dmd.addArtifact(conf.getName, nart) - } - } - dmd - } -} diff --git a/ivy/src/main/scala/sbt/CustomXmlParser.scala b/ivy/src/main/scala/sbt/CustomXmlParser.scala deleted file mode 100644 index bc8d7544b..000000000 --- a/ivy/src/main/scala/sbt/CustomXmlParser.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.ByteArrayInputStream -import java.net.URL - -import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor } -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser -import org.apache.ivy.plugins.repository.Resource -import org.apache.ivy.plugins.repository.url.URLResource - -/** Subclasses the default Ivy file parser in order to provide access to protected methods.*/ -private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser { - import XmlModuleDescriptorParser.Parser - class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings) { - def setSource(url: URL) = - { - super.setResource(new URLResource(url)) - super.setInput(url) - } - def setInput(bytes: Array[Byte]): Unit = setInput(new ByteArrayInputStream(bytes)) - /** Overridden because the super implementation overwrites the module descriptor.*/ - override def setResource(res: Resource): Unit = () - override def setMd(md: DefaultModuleDescriptor) = - { - super.setMd(md) - if (defaultConfig.isDefined) setDefaultConfMapping("*->default(compile)") - } - override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd) - override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf) - } -} diff --git a/ivy/src/main/scala/sbt/DependencyFilter.scala b/ivy/src/main/scala/sbt/DependencyFilter.scala deleted file mode 100644 index fee8da46c..000000000 --- a/ivy/src/main/scala/sbt/DependencyFilter.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -trait DependencyFilterExtra { - def moduleFilter(organization: NameFilter = AllPassFilter, name: NameFilter = AllPassFilter, revision: NameFilter = AllPassFilter): ModuleFilter = - new ModuleFilter { - def apply(m: ModuleID): Boolean = organization.accept(m.organization) && name.accept(m.name) && revision.accept(m.revision) - } - def artifactFilter(name: NameFilter = AllPassFilter, `type`: NameFilter = AllPassFilter, extension: NameFilter = AllPassFilter, classifier: NameFilter = AllPassFilter): ArtifactFilter = - new ArtifactFilter { - def apply(a: Artifact): Boolean = name.accept(a.name) && `type`.accept(a.`type`) && extension.accept(a.extension) && classifier.accept(a.classifier getOrElse "") - } - def configurationFilter(name: NameFilter = AllPassFilter): ConfigurationFilter = - new ConfigurationFilter { - def apply(c: String): Boolean = name.accept(c) - } -} -object DependencyFilter extends DependencyFilterExtra { - def make(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): DependencyFilter = - new DependencyFilter { - def apply(c: String, m: ModuleID, a: Artifact): Boolean = configuration(c) && module(m) && artifact(a) - } - def apply(x: DependencyFilter, y: DependencyFilter, combine: (Boolean, Boolean) => Boolean): DependencyFilter = - new DependencyFilter { - def apply(c: String, m: ModuleID, a: Artifact): Boolean = combine(x(c, m, a), y(c, m, a)) - } - def allPass: DependencyFilter = configurationFilter() - implicit def fnToModuleFilter(f: ModuleID => Boolean): ModuleFilter = new ModuleFilter { def apply(m: ModuleID) = f(m) } - implicit def fnToArtifactFilter(f: Artifact => Boolean): ArtifactFilter = new ArtifactFilter { def apply(m: Artifact) = f(m) } - implicit def fnToConfigurationFilter(f: String => Boolean): ConfigurationFilter = new ConfigurationFilter { def apply(c: String) = f(c) } - implicit def subDepFilterToFn[Arg](f: SubDepFilter[Arg, _]): Arg => Boolean = f apply _ -} -trait DependencyFilter { - def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean - final def &&(o: DependencyFilter) = DependencyFilter(this, o, _ && _) - final def ||(o: DependencyFilter) = DependencyFilter(this, o, _ || _) - final def --(o: DependencyFilter) = DependencyFilter(this, o, _ && !_) -} -sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter { self: Self => - def apply(a: Arg): Boolean - protected def make(f: Arg => Boolean): Self - final def &(o: Self): Self = combine(o, _ && _) - final def |(o: Self): Self = combine(o, _ || _) - final def -(o: Self): Self = combine(o, _ && !_) - private[this] def combine(o: Self, f: (Boolean, Boolean) => Boolean): Self = make((m: Arg) => f(this(m), o(m))) -} -trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter] { - protected final def make(f: ModuleID => Boolean) = new ModuleFilter { def apply(m: ModuleID) = f(m) } - final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(module) -} -trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter] { - protected final def make(f: Artifact => Boolean) = new ArtifactFilter { def apply(m: Artifact) = f(m) } - final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(artifact) -} -trait ConfigurationFilter extends SubDepFilter[String, ConfigurationFilter] { - protected final def make(f: String => Boolean) = new ConfigurationFilter { def apply(m: String) = f(m) } - final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(configuration) -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/EvictionWarning.scala b/ivy/src/main/scala/sbt/EvictionWarning.scala deleted file mode 100644 index 7a3bcaed8..000000000 --- a/ivy/src/main/scala/sbt/EvictionWarning.scala +++ /dev/null @@ -1,241 +0,0 @@ -package sbt - -import collection.mutable -import Configurations.Compile -import ScalaArtifacts.{ LibraryID, CompilerID } - -final class EvictionWarningOptions private[sbt] ( - val configurations: Seq[Configuration], - val warnScalaVersionEviction: Boolean, - val warnDirectEvictions: Boolean, - val warnTransitiveEvictions: Boolean, - val infoAllEvictions: Boolean, - val showCallers: Boolean, - val guessCompatible: Function1[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean]) { - private[sbt] def configStrings = configurations map { _.name } - - def withConfigurations(configurations: Seq[Configuration]): EvictionWarningOptions = - copy(configurations = configurations) - def withWarnScalaVersionEviction(warnScalaVersionEviction: Boolean): EvictionWarningOptions = - copy(warnScalaVersionEviction = warnScalaVersionEviction) - def withWarnDirectEvictions(warnDirectEvictions: Boolean): EvictionWarningOptions = - copy(warnDirectEvictions = warnDirectEvictions) - def withWarnTransitiveEvictions(warnTransitiveEvictions: Boolean): EvictionWarningOptions = - copy(warnTransitiveEvictions = warnTransitiveEvictions) - def withInfoAllEvictions(infoAllEvictions: Boolean): EvictionWarningOptions = - copy(infoAllEvictions = infoAllEvictions) - def withShowCallers(showCallers: Boolean): EvictionWarningOptions = - copy(showCallers = showCallers) - def withGuessCompatible(guessCompatible: Function1[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean]): EvictionWarningOptions = - copy(guessCompatible = guessCompatible) - - private[sbt] def copy(configurations: Seq[Configuration] = configurations, - warnScalaVersionEviction: Boolean = warnScalaVersionEviction, - warnDirectEvictions: Boolean = warnDirectEvictions, - warnTransitiveEvictions: Boolean = warnTransitiveEvictions, - infoAllEvictions: Boolean = infoAllEvictions, - showCallers: Boolean = showCallers, - guessCompatible: Function1[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean] = guessCompatible): EvictionWarningOptions = - new EvictionWarningOptions(configurations = configurations, - warnScalaVersionEviction = warnScalaVersionEviction, - warnDirectEvictions = warnDirectEvictions, - warnTransitiveEvictions = warnTransitiveEvictions, - infoAllEvictions = infoAllEvictions, - showCallers = showCallers, - guessCompatible = guessCompatible) -} - -object EvictionWarningOptions { - def empty: EvictionWarningOptions = - new EvictionWarningOptions(Vector(), false, false, false, false, false, defaultGuess) - def default: EvictionWarningOptions = - new EvictionWarningOptions(Vector(Compile), true, true, false, false, false, defaultGuess) - def full: EvictionWarningOptions = - new EvictionWarningOptions(Vector(Compile), true, true, true, true, true, defaultGuess) - - lazy val defaultGuess: Function1[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean] = - guessSecondSegment orElse guessSemVer orElse guessFalse - lazy val guessSecondSegment: PartialFunction[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean] = { - case (m1, Some(m2), Some(ivyScala)) if m2.name.endsWith("_" + ivyScala.scalaFullVersion) || m2.name.endsWith("_" + ivyScala.scalaBinaryVersion) => - (m1.revision, m2.revision) match { - case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => - VersionNumber.SecondSegment.isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) - case _ => false - } - } - lazy val guessSemVer: PartialFunction[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean] = { - case (m1, Some(m2), _) => - (m1.revision, m2.revision) match { - case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => - VersionNumber.SemVer.isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) - case _ => false - } - } - lazy val guessFalse: PartialFunction[(ModuleID, Option[ModuleID], Option[IvyScala]), Boolean] = { - case (_, _, _) => false - } -} - -final class EvictionPair private[sbt] ( - val organization: String, - val name: String, - val winner: Option[ModuleReport], - val evicteds: Seq[ModuleReport], - val includesDirect: Boolean, - val showCallers: Boolean) { - override def toString: String = - EvictionPair.evictionPairLines.showLines(this).mkString - override def equals(o: Any): Boolean = o match { - case o: EvictionPair => - (this.organization == o.organization) && - (this.name == o.name) - case _ => false - } - override def hashCode: Int = { - var hash = 1 - hash = hash * 31 + this.organization.## - hash = hash * 31 + this.name.## - hash - } -} - -object EvictionPair { - implicit val evictionPairLines: ShowLines[EvictionPair] = ShowLines { a: EvictionPair => - val revs = a.evicteds map { _.module.revision } - val revsStr = if (revs.size <= 1) revs.mkString else "(" + revs.mkString(", ") + ")" - val winnerRev = (a.winner map { r => - val callers: String = - if (a.showCallers) - r.callers match { - case Seq() => "" - case cs => (cs map { _.caller.toString }).mkString(" (caller: ", ", ", ")") - } - else "" - r.module.revision + callers - }) map { " -> " + _ } getOrElse "" - Seq(s"\t* ${a.organization}:${a.name}:${revsStr}$winnerRev") - } -} - -final class EvictionWarning private[sbt] ( - val options: EvictionWarningOptions, - val scalaEvictions: Seq[EvictionPair], - val directEvictions: Seq[EvictionPair], - val transitiveEvictions: Seq[EvictionPair], - val allEvictions: Seq[EvictionPair]) { - def reportedEvictions: Seq[EvictionPair] = scalaEvictions ++ directEvictions ++ transitiveEvictions - private[sbt] def infoAllTheThings: List[String] = EvictionWarning.infoAllTheThings(this) -} - -object EvictionWarning { - def apply(module: IvySbt#Module, options: EvictionWarningOptions, report: UpdateReport, log: Logger): EvictionWarning = { - val evictions = buildEvictions(options, report) - processEvictions(module, options, evictions) - } - - private[sbt] def buildEvictions(options: EvictionWarningOptions, report: UpdateReport): Seq[OrganizationArtifactReport] = { - val buffer: mutable.ListBuffer[OrganizationArtifactReport] = mutable.ListBuffer() - val confs = report.configurations filter { x => options.configStrings contains x.configuration } - confs flatMap { confReport => - confReport.details map { detail => - if ((detail.modules exists { _.evicted }) && - !(buffer exists { x => (x.organization == detail.organization) && (x.name == detail.name) })) { - buffer += detail - } - } - } - buffer.toList.toVector - } - - private[sbt] def isScalaArtifact(module: IvySbt#Module, organization: String, name: String): Boolean = - module.moduleSettings.ivyScala match { - case Some(s) => - organization == s.scalaOrganization && - (name == LibraryID) || (name == CompilerID) - case _ => false - } - - private[sbt] def processEvictions(module: IvySbt#Module, options: EvictionWarningOptions, reports: Seq[OrganizationArtifactReport]): EvictionWarning = { - val directDependencies = module.moduleSettings match { - case x: InlineConfiguration => x.dependencies - case x: InlineConfigurationWithExcludes => x.dependencies - case _ => Seq() - } - val pairs = reports map { detail => - val evicteds = detail.modules filter { _.evicted } - val winner = (detail.modules filterNot { _.evicted }).headOption - val includesDirect: Boolean = - options.warnDirectEvictions && - (directDependencies exists { dep => - (detail.organization == dep.organization) && (detail.name == dep.name) - }) - new EvictionPair(detail.organization, detail.name, winner, evicteds, includesDirect, options.showCallers) - } - val scalaEvictions: mutable.ListBuffer[EvictionPair] = mutable.ListBuffer() - val directEvictions: mutable.ListBuffer[EvictionPair] = mutable.ListBuffer() - val transitiveEvictions: mutable.ListBuffer[EvictionPair] = mutable.ListBuffer() - def guessCompatible(p: EvictionPair): Boolean = - p.evicteds forall { r => - options.guessCompatible((r.module, p.winner map { _.module }, module.moduleSettings.ivyScala)) - } - pairs foreach { - case p if isScalaArtifact(module, p.organization, p.name) => - (module.moduleSettings.ivyScala, p.winner) match { - case (Some(s), Some(winner)) if (s.scalaFullVersion != winner.module.revision) && options.warnScalaVersionEviction => - scalaEvictions += p - case _ => - } - case p if p.includesDirect => - if (!guessCompatible(p) && options.warnDirectEvictions) { - directEvictions += p - } - case p => - if (!guessCompatible(p) && options.warnTransitiveEvictions) { - transitiveEvictions += p - } - } - new EvictionWarning(options, scalaEvictions.toList, - directEvictions.toList, transitiveEvictions.toList, pairs) - } - - implicit val evictionWarningLines: ShowLines[EvictionWarning] = ShowLines { a: EvictionWarning => - import ShowLines._ - val out: mutable.ListBuffer[String] = mutable.ListBuffer() - if (a.scalaEvictions.nonEmpty) { - out += "Scala version was updated by one of library dependencies:" - out ++= (a.scalaEvictions flatMap { _.lines }) - out += "To force scalaVersion, add the following:" - out += "\tivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) }" - } - - if (a.directEvictions.nonEmpty || a.transitiveEvictions.nonEmpty) { - out += "There may be incompatibilities among your library dependencies." - out += "Here are some of the libraries that were evicted:" - out ++= (a.directEvictions flatMap { _.lines }) - out ++= (a.transitiveEvictions flatMap { _.lines }) - } - - if (a.allEvictions.nonEmpty && a.reportedEvictions.nonEmpty && !a.options.showCallers) { - out += "Run 'evicted' to see detailed eviction warnings" - } - - out.toList - } - - private[sbt] def infoAllTheThings(a: EvictionWarning): List[String] = - if (a.options.infoAllEvictions) { - import ShowLines._ - val evo = a.options - val out: mutable.ListBuffer[String] = mutable.ListBuffer() - a.allEvictions foreach { ev => - if ((a.scalaEvictions contains ev) && evo.warnScalaVersionEviction) () - else if ((a.directEvictions contains ev) && evo.warnDirectEvictions) () - else if ((a.transitiveEvictions contains ev) && evo.warnTransitiveEvictions) () - else { - out ++= ev.lines - } - } - if (out.isEmpty) Nil - else List("Here are other libraries that were evicted:") ::: out.toList - } else Nil -} diff --git a/ivy/src/main/scala/sbt/Ivy.scala b/ivy/src/main/scala/sbt/Ivy.scala deleted file mode 100644 index b8540fd68..000000000 --- a/ivy/src/main/scala/sbt/Ivy.scala +++ /dev/null @@ -1,703 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import Resolver.PluginPattern -import ivyint.{ CachedResolutionResolveEngine, CachedResolutionResolveCache, SbtDefaultDependencyDescriptor } - -import java.io.File -import java.net.URI -import java.text.ParseException -import java.util.concurrent.Callable -import java.util.{ Collection, Collections => CS, Date } -import CS.singleton - -import org.apache.ivy.Ivy -import org.apache.ivy.core.report.ResolveReport -import org.apache.ivy.core.{ IvyPatternHelper, LogOptions, IvyContext } -import org.apache.ivy.core.cache.{ ResolutionCacheManager, CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter } -import org.apache.ivy.core.event.EventManager -import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact } -import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License } -import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator -import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } -import org.apache.ivy.core.resolve._ -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.sort.SortEngine -import org.apache.ivy.plugins.latest.{ LatestStrategy, LatestRevisionStrategy, ArtifactInfo } -import org.apache.ivy.plugins.matcher.PatternMatcher -import org.apache.ivy.plugins.parser.m2.{ PomModuleDescriptorParser } -import org.apache.ivy.plugins.resolver.{ ChainResolver, DependencyResolver, BasicResolver } -import org.apache.ivy.plugins.resolver.util.{ HasLatestStrategy, ResolvedResource } -import org.apache.ivy.plugins.version.ExactVersionMatcher -import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository => IFileRepository } -import org.apache.ivy.plugins.repository.url.URLResource -import org.apache.ivy.util.{ Message, MessageLogger, StringUtils => IvyStringUtils } -import org.apache.ivy.util.extendable.ExtendableItem - -import scala.xml.{ NodeSeq, Text } -import scala.collection.mutable - -final class IvySbt(val configuration: IvyConfiguration) { - import configuration.baseDirectory - - /** - * ========== Configuration/Setup ============ - * This part configures the Ivy instance by first creating the logger interface to ivy, then IvySettings, and then the Ivy instance. - * These are lazy so that they are loaded within the right context. This is important so that no Ivy XML configuration needs to be loaded, - * saving some time. This is necessary because Ivy has global state (IvyContext, Message, DocumentBuilder, ...). - */ - private def withDefaultLogger[T](logger: MessageLogger)(f: => T): T = - { - def action() = - IvySbt.synchronized { - val originalLogger = Message.getDefaultLogger - Message.setDefaultLogger(logger) - try { f } - finally { Message.setDefaultLogger(originalLogger) } - } - // Ivy is not thread-safe nor can the cache be used concurrently. - // If provided a GlobalLock, we can use that to ensure safe access to the cache. - // Otherwise, we can at least synchronize within the JVM. - // For thread-safety in particular, Ivy uses a static DocumentBuilder, which is not thread-safe. - configuration.lock match { - case Some(lock) => lock(ivyLockFile, new Callable[T] { def call = action() }) - case None => action() - } - } - private lazy val settings: IvySettings = - { - val is = new IvySettings - - is.setBaseDir(baseDirectory) - is.setCircularDependencyStrategy(configuration.updateOptions.circularDependencyLevel.ivyStrategy) - CustomPomParser.registerDefault - is.setVariable("alwaysDeleteSourcesAndJavadocJarsOnChangedModuleDescriptor", "true") - - configuration match { - case e: ExternalIvyConfiguration => - IvySbt.addResolvers(e.extraResolvers, is, configuration.log) - IvySbt.loadURI(is, e.uri) - case i: InlineIvyConfiguration => - is.setVariable("ivy.checksums", i.checksums mkString ",") - i.paths.ivyHome foreach is.setDefaultIvyUserDir - IvySbt.configureCache(is, i.localOnly, i.resolutionCacheDir) - IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, i.localOnly, configuration.updateOptions, configuration.log) - IvySbt.setModuleConfigurations(is, i.moduleConfigurations, configuration.log) - } - is - } - private[sbt] def mkIvy: Ivy = - { - val i = new Ivy() { - private val loggerEngine = new SbtMessageLoggerEngine - override def getLoggerEngine = loggerEngine - override def bind(): Unit = { - val prOpt = Option(getSettings.getResolver(ProjectResolver.InterProject)) map { case pr: ProjectResolver => pr } - // We inject the deps we need before we can hook our resolve engine. - setSortEngine(new SortEngine(getSettings)) - setEventManager(new EventManager()) - if (configuration.updateOptions.cachedResolution) { - setResolveEngine(new ResolveEngine(getSettings, getEventManager, getSortEngine) with CachedResolutionResolveEngine { - val cachedResolutionResolveCache = IvySbt.cachedResolutionResolveCache - val projectResolver = prOpt - def makeInstance = mkIvy - }) - } else setResolveEngine(new ResolveEngine(getSettings, getEventManager, getSortEngine)) - super.bind() - } - } - - i.setSettings(settings) - i.bind() - i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log)) - i - } - - private lazy val ivy: Ivy = mkIvy - // Must be the same file as is used in Update in the launcher - private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock") - /** ========== End Configuration/Setup ============*/ - - /** Uses the configured Ivy instance within a safe context.*/ - def withIvy[T](log: Logger)(f: Ivy => T): T = - withIvy(new IvyLoggerInterface(log))(f) - - def withIvy[T](log: MessageLogger)(f: Ivy => T): T = - withDefaultLogger(log) { - // See #429 - We always insert a helper authenticator here which lets us get more useful authentication errors. - ivyint.ErrorMessageAuthenticator.install() - ivy.pushContext() - ivy.getLoggerEngine.pushLogger(log) - try { f(ivy) } - finally { - ivy.getLoggerEngine.popLogger() - ivy.popContext() - } - } - - /** - * Cleans cached resolution cache. - * @param md - module descriptor of the original Ivy graph. - */ - private[sbt] def cleanCachedResolutionCache(md: ModuleDescriptor, log: Logger): Unit = - withIvy(log) { i => - val prOpt = Option(i.getSettings.getResolver(ProjectResolver.InterProject)) map { case pr: ProjectResolver => pr } - if (configuration.updateOptions.cachedResolution) { - IvySbt.cachedResolutionResolveCache.clean(md, prOpt) - } - } - - final class Module(rawModuleSettings: ModuleSettings) { - val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings) - def owner = IvySbt.this - def withModule[T](log: Logger)(f: (Ivy, DefaultModuleDescriptor, String) => T): T = - withIvy[T](log) { ivy => f(ivy, moduleDescriptor0, defaultConfig0) } - - def moduleDescriptor(log: Logger): DefaultModuleDescriptor = withModule(log)((_, md, _) => md) - def dependencyMapping(log: Logger): (ModuleRevisionId, ModuleDescriptor) = - { - val md = moduleDescriptor(log) - (md.getModuleRevisionId, md) - } - def defaultConfig(log: Logger): String = withModule(log)((_, _, dc) => dc) - // these should only be referenced by withModule because lazy vals synchronize on this object - // withIvy explicitly locks the IvySbt object, so they have to be done in the right order to avoid deadlock - private[this] lazy val (moduleDescriptor0: DefaultModuleDescriptor, defaultConfig0: String) = - { - val (baseModule, baseConfiguration) = - moduleSettings match { - case ic: InlineConfiguration => configureInline(ic.withExcludes, configuration.log) - case ic: InlineConfigurationWithExcludes => configureInline(ic, configuration.log) - case ec: EmptyConfiguration => configureEmpty(ec) - case pc: PomConfiguration => configurePom(pc) - case ifc: IvyFileConfiguration => configureIvyFile(ifc) - } - moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration, configuration.log)) - IvySbt.addExtraNamespace(baseModule) - (baseModule, baseConfiguration) - } - private def configureInline(ic: InlineConfigurationWithExcludes, log: Logger) = - { - import ic._ - val moduleID = newConfiguredModuleID(module, moduleInfo, configurations) - IvySbt.setConflictManager(moduleID, conflictManager, ivy.getSettings) - val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION) - log.debug("Using inline dependencies specified in Scala" + (if (ivyXML.isEmpty) "." else " and XML.")) - - val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate) - IvySbt.addMainArtifact(moduleID) - IvySbt.addOverrides(moduleID, overrides, ivy.getSettings.getMatcher(PatternMatcher.EXACT)) - IvySbt.addExcludes(moduleID, excludes, ivyScala) - val transformedDeps = IvySbt.overrideDirect(dependencies, overrides) - IvySbt.addDependencies(moduleID, transformedDeps, parser) - (moduleID, parser.getDefaultConf) - } - private def newConfiguredModuleID(module: ModuleID, moduleInfo: ModuleInfo, configurations: Iterable[Configuration]) = - { - val mod = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false) - mod.setLastModified(System.currentTimeMillis) - mod.setDescription(moduleInfo.description) - moduleInfo.homepage foreach { h => mod.setHomePage(h.toString) } - moduleInfo.licenses foreach { l => mod.addLicense(new License(l._1, l._2.toString)) } - IvySbt.addConfigurations(mod, configurations) - IvySbt.addArtifacts(mod, module.explicitArtifacts) - mod - } - - /** Parses the Maven pom 'pomFile' from the given `PomConfiguration`.*/ - private def configurePom(pc: PomConfiguration) = - { - val md = CustomPomParser.default.parseDescriptor(settings, toURL(pc.file), pc.validate) - val dmd = IvySbt.toDefaultModuleDescriptor(md) - IvySbt.addConfigurations(dmd, Configurations.defaultInternal) - val defaultConf = Configurations.DefaultMavenConfiguration.name - for (is <- pc.ivyScala) if (pc.autoScalaTools) { - val confParser = new CustomXmlParser.CustomParser(settings, Some(defaultConf)) - confParser.setMd(dmd) - addScalaToolDependencies(dmd, confParser, is) - } - (dmd, defaultConf) - } - /** Parses the Ivy file 'ivyFile' from the given `IvyFileConfiguration`.*/ - private def configureIvyFile(ifc: IvyFileConfiguration) = - { - val parser = new CustomXmlParser.CustomParser(settings, None) - parser.setValidate(ifc.validate) - parser.setSource(toURL(ifc.file)) - parser.parse() - val dmd = IvySbt.toDefaultModuleDescriptor(parser.getModuleDescriptor()) - for (is <- ifc.ivyScala) if (ifc.autoScalaTools) - addScalaToolDependencies(dmd, parser, is) - (dmd, parser.getDefaultConf) - } - private def addScalaToolDependencies(dmd: DefaultModuleDescriptor, parser: CustomXmlParser.CustomParser, is: IvyScala): Unit = { - IvySbt.addConfigurations(dmd, Configurations.ScalaTool :: Nil) - IvySbt.addDependencies(dmd, ScalaArtifacts.toolDependencies(is.scalaOrganization, is.scalaFullVersion), parser) - } - private def toURL(file: File) = file.toURI.toURL - private def configureEmpty(ec: EmptyConfiguration) = - { - val defaultConf = ModuleDescriptor.DEFAULT_CONFIGURATION - val mod = newConfiguredModuleID(ec.module, ec.moduleInfo, Seq(Configurations.Default)) - IvySbt.addMainArtifact(mod) - (mod, defaultConf) - } - } -} - -private[sbt] object IvySbt { - val DefaultIvyConfigFilename = "ivysettings.xml" - val DefaultIvyFilename = "ivy.xml" - val DefaultMavenFilename = "pom.xml" - val DefaultChecksums = Seq("sha1", "md5") - private[sbt] val cachedResolutionResolveCache: CachedResolutionResolveCache = new CachedResolutionResolveCache() - - def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename) - def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) - def defaultPOM(project: File) = new File(project, DefaultMavenFilename) - - def loadURI(is: IvySettings, uri: URI): Unit = { - if (uri.getScheme == "file") - is.load(new File(uri)) // IVY-1114 - else - is.load(uri.toURL) - } - - /** - * Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. - * 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. - */ - private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], other: Seq[Resolver], localOnly: Boolean, updateOptions: UpdateOptions, log: Logger): Unit = { - def makeChain(label: String, name: String, rs: Seq[Resolver]) = { - log.debug(label + " repositories:") - val chain = resolverChain(name, rs, localOnly, settings, updateOptions, log) - settings.addResolver(chain) - chain - } - val otherChain = makeChain("Other", "sbt-other", other) - val mainChain = makeChain("Default", "sbt-chain", resolvers) - settings.setDefaultResolver(mainChain.getName) - } - private[sbt] def isChanging(dd: DependencyDescriptor): Boolean = - dd.isChanging || isChanging(dd.getDependencyRevisionId) - private[sbt] def isChanging(module: ModuleID): Boolean = - module.revision endsWith "-SNAPSHOT" - private[sbt] def isChanging(mrid: ModuleRevisionId): Boolean = - mrid.getRevision endsWith "-SNAPSHOT" - def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, log: Logger): DependencyResolver = - resolverChain(name, resolvers, localOnly, settings, UpdateOptions(), log) - def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, updateOptions: UpdateOptions, log: Logger): DependencyResolver = { - def mapResolvers(rs: Seq[Resolver]) = rs.map(r => ConvertResolver(r, settings, updateOptions, log)) - val (projectResolvers, rest) = resolvers.partition(_.name == "inter-project") - if (projectResolvers.isEmpty) new ivyint.SbtChainResolver(name, mapResolvers(rest), settings, updateOptions, log) - else { - // Here we set up a "first repo wins" chain resolver - val delegate = new ivyint.SbtChainResolver(name + "-delegate", mapResolvers(rest), settings, updateOptions, log) - val prs = mapResolvers(projectResolvers) - // Here we construct a chain resolver which will FORCE looking at the project resolver first. - new ivyint.SbtChainResolver(name, - prs :+ delegate, - settings, - UpdateOptions().withLatestSnapshots(false), - log) - - } - } - def addResolvers(resolvers: Seq[Resolver], settings: IvySettings, log: Logger): Unit = { - for (r <- resolvers) { - log.debug("\t" + r) - settings.addResolver(ConvertResolver(r, settings, log)) - } - } - /** - * A hack to detect if the given artifact is an automatically generated request for a classifier, - * as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:. - * Clearly, it would be better to have an explicit option in Ivy to control this. - */ - def hasImplicitClassifier(artifact: IArtifact): Boolean = - { - import collection.JavaConversions._ - artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:") - } - private def setModuleConfigurations(settings: IvySettings, moduleConfigurations: Seq[ModuleConfiguration], log: Logger): Unit = { - val existing = settings.getResolverNames - for (moduleConf <- moduleConfigurations) { - import moduleConf._ - import IvyPatternHelper._ - import PatternMatcher._ - if (!existing.contains(resolver.name)) - settings.addResolver(ConvertResolver(resolver, settings, log)) - val attributes = javaMap(Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision)) - settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null) - } - } - private def configureCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]): Unit = { - configureResolutionCache(settings, localOnly, resCacheDir) - configureRepositoryCache(settings, localOnly) - } - private[this] def configureResolutionCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]): Unit = { - val base = resCacheDir getOrElse settings.getDefaultResolutionCacheBasedir - settings.setResolutionCacheManager(new ResolutionCache(base, settings)) - } - // set the artifact resolver to be the main resolver. - // this is because sometimes the artifact resolver saved in the cache is not correct - // the common case is for resolved.getArtifactResolver to be inter-project from a different project's publish-local - // if there are problems with this, a less aggressive fix might be to only reset the artifact resolver when it is a ProjectResolver - // a possible problem is that fetching artifacts is slower, due to the full chain being the artifact resolver instead of the specific resolver - // This also fixes #760, which occurs when metadata exists in a repository, but the artifact doesn't. - private[sbt] def resetArtifactResolver(resolved: ResolvedModuleRevision): ResolvedModuleRevision = - if (resolved eq null) - null - else { - val desc = resolved.getDescriptor - val updatedDescriptor = CustomPomParser.defaultTransform(desc.getParser, desc) - new ResolvedModuleRevision(resolved.getResolver, resolved.getResolver, updatedDescriptor, resolved.getReport, resolved.isForce) - } - - private[this] def configureRepositoryCache(settings: IvySettings, localOnly: Boolean) //, artifactResolver: DependencyResolver) - { - val cacheDir = settings.getDefaultRepositoryCacheBasedir() - val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { - override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) = { - // ignore and reset the resolver- not ideal, but avoids thrashing. - val resolved = resetArtifactResolver(super.findModuleInCache(dd, revId, options, null)) - // invalidate the cache if the artifact was removed from the local repository - if (resolved == null) null - else if (isProjectResolver(resolved.getResolver)) { - resolved.getReport.getLocalFile.delete() - null - } else { - val origin = resolved.getReport.getArtifactOrigin - if (!origin.isLocal) resolved - else { - val file = new File(origin.getLocation) - if (file == null || file.exists) resolved - else { - resolved.getReport.getLocalFile.delete() - null - } - } - } - } - private[this] def isProjectResolver(r: DependencyResolver): Boolean = r match { - case pr: ProjectResolver => true - case _ => false - } - // ignore the original resolver wherever possible to avoid issues like #704 - override def saveResolvers(descriptor: ModuleDescriptor, metadataResolverName: String, artifactResolverName: String): Unit = () - } - manager.setArtifactPattern(PluginPattern + manager.getArtifactPattern) - manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern) - manager.setIvyPattern(PluginPattern + manager.getIvyPattern) - manager.setUseOrigin(true) - if (localOnly) - manager.setDefaultTTL(java.lang.Long.MAX_VALUE) - else { - manager.setChangingMatcher(PatternMatcher.REGEXP) - manager.setChangingPattern(".*-SNAPSHOT") - } - settings.addRepositoryCacheManager(manager) - settings.setDefaultRepositoryCacheManager(manager) - } - def toIvyConfiguration(configuration: Configuration) = - { - import org.apache.ivy.core.module.descriptor.{ Configuration => IvyConfig } - import IvyConfig.Visibility._ - import configuration._ - new IvyConfig(name, if (isPublic) PUBLIC else PRIVATE, description, extendsConfigs.map(_.name).toArray, transitive, null) - } - def addExtraNamespace(dmd: DefaultModuleDescriptor): Unit = - dmd.addExtraAttributeNamespace("e", "http://ant.apache.org/ivy/extra") - - /** Adds the ivy.xml main artifact. */ - private def addMainArtifact(moduleID: DefaultModuleDescriptor): Unit = { - val artifact = DefaultArtifact.newIvyArtifact(moduleID.getResolvedModuleRevisionId, moduleID.getPublicationDate) - moduleID.setModuleArtifact(artifact) - moduleID.check() - } - private def setConflictManager(moduleID: DefaultModuleDescriptor, conflict: ConflictManager, is: IvySettings): Unit = { - val mid = ModuleId.newInstance(conflict.organization, conflict.module) - val matcher = is.getMatcher(PatternMatcher.EXACT_OR_REGEXP) - val manager = is.getConflictManager(conflict.name) - moduleID.addConflictManager(mid, matcher, manager) - } - - /** Converts the given sbt module id into an Ivy ModuleRevisionId.*/ - def toID(m: ModuleID) = - { - import m._ - ModuleRevisionId.newInstance(organization, name, revision, javaMap(extraAttributes)) - } - - private def substituteCross(m: ModuleSettings): ModuleSettings = - m.ivyScala match { - case None => m - case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion) - } - private def substituteCross(m: ModuleSettings, scalaFullVersion: String, scalaBinaryVersion: String): ModuleSettings = - { - val sub = CrossVersion(scalaFullVersion, scalaBinaryVersion) - m match { - case ec: EmptyConfiguration => ec.copy(module = sub(ec.module)) - case ic: InlineConfiguration => ic.copy(module = sub(ic.module), dependencies = ic.dependencies map sub, overrides = ic.overrides map sub) - case ic: InlineConfigurationWithExcludes => ic.copy(module = sub(ic.module), dependencies = ic.dependencies map sub, overrides = ic.overrides map sub) - case _ => m - } - } - - private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, allConfigurations: Iterable[String]): MDArtifact = - { - val artifact = new MDArtifact(moduleID, a.name, a.`type`, a.extension, null, extra(a, false)) - copyConfigurations(a, artifact.addConfiguration, allConfigurations) - artifact - } - def getExtraAttributes(revID: ExtendableItem): Map[String, String] = - { - import collection.JavaConverters._ - revID.getExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap - } - private[sbt] def extra(artifact: Artifact, unqualify: Boolean = false): java.util.Map[String, String] = - { - val ea = artifact.classifier match { case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact } - javaMap(ea.extraAttributes, unqualify) - } - private[sbt] def javaMap(m: Map[String, String], unqualify: Boolean = false) = - { - val map = if (unqualify) m map { case (k, v) => (k.stripPrefix("e:"), v) } else m - if (map.isEmpty) null else scala.collection.JavaConversions.mapAsJavaMap(map) - } - - private object javaMap { - import java.util.{ HashMap, Map } - def apply[K, V](pairs: (K, V)*): Map[K, V] = - { - val map = new HashMap[K, V] - pairs.foreach { case (key, value) => map.put(key, value) } - map - } - } - /** Creates a full ivy file for 'module' using the 'dependencies' XML as the part after the <info>...</info> section. */ - private def wrapped(module: ModuleID, dependencies: NodeSeq) = - { - - { - if (hasInfo(module, dependencies)) - NodeSeq.Empty - else - addExtraAttributes(defaultInfo(module), module.extraAttributes) - } - { dependencies } - { - // this is because Ivy adds a default artifact if none are specified. - if (dependencies \\ "publications" isEmpty) else NodeSeq.Empty - } - - } - private[this] def defaultInfo(module: ModuleID): scala.xml.Elem = { - import module._ - - } - private[this] def addExtraAttributes(elem: scala.xml.Elem, extra: Map[String, String]): scala.xml.Elem = - (elem /: extra) { case (e, (key, value)) => e % new scala.xml.UnprefixedAttribute(key, value, scala.xml.Null) } - private def hasInfo(module: ModuleID, x: scala.xml.NodeSeq) = - { - val info = { x } \ "info" - if (info.nonEmpty) { - def check(found: NodeSeq, expected: String, label: String) = - if (found.isEmpty) - sys.error("Missing " + label + " in inline Ivy XML.") - else { - val str = found.text - if (str != expected) sys.error("Inconsistent " + label + " in inline Ivy XML. Expected '" + expected + "', got '" + str + "'") - } - check(info \ "@organisation", module.organization, "organisation") - check(info \ "@module", module.name, "name") - check(info \ "@revision", module.revision, "version") - } - info.nonEmpty - } - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML(settings: IvySettings, xml: scala.xml.NodeSeq, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = - parseIvyXML(settings, xml.toString, moduleID, defaultConfiguration, validate) - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = - { - val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration)) - parser.setMd(moduleID) - parser.setValidate(validate) - parser.setInput(xml.getBytes) - parser.parse() - parser - } - - def inconsistentDuplicateWarning(moduleID: DefaultModuleDescriptor): List[String] = - { - import IvyRetrieve.toModuleID - val dds = moduleID.getDependencies - inconsistentDuplicateWarning(dds map { dd => toModuleID(dd.getDependencyRevisionId) }) - } - - def inconsistentDuplicateWarning(dependencies: Seq[ModuleID]): List[String] = - { - val warningHeader = "Multiple dependencies with the same organization/name but different versions. To avoid conflict, pick one version:" - val out: mutable.ListBuffer[String] = mutable.ListBuffer() - (dependencies groupBy { dep => (dep.organization, dep.name) }) foreach { - case (k, vs) if vs.size > 1 => - val v0 = vs.head - (vs find { _.revision != v0.revision }) foreach { v => - out += s" * ${v0.organization}:${v0.name}:(" + (vs map { _.revision }).mkString(", ") + ")" - } - case _ => () - } - if (out.isEmpty) Nil - else warningHeader :: out.toList - } - - /** This method is used to add inline dependencies to the provided module. */ - def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Seq[ModuleID], parser: CustomXmlParser.CustomParser): Unit = { - val converted = dependencies map { dependency => convertDependency(moduleID, dependency, parser) } - val unique = if (hasDuplicateDependencies(converted)) mergeDuplicateDefinitions(converted) else converted - unique foreach moduleID.addDependency - } - /** Determines if there are multiple dependency definitions for the same dependency ID. */ - def hasDuplicateDependencies(dependencies: Seq[DependencyDescriptor]): Boolean = - { - val ids = dependencies.map(_.getDependencyRevisionId) - ids.toSet.size != ids.size - } - - /** - * Combines the artifacts, includes, and excludes of duplicate dependency definitions. - * This is somewhat fragile and is only intended to workaround Ivy (or sbt's use of Ivy) not handling this case properly. - * In particular, Ivy will create multiple dependency entries when converting a pom with a dependency on a classified artifact and a non-classified artifact: - * https://github.com/sbt/sbt/issues/468 - * It will also allow users to declare dependencies on classified modules in different configurations: - * https://groups.google.com/d/topic/simple-build-tool/H2MdAARz6e0/discussion - * as well as basic multi-classifier handling: #285, #419, #480. - * Multiple dependency definitions should otherwise be avoided as much as possible. - */ - def mergeDuplicateDefinitions(dependencies: Seq[DependencyDescriptor]): Seq[DependencyDescriptor] = - { - // need to preserve basic order of dependencies: can't use dependencies.groupBy - val deps = new java.util.LinkedHashMap[ModuleRevisionId, List[DependencyDescriptor]] - for (dd <- dependencies) { - val id = dd.getDependencyRevisionId - val updated = deps get id match { - case null => dd :: Nil - case v => dd :: v - } - deps.put(id, updated) - } - - import collection.JavaConverters._ - deps.values.asScala.toSeq.flatMap { dds => - val mergeable = (dds, dds.tail).zipped.forall(ivyint.MergeDescriptors.mergeable _) - if (mergeable) dds.reverse.reduceLeft(ivyint.MergeDescriptors.apply _) :: Nil else dds - } - } - - /** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor.*/ - def convertDependency(moduleID: DefaultModuleDescriptor, dependency: ModuleID, parser: CustomXmlParser.CustomParser): DefaultDependencyDescriptor = - { - val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), dependency.isForce, dependency.isChanging, dependency.isTransitive) with SbtDefaultDependencyDescriptor { - def dependencyModuleId = dependency - } - dependency.configurations match { - case None => // The configuration for this dependency was not explicitly specified, so use the default - parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor) - case Some(confs) => // The configuration mapping (looks like: test->default) was specified for this dependency - parser.parseDepsConfs(confs, dependencyDescriptor) - } - for (artifact <- dependency.explicitArtifacts) { - import artifact.{ name, classifier, `type`, extension, url } - val extraMap = extra(artifact) - val ivyArtifact = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, name, `type`, extension, url.orNull, extraMap) - copyConfigurations(artifact, ivyArtifact.addConfiguration) - for (conf <- dependencyDescriptor.getModuleConfigurations) - dependencyDescriptor.addDependencyArtifact(conf, ivyArtifact) - } - for (excls <- dependency.exclusions) { - for (conf <- dependencyDescriptor.getModuleConfigurations) { - dependencyDescriptor.addExcludeRule(conf, IvyScala.excludeRule(excls.organization, excls.name, excls.configurations, excls.artifact)) - } - } - dependencyDescriptor - } - def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit): Unit = - copyConfigurations(artifact, addConfiguration, "*" :: Nil) - - private[this] def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit, allConfigurations: Iterable[String]): Unit = - { - val confs = if (artifact.configurations.isEmpty) allConfigurations else artifact.configurations.map(_.name) - confs foreach addConfiguration - } - - def addExcludes(moduleID: DefaultModuleDescriptor, excludes: Seq[SbtExclusionRule], ivyScala: Option[IvyScala]): Unit = - excludes foreach addExclude(moduleID, ivyScala) - def addExclude(moduleID: DefaultModuleDescriptor, ivyScala: Option[IvyScala])(exclude0: SbtExclusionRule): Unit = - { - // this adds _2.11 postfix - val exclude = CrossVersion.substituteCross(exclude0, ivyScala) - val confs = - if (exclude.configurations.isEmpty) moduleID.getConfigurationsNames.toList - else exclude.configurations - val excludeRule = IvyScala.excludeRule(exclude.organization, exclude.name, confs, exclude.artifact) - moduleID.addExcludeRule(excludeRule) - } - - def addOverrides(moduleID: DefaultModuleDescriptor, overrides: Set[ModuleID], matcher: PatternMatcher): Unit = - overrides foreach addOverride(moduleID, matcher) - def addOverride(moduleID: DefaultModuleDescriptor, matcher: PatternMatcher)(overrideDef: ModuleID): Unit = - { - val overrideID = new ModuleId(overrideDef.organization, overrideDef.name) - val overrideWith = new OverrideDependencyDescriptorMediator(null, overrideDef.revision) - moduleID.addDependencyDescriptorMediator(overrideID, matcher, overrideWith) - } - /** - * It is necessary to explicitly modify direct dependencies because Ivy gives - * "IllegalStateException: impossible to get artifacts when data has not been loaded." - * when a direct dependency is overridden with a newer version." - */ - def overrideDirect(dependencies: Seq[ModuleID], overrides: Set[ModuleID]): Seq[ModuleID] = - { - def key(id: ModuleID) = (id.organization, id.name) - val overridden = overrides.map(id => (key(id), id.revision)).toMap - dependencies map { dep => - overridden get key(dep) match { - case Some(rev) => dep.copy(revision = rev) - case None => dep - } - } - } - - /** This method is used to add inline artifacts to the provided module. */ - def addArtifacts(moduleID: DefaultModuleDescriptor, artifacts: Iterable[Artifact]): Unit = - for (art <- mapArtifacts(moduleID, artifacts.toSeq); c <- art.getConfigurations) - moduleID.addArtifact(c, art) - - def addConfigurations(mod: DefaultModuleDescriptor, configurations: Iterable[Configuration]): Unit = - configurations.foreach(config => mod.addConfiguration(toIvyConfiguration(config))) - - def mapArtifacts(moduleID: ModuleDescriptor, artifacts: Seq[Artifact]): Seq[IArtifact] = - { - lazy val allConfigurations = moduleID.getPublicConfigurationsNames - for (artifact <- artifacts) yield toIvyArtifact(moduleID, artifact, allConfigurations) - } - - /** - * This code converts the given ModuleDescriptor to a DefaultModuleDescriptor by casting or generating an error. - * Ivy 2.0.0 always produces a DefaultModuleDescriptor. - */ - private def toDefaultModuleDescriptor(md: ModuleDescriptor) = - md match { - case dmd: DefaultModuleDescriptor => dmd - case _ => sys.error("Unknown ModuleDescriptor type.") - } - def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) = - configurations match { - case Some(confs) => confs.map(_.name).toList.toArray - case None => module.getPublicConfigurationsNames - } -} diff --git a/ivy/src/main/scala/sbt/IvyActions.scala b/ivy/src/main/scala/sbt/IvyActions.scala deleted file mode 100644 index 33436452c..000000000 --- a/ivy/src/main/scala/sbt/IvyActions.scala +++ /dev/null @@ -1,439 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import scala.xml.{ Node => XNode, NodeSeq } -import collection.mutable -import ivyint.CachedResolutionResolveEngine - -import org.apache.ivy.Ivy -import org.apache.ivy.core.{ IvyPatternHelper, LogOptions } -import org.apache.ivy.core.deliver.DeliverOptions -import org.apache.ivy.core.install.InstallOptions -import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, MDArtifact, ModuleDescriptor, DefaultModuleDescriptor } -import org.apache.ivy.core.report.ResolveReport -import org.apache.ivy.core.resolve.ResolveOptions -import org.apache.ivy.plugins.resolver.{ BasicResolver, DependencyResolver } - -final class DeliverConfiguration(val deliverIvyPattern: String, val status: String, val configurations: Option[Seq[Configuration]], val logging: UpdateLogging.Value) -final class PublishConfiguration(val ivyFile: Option[File], val resolverName: String, val artifacts: Map[Artifact, File], val checksums: Seq[String], val logging: UpdateLogging.Value, - val overwrite: Boolean) { - def this(ivyFile: Option[File], resolverName: String, artifacts: Map[Artifact, File], checksums: Seq[String], logging: UpdateLogging.Value) = - this(ivyFile, resolverName, artifacts, checksums, logging, false) -} - -final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value) { - private[sbt] def copy( - retrieve: Option[RetrieveConfiguration] = this.retrieve, - missingOk: Boolean = this.missingOk, - logging: UpdateLogging.Value = this.logging): UpdateConfiguration = - new UpdateConfiguration(retrieve, missingOk, logging) -} -final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String, val sync: Boolean, val configurationsToRetrieve: Option[Set[Configuration]]) { - def this(retrieveDirectory: File, outputPattern: String) = this(retrieveDirectory, outputPattern, false, None) - def this(retrieveDirectory: File, outputPattern: String, sync: Boolean) = this(retrieveDirectory, outputPattern, sync, None) -} -final case class MakePomConfiguration(file: File, moduleInfo: ModuleInfo, configurations: Option[Seq[Configuration]] = None, extra: NodeSeq = NodeSeq.Empty, process: XNode => XNode = n => n, filterRepositories: MavenRepository => Boolean = _ => true, allRepositories: Boolean, includeTypes: Set[String] = Set(Artifact.DefaultType, Artifact.PomType)) -// exclude is a map on a restricted ModuleID -final case class GetClassifiersConfiguration(module: GetClassifiersModule, exclude: Map[ModuleID, Set[String]], configuration: UpdateConfiguration, ivyScala: Option[IvyScala]) -final case class GetClassifiersModule(id: ModuleID, modules: Seq[ModuleID], configurations: Seq[Configuration], classifiers: Seq[String]) - -final class UnresolvedWarningConfiguration private[sbt] ( - val modulePositions: Map[ModuleID, SourcePosition]) -object UnresolvedWarningConfiguration { - def apply(): UnresolvedWarningConfiguration = apply(Map()) - def apply(modulePositions: Map[ModuleID, SourcePosition]): UnresolvedWarningConfiguration = - new UnresolvedWarningConfiguration(modulePositions) -} - -/** - * Configures logging during an 'update'. `level` determines the amount of other information logged. - * `Full` is the default and logs the most. - * `DownloadOnly` only logs what is downloaded. - * `Quiet` only displays errors. - * `Default` uses the current log level of `update` task. - */ -object UpdateLogging extends Enumeration { - val Full, DownloadOnly, Quiet, Default = Value -} - -object IvyActions { - /** Installs the dependencies of the given 'module' from the resolver named 'from' to the resolver named 'to'.*/ - def install(module: IvySbt#Module, from: String, to: String, log: Logger): Unit = { - module.withModule(log) { (ivy, md, default) => - for (dependency <- md.getDependencies) { - log.info("Installing " + dependency) - val options = new InstallOptions - options.setValidate(module.moduleSettings.validate) - options.setTransitive(dependency.isTransitive) - ivy.install(dependency.getDependencyRevisionId, from, to, options) - } - } - } - - /** Clears the Ivy cache, as configured by 'config'. */ - def cleanCache(ivy: IvySbt, log: Logger) = ivy.withIvy(log) { iv => - iv.getSettings.getResolutionCacheManager.clean() - iv.getSettings.getRepositoryCacheManagers.foreach(_.clean()) - } - - /** - * Cleans the cached resolution cache, if any. - * This is called by clean. - */ - private[sbt] def cleanCachedResolutionCache(module: IvySbt#Module, log: Logger): Unit = - module.withModule(log) { (ivy, md, default) => - module.owner.cleanCachedResolutionCache(md, log) - } - - /** Creates a Maven pom from the given Ivy configuration*/ - def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger): Unit = { - import configuration.{ allRepositories, moduleInfo, configurations, extra, file, filterRepositories, process, includeTypes } - module.withModule(log) { (ivy, md, default) => - (new MakePom(log)).write(ivy, md, moduleInfo, configurations, includeTypes, extra, process, filterRepositories, allRepositories, file) - log.info("Wrote " + file.getAbsolutePath) - } - } - - def deliver(module: IvySbt#Module, configuration: DeliverConfiguration, log: Logger): File = - { - import configuration._ - module.withModule(log) { - case (ivy, md, default) => - val revID = md.getModuleRevisionId - val options = DeliverOptions.newInstance(ivy.getSettings).setStatus(status) - options.setConfs(IvySbt.getConfigurations(md, configurations)) - ivy.deliver(revID, revID.getRevision, deliverIvyPattern, options) - deliveredFile(ivy, deliverIvyPattern, md) - } - } - def deliveredFile(ivy: Ivy, pattern: String, md: ModuleDescriptor): File = - ivy.getSettings.resolveFile(IvyPatternHelper.substitute(pattern, md.getResolvedModuleRevisionId)) - - def publish(module: IvySbt#Module, configuration: PublishConfiguration, log: Logger): Unit = { - import configuration._ - module.withModule(log) { - case (ivy, md, default) => - val resolver = ivy.getSettings.getResolver(resolverName) - if (resolver eq null) sys.error("Undefined resolver '" + resolverName + "'") - val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) } - val cross = crossVersionMap(module.moduleSettings) - val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toSeq - withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = overwrite) } - } - } - private[this] def withChecksums[T](resolver: DependencyResolver, checksums: Seq[String])(act: => T): T = - resolver match { case br: BasicResolver => withChecksums(br, checksums)(act); case _ => act } - private[this] def withChecksums[T](resolver: BasicResolver, checksums: Seq[String])(act: => T): T = - { - val previous = resolver.getChecksumAlgorithms - resolver.setChecksums(checksums mkString ",") - try { act } - finally { resolver.setChecksums(previous mkString ",") } - } - private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] = - moduleSettings match { - case i: InlineConfiguration => CrossVersion(i.module, i.ivyScala) - case i: InlineConfigurationWithExcludes => CrossVersion(i.module, i.ivyScala) - case e: EmptyConfiguration => CrossVersion(e.module, e.ivyScala) - case _ => None - } - def mapArtifacts(module: ModuleDescriptor, cross: Option[String => String], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] = - { - val rawa = artifacts.keys.toSeq - val seqa = CrossVersion.substituteCross(rawa, cross) - val zipped = rawa zip IvySbt.mapArtifacts(module, seqa) - zipped map { case (a, ivyA) => (ivyA, artifacts(a)) } - } - /** - * Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration. - * 'updateConfig' configures the actual resolution and retrieval process. - */ - @deprecated("This is no longer public.", "0.13.6") - def update(module: IvySbt#Module, configuration: UpdateConfiguration, log: Logger): UpdateReport = - updateEither(module, configuration, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, log) match { - case Right(r) => r - case Left(w) => - throw w.resolveException - } - - /** - * Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration. - * 'updateConfig' configures the actual resolution and retrieval process. - */ - private[sbt] def updateEither(module: IvySbt#Module, configuration: UpdateConfiguration, - uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], log: Logger): Either[UnresolvedWarning, UpdateReport] = - module.withModule(log) { - case (ivy, md, default) if module.owner.configuration.updateOptions.cachedResolution && depDir.isDefined => - ivy.getResolveEngine match { - case x: CachedResolutionResolveEngine => - val iw = IvySbt.inconsistentDuplicateWarning(md) - iw foreach { log.warn(_) } - val resolveOptions = new ResolveOptions - val resolveId = ResolveOptions.getDefaultResolveId(md) - resolveOptions.setResolveId(resolveId) - resolveOptions.setLog(ivyLogLevel(configuration.logging)) - x.customResolve(md, configuration.missingOk, logicalClock, resolveOptions, depDir getOrElse { sys.error("dependency base directory is not specified") }, log) match { - case Left(x) => - Left(UnresolvedWarning(x, uwconfig)) - case Right(uReport) => - configuration.retrieve match { - case Some(rConf) => Right(retrieve(log, ivy, uReport, rConf)) - case None => Right(uReport) - } - } - } - case (ivy, md, default) => - val iw = IvySbt.inconsistentDuplicateWarning(md) - iw foreach { log.warn(_) } - val (report, err) = resolve(configuration.logging)(ivy, md, default) - err match { - case Some(x) if !configuration.missingOk => - Left(UnresolvedWarning(x, uwconfig)) - case _ => - val cachedDescriptor = ivy.getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md.getModuleRevisionId) - val uReport = IvyRetrieve.updateReport(report, cachedDescriptor) - configuration.retrieve match { - case Some(rConf) => Right(retrieve(log, ivy, uReport, rConf)) - case None => Right(uReport) - } - } - } - @deprecated("No longer used.", "0.13.6") - def processUnresolved(err: ResolveException, log: Logger): Unit = () - def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(report: UpdateReport): Map[T, Set[String]] = - report.configurations.flatMap { confReport => - val evicted = confReport.evicted.filter(moduleFilter) - val evictedSet = evicted.map(m => (m.organization, m.name)).toSet - val conflicted = confReport.allModules.filter(mod => evictedSet((mod.organization, mod.name))) - grouped(grouping)(conflicted ++ evicted) - } toMap; - - def grouped[T](grouping: ModuleID => T)(mods: Seq[ModuleID]): Map[T, Set[String]] = - mods groupBy (grouping) mapValues (_.map(_.revision).toSet) - - @deprecated("This is no longer public.", "0.13.6") - def transitiveScratch(ivySbt: IvySbt, label: String, config: GetClassifiersConfiguration, log: Logger): UpdateReport = - transitiveScratch(ivySbt, label, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, log) - - private[sbt] def transitiveScratch(ivySbt: IvySbt, label: String, config: GetClassifiersConfiguration, - uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], log: Logger): UpdateReport = - { - import config.{ configuration => c, ivyScala, module => mod } - import mod.{ id, modules => deps } - val base = restrictedCopy(id, true).copy(name = id.name + "$" + label) - val module = new ivySbt.Module(InlineConfigurationWithExcludes(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala)) - val report = updateEither(module, c, uwconfig, logicalClock, depDir, log) match { - case Right(r) => r - case Left(w) => - throw w.resolveException - } - val newConfig = config.copy(module = mod.copy(modules = report.allModules)) - updateClassifiers(ivySbt, newConfig, uwconfig, logicalClock, depDir, log) - } - @deprecated("This is no longer public.", "0.13.6") - def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, log: Logger): UpdateReport = - updateClassifiers(ivySbt, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, log) - - private[sbt] def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, - uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], log: Logger): UpdateReport = - { - import config.{ configuration => c, module => mod, _ } - import mod.{ configurations => confs, _ } - assert(classifiers.nonEmpty, "classifiers cannot be empty") - val baseModules = modules map { m => restrictedCopy(m, true) } - val deps = baseModules.distinct flatMap classifiedArtifacts(classifiers, exclude) - val base = restrictedCopy(id, true).copy(name = id.name + classifiers.mkString("$", "_", "")) - val module = new ivySbt.Module(InlineConfigurationWithExcludes(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala, configurations = confs)) - val upConf = new UpdateConfiguration(c.retrieve, true, c.logging) - updateEither(module, upConf, uwconfig, logicalClock, depDir, log) match { - case Right(r) => r - case Left(w) => - throw w.resolveException - } - } - def classifiedArtifacts(classifiers: Seq[String], exclude: Map[ModuleID, Set[String]])(m: ModuleID): Option[ModuleID] = - { - val excluded = exclude getOrElse (restrictedCopy(m, false), Set.empty) - val included = classifiers filterNot excluded - if (included.isEmpty) None else Some(m.copy(isTransitive = false, explicitArtifacts = classifiedArtifacts(m.name, included))) - } - def addExcluded(report: UpdateReport, classifiers: Seq[String], exclude: Map[ModuleID, Set[String]]): UpdateReport = - report.addMissing { id => classifiedArtifacts(id.name, classifiers filter getExcluded(id, exclude)) } - def classifiedArtifacts(name: String, classifiers: Seq[String]): Seq[Artifact] = - classifiers map { c => Artifact.classified(name, c) } - private[this] def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] = - exclude.getOrElse(restrictedCopy(id, false), Set.empty[String]) - - def extractExcludes(report: UpdateReport): Map[ModuleID, Set[String]] = - report.allMissing flatMap { case (_, mod, art) => art.classifier.map { c => (restrictedCopy(mod, false), c) } } groupBy (_._1) map { case (mod, pairs) => (mod, pairs.map(_._2).toSet) } - - private[this] def restrictedCopy(m: ModuleID, confs: Boolean) = - ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, extraAttributes = m.extraAttributes, configurations = if (confs) m.configurations else None) - private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) = - { - val resolveOptions = new ResolveOptions - val resolveId = ResolveOptions.getDefaultResolveId(module) - resolveOptions.setResolveId(resolveId) - resolveOptions.setLog(ivyLogLevel(logging)) - ResolutionCache.cleanModule(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager) - val resolveReport = ivy.resolve(module, resolveOptions) - val err = - if (resolveReport.hasError) { - val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct - val failedPaths = Map(resolveReport.getUnresolvedDependencies map { node => - val m = IvyRetrieve.toModuleID(node.getId) - val path = IvyRetrieve.findPath(node, module.getModuleRevisionId) map { x => - IvyRetrieve.toModuleID(x.getId) - } - m -> path - }: _*) - val failed = failedPaths.keys.toSeq - Some(new ResolveException(messages, failed, failedPaths)) - } else None - (resolveReport, err) - } - private def retrieve(log: Logger, ivy: Ivy, report: UpdateReport, config: RetrieveConfiguration): UpdateReport = - retrieve(log, ivy, report, config.retrieveDirectory, config.outputPattern, config.sync, config.configurationsToRetrieve) - - private def retrieve(log: Logger, ivy: Ivy, report: UpdateReport, base: File, pattern: String, sync: Boolean, configurationsToRetrieve: Option[Set[Configuration]]): UpdateReport = - { - val configurationNames = configurationsToRetrieve match { - case None => None - case Some(configs) => Some(configs.map(_.name)) - } - val existingFiles = PathFinder(base).***.get filterNot { _.isDirectory } - val toCopy = new collection.mutable.HashSet[(File, File)] - val retReport = report retrieve { (conf, mid, art, cached) => - configurationNames match { - case None => performRetrieve(conf, mid, art, base, pattern, cached, toCopy) - case Some(names) if names(conf) => performRetrieve(conf, mid, art, base, pattern, cached, toCopy) - case _ => cached - } - } - IO.copy(toCopy) - val resolvedFiles = toCopy.map(_._2) - if (sync) { - val filesToDelete = existingFiles.filterNot(resolvedFiles.contains) - filesToDelete foreach { f => - log.info(s"Deleting old dependency: ${f.getAbsolutePath}") - f.delete() - } - } - - retReport - } - - private def performRetrieve(conf: String, mid: ModuleID, art: Artifact, base: File, pattern: String, cached: File, toCopy: collection.mutable.HashSet[(File, File)]): File = { - val to = retrieveTarget(conf, mid, art, base, pattern) - toCopy += ((cached, to)) - to - } - - private def retrieveTarget(conf: String, mid: ModuleID, art: Artifact, base: File, pattern: String): File = - new File(base, substitute(conf, mid, art, pattern)) - - private def substitute(conf: String, mid: ModuleID, art: Artifact, pattern: String): String = - { - val mextra = IvySbt.javaMap(mid.extraAttributes, true) - val aextra = IvySbt.extra(art, true) - IvyPatternHelper.substitute(pattern, mid.organization, mid.name, mid.revision, art.name, art.`type`, art.extension, conf, mextra, aextra) - } - - import UpdateLogging.{ Quiet, Full, DownloadOnly, Default } - import LogOptions.{ LOG_QUIET, LOG_DEFAULT, LOG_DOWNLOAD_ONLY } - private def ivyLogLevel(level: UpdateLogging.Value) = - level match { - case Quiet => LOG_QUIET - case DownloadOnly => LOG_DOWNLOAD_ONLY - case Full => LOG_DEFAULT - case Default => LOG_DOWNLOAD_ONLY - } - - def publish(module: ModuleDescriptor, artifacts: Seq[(IArtifact, File)], resolver: DependencyResolver, overwrite: Boolean): Unit = - { - if (artifacts.nonEmpty) { - checkFilesPresent(artifacts) - try { - resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite); - for ((artifact, file) <- artifacts) - resolver.publish(artifact, file, overwrite) - resolver.commitPublishTransaction() - } catch { - case e: Throwable => - try { resolver.abortPublishTransaction() } - finally { throw e } - } - } - } - private[this] def checkFilesPresent(artifacts: Seq[(IArtifact, File)]): Unit = { - val missing = artifacts filter { case (a, file) => !file.exists } - if (missing.nonEmpty) - sys.error("Missing files for publishing:\n\t" + missing.map(_._2.getAbsolutePath).mkString("\n\t")) - } -} -final class ResolveException( - val messages: Seq[String], - val failed: Seq[ModuleID], - val failedPaths: Map[ModuleID, Seq[ModuleID]]) extends RuntimeException(messages.mkString("\n")) { - def this(messages: Seq[String], failed: Seq[ModuleID]) = - this(messages, failed, Map(failed map { m => m -> Nil }: _*)) -} -/** - * Represents unresolved dependency warning, which displays reconstructed dependency tree - * along with source position of each node. - */ -final class UnresolvedWarning private[sbt] ( - val resolveException: ResolveException, - val failedPaths: Seq[Seq[(ModuleID, Option[SourcePosition])]]) -object UnresolvedWarning { - private[sbt] def apply(err: ResolveException, config: UnresolvedWarningConfiguration): UnresolvedWarning = { - def modulePosition(m0: ModuleID): Option[SourcePosition] = - config.modulePositions.find { - case (m, p) => - (m.organization == m0.organization) && - (m0.name startsWith m.name) && - (m.revision == m0.revision) - } map { - case (m, p) => p - } - val failedPaths = err.failed map { x: ModuleID => - err.failedPaths(x).toList.reverse map { id => - (id, modulePosition(id)) - } - } - apply(err, failedPaths) - } - private[sbt] def apply(err: ResolveException, failedPaths: Seq[Seq[(ModuleID, Option[SourcePosition])]]): UnresolvedWarning = - new UnresolvedWarning(err, failedPaths) - private[sbt] def sourcePosStr(posOpt: Option[SourcePosition]): String = - posOpt match { - case Some(LinePosition(path, start)) => s" ($path#L$start)" - case Some(RangePosition(path, LineRange(start, end))) => s" ($path#L$start-$end)" - case _ => "" - } - implicit val unresolvedWarningLines: ShowLines[UnresolvedWarning] = ShowLines { a => - val withExtra = a.resolveException.failed.filter(_.extraDependencyAttributes.nonEmpty) - val buffer = mutable.ListBuffer[String]() - if (withExtra.nonEmpty) { - buffer += "\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes." - withExtra foreach { id => buffer += "\t\t" + id } - } - if (a.failedPaths.nonEmpty) { - buffer += "\n\tNote: Unresolved dependencies path:" - a.failedPaths foreach { path => - if (path.nonEmpty) { - val head = path.head - buffer += "\t\t" + head._1.toString + sourcePosStr(head._2) - path.tail foreach { - case (m, pos) => - buffer += "\t\t +- " + m.toString + sourcePosStr(pos) - } - } - } - } - buffer.toList - } -} diff --git a/ivy/src/main/scala/sbt/IvyCache.scala b/ivy/src/main/scala/sbt/IvyCache.scala deleted file mode 100644 index ce2ddd9f2..000000000 --- a/ivy/src/main/scala/sbt/IvyCache.scala +++ /dev/null @@ -1,97 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import java.net.URL - -import org.apache.ivy.core.cache.{ ArtifactOrigin, CacheDownloadOptions, DefaultRepositoryCacheManager } -import org.apache.ivy.core.module.descriptor.{ Artifact => IvyArtifact, DefaultArtifact } -import org.apache.ivy.plugins.repository.file.{ FileRepository => IvyFileRepository, FileResource } -import org.apache.ivy.plugins.repository.{ ArtifactResourceResolver, Resource, ResourceDownloader } -import org.apache.ivy.plugins.resolver.util.ResolvedResource -import org.apache.ivy.util.FileUtil - -class NotInCache(val id: ModuleID, cause: Throwable) - extends RuntimeException(NotInCache(id, cause), cause) { - def this(id: ModuleID) = this(id, null) -} -private object NotInCache { - def apply(id: ModuleID, cause: Throwable) = - { - val postfix = if (cause == null) "" else (": " + cause.toString) - "File for " + id + " not in cache" + postfix - } -} -/** Provides methods for working at the level of a single jar file with the default Ivy cache.*/ -class IvyCache(val ivyHome: Option[File]) { - def lockFile = new File(ivyHome getOrElse Path.userHome, ".sbt.cache.lock") - /** Caches the given 'file' with the given ID. It may be retrieved or cleared using this ID.*/ - def cacheJar(moduleID: ModuleID, file: File, lock: Option[xsbti.GlobalLock], log: Logger): Unit = { - val artifact = defaultArtifact(moduleID) - val resolved = new ResolvedResource(new FileResource(new IvyFileRepository, file), moduleID.revision) - withDefaultCache(lock, log) { cache => - val resolver = new ArtifactResourceResolver { def resolve(artifact: IvyArtifact) = resolved } - cache.download(artifact, resolver, new FileDownloader, new CacheDownloadOptions) - } - } - /** Clears the cache of the jar for the given ID.*/ - def clearCachedJar(id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger): Unit = { - try { withCachedJar(id, lock, log)(_.delete) } - catch { case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) } - } - /** Copies the cached jar for the given ID to the directory 'toDirectory'. If the jar is not in the cache, NotInCache is thrown.*/ - def retrieveCachedJar(id: ModuleID, toDirectory: File, lock: Option[xsbti.GlobalLock], log: Logger) = - withCachedJar(id, lock, log) { cachedFile => - val copyTo = new File(toDirectory, cachedFile.getName) - FileUtil.copy(cachedFile, copyTo, null) - copyTo - } - - /** Get the location of the cached jar for the given ID in the Ivy cache. If the jar is not in the cache, NotInCache is thrown .*/ - def withCachedJar[T](id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger)(f: File => T): T = - { - val cachedFile = - try { - withDefaultCache(lock, log) { cache => - val artifact = defaultArtifact(id) - cache.getArchiveFileInCache(artifact, unknownOrigin(artifact)) - } - } catch { case e: Exception => throw new NotInCache(id, e) } - - if (cachedFile.exists) f(cachedFile) else throw new NotInCache(id) - } - /** Calls the given function with the default Ivy cache.*/ - def withDefaultCache[T](lock: Option[xsbti.GlobalLock], log: Logger)(f: DefaultRepositoryCacheManager => T): T = - { - val (ivy, local) = basicLocalIvy(lock, log) - ivy.withIvy(log) { ivy => - val cache = ivy.getSettings.getDefaultRepositoryCacheManager.asInstanceOf[DefaultRepositoryCacheManager] - cache.setUseOrigin(false) - f(cache) - } - } - private def unknownOrigin(artifact: IvyArtifact) = ArtifactOrigin.unkwnown(artifact) - /** A minimal Ivy setup with only a local resolver and the current directory as the base directory.*/ - private def basicLocalIvy(lock: Option[xsbti.GlobalLock], log: Logger) = - { - val local = Resolver.defaultLocal - val paths = new IvyPaths(new File("."), ivyHome) - val conf = new InlineIvyConfiguration(paths, Seq(local), Nil, Nil, false, lock, IvySbt.DefaultChecksums, None, log) - (new IvySbt(conf), local) - } - /** Creates a default jar artifact based on the given ID.*/ - private def defaultArtifact(moduleID: ModuleID): IvyArtifact = - new DefaultArtifact(IvySbt.toID(moduleID), null, moduleID.name, "jar", "jar") -} -/** Required by Ivy for copying to the cache.*/ -private class FileDownloader extends ResourceDownloader with NotNull { - def download(artifact: IvyArtifact, resource: Resource, dest: File): Unit = { - if (dest.exists()) dest.delete() - val part = new File(dest.getAbsolutePath + ".part") - FileUtil.copy(resource.openStream, part, null) - if (!part.renameTo(dest)) - sys.error("Could not move temporary file " + part + " to final location " + dest) - } -} diff --git a/ivy/src/main/scala/sbt/IvyConfigurations.scala b/ivy/src/main/scala/sbt/IvyConfigurations.scala deleted file mode 100644 index ed8df9546..000000000 --- a/ivy/src/main/scala/sbt/IvyConfigurations.scala +++ /dev/null @@ -1,255 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import java.net.{ URI, URL } -import scala.xml.NodeSeq - -final class IvyPaths(val baseDirectory: File, val ivyHome: Option[File]) { - def withBase(newBaseDirectory: File) = new IvyPaths(newBaseDirectory, ivyHome) -} -sealed trait IvyConfiguration { - type This <: IvyConfiguration - def lock: Option[xsbti.GlobalLock] - def baseDirectory: File - def log: Logger - def withBase(newBaseDirectory: File): This - def updateOptions: UpdateOptions -} -final class InlineIvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver], val otherResolvers: Seq[Resolver], - val moduleConfigurations: Seq[ModuleConfiguration], val localOnly: Boolean, val lock: Option[xsbti.GlobalLock], - val checksums: Seq[String], val resolutionCacheDir: Option[File], val updateOptions: UpdateOptions, - val log: Logger) extends IvyConfiguration { - @deprecated("Use the variant that accepts resolutionCacheDir and updateOptions.", "0.13.0") - def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver], - moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock], - checksums: Seq[String], log: Logger) = - this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, None, UpdateOptions(), log) - - @deprecated("Use the variant that accepts updateOptions.", "0.13.6") - def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver], - moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock], - checksums: Seq[String], resolutionCacheDir: Option[File], log: Logger) = - this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, UpdateOptions(), log) - - override def toString: String = s"InlineIvyConfiguration($paths, $resolvers, $otherResolvers, " + - s"$moduleConfigurations, $localOnly, $checksums, $resolutionCacheDir, $updateOptions)" - - type This = InlineIvyConfiguration - def baseDirectory = paths.baseDirectory - def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, - resolutionCacheDir, updateOptions, log) - def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, - resolutionCacheDir, updateOptions, log) - - override def equals(o: Any): Boolean = o match { - case o: InlineIvyConfiguration => - this.paths == o.paths && - this.resolvers == o.resolvers && - this.otherResolvers == o.otherResolvers && - this.moduleConfigurations == o.moduleConfigurations && - this.localOnly == o.localOnly && - this.checksums == o.checksums && - this.resolutionCacheDir == o.resolutionCacheDir && - this.updateOptions == o.updateOptions - case _ => false - } - - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.paths.## - hash = hash * 31 + this.resolvers.## - hash = hash * 31 + this.otherResolvers.## - hash = hash * 31 + this.moduleConfigurations.## - hash = hash * 31 + this.localOnly.## - hash = hash * 31 + this.checksums.## - hash = hash * 31 + this.resolutionCacheDir.## - hash = hash * 31 + this.updateOptions.## - hash - } -} -final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock], - val extraResolvers: Seq[Resolver], val updateOptions: UpdateOptions, val log: Logger) extends IvyConfiguration { - @deprecated("Use the variant that accepts updateOptions.", "0.13.6") - def this(baseDirectory: File, uri: URI, lock: Option[xsbti.GlobalLock], extraResolvers: Seq[Resolver], log: Logger) = - this(baseDirectory, uri, lock, extraResolvers, UpdateOptions(), log) - - type This = ExternalIvyConfiguration - def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log) -} -object ExternalIvyConfiguration { - def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, UpdateOptions(), log) -} - -object IvyConfiguration { - /** - * Called to configure Ivy when inline resolvers are not specified. - * This will configure Ivy with an 'ivy-settings.xml' file if there is one or else use default resolvers. - */ - @deprecated("Explicitly use either external or inline configuration.", "0.12.0") - def apply(paths: IvyPaths, lock: Option[xsbti.GlobalLock], localOnly: Boolean, checksums: Seq[String], log: Logger): IvyConfiguration = - { - log.debug("Autodetecting configuration.") - val defaultIvyConfigFile = IvySbt.defaultIvyConfiguration(paths.baseDirectory) - if (defaultIvyConfigFile.canRead) - ExternalIvyConfiguration(paths.baseDirectory, defaultIvyConfigFile, lock, log) - else - new InlineIvyConfiguration(paths, Resolver.withDefaultResolvers(Nil), Nil, Nil, localOnly, lock, checksums, None, log) - } -} - -sealed trait ModuleSettings { - def validate: Boolean - def ivyScala: Option[IvyScala] - def noScala: ModuleSettings -} -final case class IvyFileConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings { - def noScala = copy(ivyScala = None) -} -final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings { - def noScala = copy(ivyScala = None) -} - -// TODO: When we go sbt 1.0 we should rename InlineConfigurationWithExcludes to InlineConfiguration. -@deprecated("Use InlineConfigurationWithExcludes.", "0.13.8") -final case class InlineConfiguration(module: ModuleID, moduleInfo: ModuleInfo, dependencies: Seq[ModuleID], overrides: Set[ModuleID] = Set.empty, ivyXML: NodeSeq = NodeSeq.Empty, configurations: Seq[Configuration] = Nil, defaultConfiguration: Option[Configuration] = None, ivyScala: Option[IvyScala] = None, validate: Boolean = false, conflictManager: ConflictManager = ConflictManager.default) extends ModuleSettings { - def withConfigurations(configurations: Seq[Configuration]) = copy(configurations = configurations) - def noScala = copy(ivyScala = None) - def withExcludes: InlineConfigurationWithExcludes = - InlineConfigurationWithExcludes(this.module, this.moduleInfo, this.dependencies, this.overrides, Nil, this.ivyXML, - this.configurations, this.defaultConfiguration, this.ivyScala, this.validate, this.conflictManager) - def withOverrides(overrides: Set[ModuleID]): ModuleSettings = - copy(overrides = overrides) -} -object InlineConfiguration { - @deprecated("Use InlineConfigurationWithExcludes.explicitConfigurations.", "0.13.8") - def configurations(explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration]) = - if (explicitConfigurations.isEmpty) { - defaultConfiguration match { - case Some(Configurations.DefaultIvyConfiguration) => Configurations.Default :: Nil - case Some(Configurations.DefaultMavenConfiguration) => Configurations.defaultMavenConfigurations - case _ => Nil - } - } else - explicitConfigurations -} - -final class InlineConfigurationWithExcludes private[sbt] (val module: ModuleID, - val moduleInfo: ModuleInfo, - val dependencies: Seq[ModuleID], - val overrides: Set[ModuleID], - val excludes: Seq[SbtExclusionRule], - val ivyXML: NodeSeq, - val configurations: Seq[Configuration], - val defaultConfiguration: Option[Configuration], - val ivyScala: Option[IvyScala], - val validate: Boolean, - val conflictManager: ConflictManager) extends ModuleSettings { - def withConfigurations(configurations: Seq[Configuration]) = copy(configurations = configurations) - def noScala = copy(ivyScala = None) - def withOverrides(overrides: Set[ModuleID]): ModuleSettings = - copy(overrides = overrides) - - private[sbt] def copy(module: ModuleID = this.module, - moduleInfo: ModuleInfo = this.moduleInfo, - dependencies: Seq[ModuleID] = this.dependencies, - overrides: Set[ModuleID] = this.overrides, - excludes: Seq[SbtExclusionRule] = this.excludes, - ivyXML: NodeSeq = this.ivyXML, - configurations: Seq[Configuration] = this.configurations, - defaultConfiguration: Option[Configuration] = this.defaultConfiguration, - ivyScala: Option[IvyScala] = this.ivyScala, - validate: Boolean = this.validate, - conflictManager: ConflictManager = this.conflictManager): InlineConfigurationWithExcludes = - InlineConfigurationWithExcludes(module, moduleInfo, dependencies, overrides, excludes, ivyXML, - configurations, defaultConfiguration, ivyScala, validate, conflictManager) - - override def toString: String = - s"InlineConfigurationWithExcludes($module, $moduleInfo, $dependencies, $overrides, $excludes, " + - s"$ivyXML, $configurations, $defaultConfiguration, $ivyScala, $validate, $conflictManager)" - - override def equals(o: Any): Boolean = o match { - case o: InlineConfigurationWithExcludes => - this.module == o.module && - this.moduleInfo == o.moduleInfo && - this.dependencies == o.dependencies && - this.overrides == o.overrides && - this.excludes == o.excludes && - this.ivyXML == o.ivyXML && - this.configurations == o.configurations && - this.defaultConfiguration == o.defaultConfiguration && - this.ivyScala == o.ivyScala && - this.validate == o.validate && - this.conflictManager == o.conflictManager - case _ => false - } - - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.module.## - hash = hash * 31 + this.dependencies.## - hash = hash * 31 + this.overrides.## - hash = hash * 31 + this.excludes.## - hash = hash * 31 + this.ivyXML.## - hash = hash * 31 + this.configurations.## - hash = hash * 31 + this.defaultConfiguration.## - hash = hash * 31 + this.ivyScala.## - hash = hash * 31 + this.validate.## - hash = hash * 31 + this.conflictManager.## - hash - } -} -object InlineConfigurationWithExcludes { - def apply(module: ModuleID, - moduleInfo: ModuleInfo, - dependencies: Seq[ModuleID], - overrides: Set[ModuleID] = Set.empty, - excludes: Seq[SbtExclusionRule] = Nil, - ivyXML: NodeSeq = NodeSeq.Empty, - configurations: Seq[Configuration] = Nil, - defaultConfiguration: Option[Configuration] = None, - ivyScala: Option[IvyScala] = None, - validate: Boolean = false, - conflictManager: ConflictManager = ConflictManager.default): InlineConfigurationWithExcludes = - new InlineConfigurationWithExcludes(module, moduleInfo, dependencies, overrides, excludes, ivyXML, - configurations, defaultConfiguration, ivyScala, validate, conflictManager) - - def configurations(explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration]) = - if (explicitConfigurations.isEmpty) { - defaultConfiguration match { - case Some(Configurations.DefaultIvyConfiguration) => Configurations.Default :: Nil - case Some(Configurations.DefaultMavenConfiguration) => Configurations.defaultMavenConfigurations - case _ => Nil - } - } else - explicitConfigurations -} - -@deprecated("Define a module using inline Scala (InlineConfiguration), a pom.xml (PomConfiguration), or an ivy.xml (IvyFileConfiguration).", "0.13.0") -final case class EmptyConfiguration(module: ModuleID, moduleInfo: ModuleInfo, ivyScala: Option[IvyScala], validate: Boolean) extends ModuleSettings { - def noScala = copy(ivyScala = None) -} - -object ModuleSettings { - @deprecated("Explicitly select configuration from pom.xml, ivy.xml, or inline Scala.", "0.13.0") - def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID, moduleInfo: => ModuleInfo)(baseDirectory: File, log: Logger): ModuleSettings = - { - log.debug("Autodetecting dependencies.") - val defaultPOMFile = IvySbt.defaultPOM(baseDirectory) - if (defaultPOMFile.canRead) - new PomConfiguration(defaultPOMFile, ivyScala, validate, true) - else { - val defaultIvy = IvySbt.defaultIvyFile(baseDirectory) - if (defaultIvy.canRead) - new IvyFileConfiguration(defaultIvy, ivyScala, validate, true) - else { - log.warn("No dependency configuration found, using defaults.") - new EmptyConfiguration(module, moduleInfo, ivyScala, validate) - } - } - } -} diff --git a/ivy/src/main/scala/sbt/IvyInterface.scala b/ivy/src/main/scala/sbt/IvyInterface.scala deleted file mode 100644 index 3b54c684c..000000000 --- a/ivy/src/main/scala/sbt/IvyInterface.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import java.net.{ URI, URL } -import scala.xml.NodeSeq -import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver } -import org.apache.ivy.util.url.CredentialsStore -import sbt.serialization._ - -/** Additional information about a project module */ -final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None, developers: Seq[Developer] = Seq()) { - def this(nameFormal: String, description: String, homepage: Option[URL], startYear: Option[Int], licenses: Seq[(String, URL)], organizationName: String, organizationHomepage: Option[URL], scmInfo: Option[ScmInfo]) = - this(nameFormal, description, homepage, startYear, licenses, organizationName, organizationHomepage, scmInfo, Seq()) - def formally(name: String) = copy(nameFormal = name) - def describing(desc: String, home: Option[URL]) = copy(description = desc, homepage = home) - def licensed(lics: (String, URL)*) = copy(licenses = lics) - def organization(name: String, home: Option[URL]) = copy(organizationName = name, organizationHomepage = home) -} - -/** Basic SCM information for a project module */ -final case class ScmInfo(browseUrl: URL, connection: String, devConnection: Option[String] = None) - -final case class Developer(id: String, name: String, email: String, url: URL) - -/** Rule to exclude unwanted dependencies pulled in transitively by a module. */ -final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) -object ExclusionRule { - implicit val pickler: Pickler[ExclusionRule] with Unpickler[ExclusionRule] = PicklerUnpickler.generate[ExclusionRule] -} - -final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) -object ModuleConfiguration { - def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver) - def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver) -} - -final case class ConflictManager(name: String, organization: String = "*", module: String = "*") - -/** See http://ant.apache.org/ivy/history/latest-milestone/settings/conflict-managers.html for details of the different conflict managers.*/ -object ConflictManager { - val all = ConflictManager("all") - val latestTime = ConflictManager("latest-time") - val latestRevision = ConflictManager("latest-revision") - val latestCompatible = ConflictManager("latest-compatible") - val strict = ConflictManager("strict") - val default = latestRevision -} diff --git a/ivy/src/main/scala/sbt/IvyLogger.scala b/ivy/src/main/scala/sbt/IvyLogger.scala deleted file mode 100644 index 94268672e..000000000 --- a/ivy/src/main/scala/sbt/IvyLogger.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import org.apache.ivy.util.{ Message, MessageLogger, MessageLoggerEngine } - -/** Interface to Ivy logging. */ -private final class IvyLoggerInterface(logger: Logger) extends MessageLogger { - def rawlog(msg: String, level: Int): Unit = log(msg, level) - def log(msg: String, level: Int): Unit = { - import Message.{ MSG_DEBUG, MSG_VERBOSE, MSG_INFO, MSG_WARN, MSG_ERR } - level match { - case MSG_DEBUG => debug(msg) - case MSG_VERBOSE => verbose(msg) - case MSG_INFO => info(msg) - case MSG_WARN => warn(msg) - case MSG_ERR => error(msg) - } - } - //DEBUG level messages are very verbose and rarely useful to users. - // TODO: provide access to this information some other way - def debug(msg: String): Unit = () - def verbose(msg: String): Unit = logger.verbose(msg) - def deprecated(msg: String): Unit = warn(msg) - def info(msg: String): Unit = logger.info(msg) - def rawinfo(msg: String): Unit = info(msg) - def warn(msg: String): Unit = logger.warn(msg) - def error(msg: String): Unit = if (SbtIvyLogger.acceptError(msg)) logger.error(msg) - - private def emptyList = java.util.Collections.emptyList[String] - def getProblems = emptyList - def getWarns = emptyList - def getErrors = emptyList - - def clearProblems(): Unit = () - def sumupProblems(): Unit = clearProblems() - def progress(): Unit = () - def endProgress(): Unit = () - - def endProgress(msg: String): Unit = info(msg) - def isShowProgress = false - def setShowProgress(progress: Boolean): Unit = () -} -private final class SbtMessageLoggerEngine extends MessageLoggerEngine { - /** This is a hack to filter error messages about 'unknown resolver ...'. */ - override def error(msg: String): Unit = if (SbtIvyLogger.acceptError(msg)) super.error(msg) - override def sumupProblems(): Unit = clearProblems() -} -private object SbtIvyLogger { - val UnknownResolver = "unknown resolver" - def acceptError(msg: String) = (msg ne null) && !msg.startsWith(UnknownResolver) -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/IvyRetrieve.scala b/ivy/src/main/scala/sbt/IvyRetrieve.scala deleted file mode 100644 index 14ad917fa..000000000 --- a/ivy/src/main/scala/sbt/IvyRetrieve.scala +++ /dev/null @@ -1,188 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import java.io.File -import java.{ util => ju } -import collection.mutable -import java.net.URL -import org.apache.ivy.core.{ module, report, resolve } -import module.descriptor.{ Artifact => IvyArtifact, License => IvyLicense } -import module.id.{ ModuleRevisionId, ModuleId => IvyModuleId } -import report.{ ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport } -import resolve.{ IvyNode, IvyNodeCallers } -import IvyNodeCallers.{ Caller => IvyCaller } -import ivyint.SbtDefaultDependencyDescriptor - -object IvyRetrieve { - def reports(report: ResolveReport): Seq[ConfigurationResolveReport] = - report.getConfigurations map report.getConfigurationReport - - def moduleReports(confReport: ConfigurationResolveReport): Seq[ModuleReport] = - for { - revId <- confReport.getModuleRevisionIds.toArray.toVector collect { case revId: ModuleRevisionId => revId } - } yield moduleRevisionDetail(confReport, confReport.getDependency(revId)) - - @deprecated("Internal only. No longer in use.", "0.13.6") - def artifactReports(mid: ModuleID, artReport: Seq[ArtifactDownloadReport]): ModuleReport = - { - val (resolved, missing) = artifacts(mid, artReport) - ModuleReport(mid, resolved, missing) - } - - private[sbt] def artifacts(mid: ModuleID, artReport: Seq[ArtifactDownloadReport]): (Seq[(Artifact, File)], Seq[Artifact]) = - { - val missing = new mutable.ListBuffer[Artifact] - val resolved = new mutable.ListBuffer[(Artifact, File)] - for (r <- artReport) { - val fileOpt = Option(r.getLocalFile) - val art = toArtifact(r.getArtifact) - fileOpt match { - case Some(file) => resolved += ((art, file)) - case None => missing += art - } - } - (resolved.toSeq, missing.toSeq) - } - - // We need this because current module report used as part of UpdateReport/ConfigurationReport contains - // only the revolved modules. - // Sometimes the entire module can be excluded via rules etc. - private[sbt] def organizationArtifactReports(confReport: ConfigurationResolveReport): Seq[OrganizationArtifactReport] = { - val dependencies = confReport.getModuleRevisionIds.toArray.toVector collect { case revId: ModuleRevisionId => revId } - val moduleIds = confReport.getModuleIds.toArray.toVector collect { case mId: IvyModuleId => mId } - def organizationArtifact(mid: IvyModuleId): OrganizationArtifactReport = { - val deps = confReport.getNodes(mid).toArray.toVector collect { case node: IvyNode => node } - OrganizationArtifactReport(mid.getOrganisation, mid.getName, deps map { moduleRevisionDetail(confReport, _) }) - } - moduleIds map { organizationArtifact } - } - - private[sbt] def nonEmptyString(s: String): Option[String] = - s match { - case null => None - case x if x.trim == "" => None - case x => Some(x.trim) - } - - private[sbt] def moduleRevisionDetail(confReport: ConfigurationResolveReport, dep: IvyNode): ModuleReport = { - def toExtraAttributes(ea: ju.Map[_, _]): Map[String, String] = - Map(ea.entrySet.toArray collect { - case entry: ju.Map.Entry[_, _] if nonEmptyString(entry.getKey.toString).isDefined && nonEmptyString(entry.getValue.toString).isDefined => - (entry.getKey.toString, entry.getValue.toString) - }: _*) - def toCaller(caller: IvyCaller): Caller = { - val m = toModuleID(caller.getModuleRevisionId) - val callerConfigurations = caller.getCallerConfigurations.toVector collect { - case x if nonEmptyString(x).isDefined => x - } - val ddOpt = Option(caller.getDependencyDescriptor) - val (extraAttributes, isForce, isChanging, isTransitive, isDirectlyForce) = ddOpt match { - case Some(dd: SbtDefaultDependencyDescriptor) => - val mod = dd.dependencyModuleId - (toExtraAttributes(dd.getExtraAttributes), mod.isForce, mod.isChanging, mod.isTransitive, mod.isForce) - case Some(dd) => (toExtraAttributes(dd.getExtraAttributes), dd.isForce, dd.isChanging, dd.isTransitive, false) - case None => (Map.empty[String, String], false, false, true, false) - } - new Caller(m, callerConfigurations, extraAttributes, isForce, isChanging, isTransitive, isDirectlyForce) - } - val revId = dep.getResolvedId - val moduleId = toModuleID(revId) - val branch = nonEmptyString(revId.getBranch) - val (status, publicationDate, resolver, artifactResolver) = dep.isLoaded match { - case true => - (nonEmptyString(dep.getDescriptor.getStatus), - Some(new ju.Date(dep.getPublication)), - nonEmptyString(dep.getModuleRevision.getResolver.getName), - nonEmptyString(dep.getModuleRevision.getArtifactResolver.getName)) - case _ => (None, None, None, None) - } - val (evicted, evictedData, evictedReason) = dep.isEvicted(confReport.getConfiguration) match { - case true => - val edOpt = Option(dep.getEvictedData(confReport.getConfiguration)) - edOpt match { - case Some(ed) => - (true, - nonEmptyString(Option(ed.getConflictManager) map { _.toString } getOrElse { "transitive" }), - nonEmptyString(ed.getDetail)) - case None => (true, None, None) - } - case _ => (false, None, None) - } - val problem = dep.hasProblem match { - case true => nonEmptyString(dep.getProblem.getMessage) - case _ => None - } - val mdOpt = for { - mr <- Option(dep.getModuleRevision) - md <- Option(mr.getDescriptor) - } yield md - val homepage = mdOpt match { - case Some(md) => - nonEmptyString(md.getHomePage) - case _ => None - } - val extraAttributes: Map[String, String] = toExtraAttributes(mdOpt match { - case Some(md) => md.getExtraAttributes - case _ => dep.getResolvedId.getExtraAttributes - }) - val isDefault = Option(dep.getDescriptor) map { _.isDefault } - val configurations = dep.getConfigurations(confReport.getConfiguration).toList - val licenses: Seq[(String, Option[String])] = mdOpt match { - case Some(md) => md.getLicenses.toVector collect { - case lic: IvyLicense if Option(lic.getName).isDefined => - val temporaryURL = "http://localhost" - (lic.getName, nonEmptyString(lic.getUrl) orElse { Some(temporaryURL) }) - } - case _ => Nil - } - val callers = dep.getCallers(confReport.getConfiguration).toVector map { toCaller } - val (resolved, missing) = artifacts(moduleId, confReport getDownloadReports revId) - - new ModuleReport(moduleId, resolved, missing, status, publicationDate, resolver, artifactResolver, - evicted, evictedData, evictedReason, problem, homepage, extraAttributes, isDefault, branch, - configurations, licenses, callers) - } - - def evicted(confReport: ConfigurationResolveReport): Seq[ModuleID] = - confReport.getEvictedNodes.map(node => toModuleID(node.getId)) - - def toModuleID(revID: ModuleRevisionId): ModuleID = - ModuleID(revID.getOrganisation, revID.getName, revID.getRevision, extraAttributes = IvySbt.getExtraAttributes(revID)) - - def toArtifact(art: IvyArtifact): Artifact = - { - import art._ - Artifact(getName, getType, getExt, Option(getExtraAttribute("classifier")), getConfigurations map Configurations.config, Option(getUrl)) - } - - def updateReport(report: ResolveReport, cachedDescriptor: File): UpdateReport = - new UpdateReport(cachedDescriptor, reports(report) map configurationReport, updateStats(report), Map.empty) recomputeStamps () - def updateStats(report: ResolveReport): UpdateStats = - new UpdateStats(report.getResolveTime, report.getDownloadTime, report.getDownloadSize, false) - def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport = - new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport), organizationArtifactReports(confReport), evicted(confReport)) - - /** - * Tries to find Ivy graph path the from node to target. - */ - def findPath(target: IvyNode, from: ModuleRevisionId): List[IvyNode] = { - def doFindPath(current: IvyNode, path: List[IvyNode]): List[IvyNode] = { - // Ivy actually returns mix of direct and non-direct callers here. - // that's why we have to calculate all possible paths below and pick the longest path. - val callers = current.getAllRealCallers.toList - val callersRevId = (callers map { _.getModuleRevisionId }).distinct - val paths: List[List[IvyNode]] = ((callersRevId map { revId => - val node = current.findNode(revId) - if (revId == from) node :: path - else if (node == node.getRoot) Nil - else if (path contains node) path - else doFindPath(node, node :: path) - }) sortBy { _.size }).reverse - paths.headOption getOrElse Nil - } - if (target.getId == from) List(target) - else doFindPath(target, List(target)) - } -} diff --git a/ivy/src/main/scala/sbt/IvyScala.scala b/ivy/src/main/scala/sbt/IvyScala.scala deleted file mode 100644 index 2614df994..000000000 --- a/ivy/src/main/scala/sbt/IvyScala.scala +++ /dev/null @@ -1,119 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.util.Collections.emptyMap -import scala.collection.mutable.HashSet - -import org.apache.ivy.core.module.descriptor.{ DefaultExcludeRule, ExcludeRule } -import org.apache.ivy.core.module.descriptor.{ DependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor, OverrideDependencyDescriptorMediator } -import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } -import org.apache.ivy.plugins.matcher.ExactPatternMatcher - -object ScalaArtifacts { - import xsbti.ArtifactInfo._ - val Organization = ScalaOrganization - val LibraryID = ScalaLibraryID - val CompilerID = ScalaCompilerID - val ReflectID = "scala-reflect" - def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version) - - private[sbt] def toolDependencies(org: String, version: String): Seq[ModuleID] = Seq( - scalaToolDependency(org, ScalaArtifacts.CompilerID, version), - scalaToolDependency(org, ScalaArtifacts.LibraryID, version) - ) - private[this] def scalaToolDependency(org: String, id: String, version: String): ModuleID = - ModuleID(org, id, version, Some(Configurations.ScalaTool.name + "->default,optional(default)")) -} -object SbtArtifacts { - import xsbti.ArtifactInfo._ - val Organization = SbtOrganization -} - -import ScalaArtifacts._ - -final case class IvyScala(scalaFullVersion: String, scalaBinaryVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean, scalaOrganization: String = ScalaArtifacts.Organization) - -private object IvyScala { - /** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */ - def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala): Unit = { - if (check.checkExplicit) - checkDependencies(module, check.scalaBinaryVersion, check.configurations, log) - if (check.filterImplicit) - excludeScalaJars(module, check.configurations) - if (check.overrideScalaVersion) - overrideScalaVersion(module, check.scalaFullVersion) - } - def overrideScalaVersion(module: DefaultModuleDescriptor, version: String): Unit = { - overrideVersion(module, Organization, LibraryID, version) - overrideVersion(module, Organization, CompilerID, version) - overrideVersion(module, Organization, ReflectID, version) - } - def overrideVersion(module: DefaultModuleDescriptor, org: String, name: String, version: String): Unit = { - val id = new ModuleId(org, name) - val over = new OverrideDependencyDescriptorMediator(null, version) - module.addDependencyDescriptorMediator(id, ExactPatternMatcher.INSTANCE, over) - } - - /** - * Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the - * dependencies matches scalaVersion. - */ - private def checkDependencies(module: ModuleDescriptor, scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger): Unit = { - val configSet = if (configurations.isEmpty) (c: String) => true else configurationSet(configurations) - def binaryScalaWarning(dep: DependencyDescriptor): Option[String] = - { - val id = dep.getDependencyRevisionId - val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision) - def isScalaLangOrg = id.getOrganisation == Organization - def isNotScalaActorsMigration = !(id.getName startsWith "scala-actors-migration") // Exception to the rule: sbt/sbt#1818 - def isNotScalaPickling = !(id.getName startsWith "scala-pickling") // Exception to the rule: sbt/sbt#1899 - def hasBinVerMismatch = depBinaryVersion != scalaBinaryVersion - def matchesOneOfTheConfigs = dep.getModuleConfigurations.exists(configSet) - val mismatched = isScalaLangOrg && isNotScalaActorsMigration && isNotScalaPickling && hasBinVerMismatch && matchesOneOfTheConfigs - if (mismatched) - Some("Binary version (" + depBinaryVersion + ") for dependency " + id + - "\n\tin " + module.getModuleRevisionId + - " differs from Scala binary version in project (" + scalaBinaryVersion + ").") - else - None - } - module.getDependencies.toList.flatMap(binaryScalaWarning).toSet foreach { (s: String) => log.warn(s) } - } - private def configurationSet(configurations: Iterable[Configuration]) = configurations.map(_.toString).toSet - - /** - * Adds exclusions for the scala library and compiler jars so that they are not downloaded. This is - * done because these jars are provided by the ScalaInstance of the project. The version of Scala to use - * is done by setting scalaVersion in the project definition. - */ - private def excludeScalaJars(module: DefaultModuleDescriptor, configurations: Iterable[Configuration]): Unit = { - val configurationNames = - { - val names = module.getConfigurationsNames - if (configurations.isEmpty) - names - else { - val configSet = configurationSet(configurations) - configSet.intersect(HashSet(names: _*)) - configSet.toArray - } - } - def excludeScalaJar(name: String): Unit = - module.addExcludeRule(excludeRule(Organization, name, configurationNames, "jar")) - excludeScalaJar(LibraryID) - excludeScalaJar(CompilerID) - } - /** - * Creates an ExcludeRule that excludes artifacts with the given module organization and name for - * the given configurations. - */ - private[sbt] def excludeRule(organization: String, name: String, configurationNames: Iterable[String], excludeTypePattern: String): ExcludeRule = - { - val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", excludeTypePattern, "*") - val rule = new DefaultExcludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) - configurationNames.foreach(rule.addConfiguration) - rule - } -} diff --git a/ivy/src/main/scala/sbt/IvyUtil.scala b/ivy/src/main/scala/sbt/IvyUtil.scala deleted file mode 100644 index 0f06b6b84..000000000 --- a/ivy/src/main/scala/sbt/IvyUtil.scala +++ /dev/null @@ -1,6 +0,0 @@ -package sbt - -private[sbt] object IvyUtil { - def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) = - (l.flatMap(_.left.toOption), l.flatMap(_.right.toOption)) -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/JsonUtil.scala b/ivy/src/main/scala/sbt/JsonUtil.scala deleted file mode 100644 index d4db781f7..000000000 --- a/ivy/src/main/scala/sbt/JsonUtil.scala +++ /dev/null @@ -1,88 +0,0 @@ -package sbt - -import java.io.File -import java.net.URL -import org.apache.ivy.core -import core.module.descriptor.ModuleDescriptor -import sbt.serialization._ -import java.net.{ URLEncoder, URLDecoder } - -private[sbt] object JsonUtil { - def sbtOrgTemp = "org.scala-sbt.temp" - def fakeCallerOrganization = "org.scala-sbt.temp-callers" - - def parseUpdateReport(md: ModuleDescriptor, path: File, cachedDescriptor: File, log: Logger): UpdateReport = - { - try { - val lite = fromJsonFile[UpdateReportLite](path).get - fromLite(lite, cachedDescriptor) - } catch { - case e: Throwable => - log.error("Unable to parse mini graph: " + path.toString) - throw e - } - } - def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit = - { - IO.createDirectory(graphPath.getParentFile) - toJsonFile(toLite(ur), graphPath) - } - def toLite(ur: UpdateReport): UpdateReportLite = - UpdateReportLite(ur.configurations map { cr => - ConfigurationReportLite(cr.configuration, cr.details map { oar => - new OrganizationArtifactReport(oar.organization, oar.name, oar.modules map { mr => - new ModuleReport( - mr.module, mr.artifacts, mr.missingArtifacts, mr.status, - mr.publicationDate, mr.resolver, mr.artifactResolver, - mr.evicted, mr.evictedData, mr.evictedReason, - mr.problem, mr.homepage, mr.extraAttributes, - mr.isDefault, mr.branch, mr.configurations, mr.licenses, - filterOutArtificialCallers(mr.callers)) - }) - }) - }) - // #1763/#2030. Caller takes up 97% of space, so we need to shrink it down, - // but there are semantics associated with some of them. - def filterOutArtificialCallers(callers: Seq[Caller]): Seq[Caller] = - if (callers.isEmpty) callers - else { - val nonArtificial = callers filter { c => - (c.caller.organization != sbtOrgTemp) && - (c.caller.organization != fakeCallerOrganization) - } - val interProj = (callers find { c => - c.caller.organization == sbtOrgTemp - }).toList - interProj ::: nonArtificial.toList - } - - def fromLite(lite: UpdateReportLite, cachedDescriptor: File): UpdateReport = - { - val stats = new UpdateStats(0L, 0L, 0L, false) - val configReports = lite.configurations map { cr => - val details = cr.details - val modules = details flatMap { - _.modules filter { mr => - !mr.evicted && mr.problem.isEmpty - } - } - val evicted = details flatMap { - _.modules filter { mr => - mr.evicted - } - } map { _.module } - new ConfigurationReport(cr.configuration, modules, details, evicted) - } - new UpdateReport(cachedDescriptor, configReports, stats) - } -} - -private[sbt] case class UpdateReportLite(configurations: Seq[ConfigurationReportLite]) -private[sbt] object UpdateReportLite { - implicit val pickler: Pickler[UpdateReportLite] with Unpickler[UpdateReportLite] = PicklerUnpickler.generate[UpdateReportLite] -} - -private[sbt] case class ConfigurationReportLite(configuration: String, details: Seq[OrganizationArtifactReport]) -private[sbt] object ConfigurationReportLite { - implicit val pickler: Pickler[ConfigurationReportLite] with Unpickler[ConfigurationReportLite] = PicklerUnpickler.generate[ConfigurationReportLite] -} diff --git a/ivy/src/main/scala/sbt/LogicalClock.scala b/ivy/src/main/scala/sbt/LogicalClock.scala deleted file mode 100644 index d4e1d4cc3..000000000 --- a/ivy/src/main/scala/sbt/LogicalClock.scala +++ /dev/null @@ -1,21 +0,0 @@ -package sbt - -/** - * Represents a logical time point for dependency resolution. - * This is used to cache dependencies across subproject resolution which may change over time. - */ -trait LogicalClock { - def toString: String -} - -object LogicalClock { - def apply(hashCode: Int): LogicalClock = { - def intToByteArray(x: Int): Array[Byte] = - Array((x >>> 24).toByte, (x >> 16 & 0xff).toByte, (x >> 8 & 0xff).toByte, (x & 0xff).toByte) - apply(Hash.toHex(intToByteArray(hashCode))) - } - def apply(x: String): LogicalClock = new LogicalClock { - override def toString: String = x - } - def unknown: LogicalClock = apply("unknown") -} diff --git a/ivy/src/main/scala/sbt/MakePom.scala b/ivy/src/main/scala/sbt/MakePom.scala deleted file mode 100644 index e2018612f..000000000 --- a/ivy/src/main/scala/sbt/MakePom.scala +++ /dev/null @@ -1,434 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ - -// based on Ivy's PomModuleDescriptorWriter, which is Apache Licensed, Version 2.0 -// http://www.apache.org/licenses/LICENSE-2.0 - -package sbt - -import java.io.File - -import sbt.mavenint.PomExtraDependencyAttributes - -// Node needs to be renamed to XNode because the task subproject contains a Node type that will shadow -// scala.xml.Node when generating aggregated API documentation -import scala.xml.{ Elem, Node => XNode, NodeSeq, PrettyPrinter, PrefixedAttribute } -import Configurations.Optional - -import org.apache.ivy.Ivy -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.module.descriptor.{ DependencyArtifactDescriptor, DependencyDescriptor, License, ModuleDescriptor, ExcludeRule } -import org.apache.ivy.plugins.resolver.{ ChainResolver, DependencyResolver, IBiblioResolver } -import ivyint.CustomRemoteMavenResolver -object MakePom { - /** True if the revision is an ivy-range, not a complete revision. */ - def isDependencyVersionRange(revision: String): Boolean = { - (revision endsWith "+") || - (revision contains "[") || - (revision contains "]") || - (revision contains "(") || - (revision contains ")") - } - - /** Converts Ivy revision ranges to that of Maven POM */ - def makeDependencyVersion(revision: String): String = { - def plusRange(s: String, shift: Int = 0) = { - def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1 - val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match { - case Array(revLast, revRest) => - (revRest.reverse + ".", revLast.reverse) - case Array(revLast) => ("", revLast.reverse) - } - val lastVersionInt = lastVersion.toInt - s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt + pow(shift)})" - } - val startSym = Set(']', '[', '(') - val stopSym = Set(']', '[', ')') - val DotPlusPattern = """(.+)\.\+""".r - val DotNumPlusPattern = """(.+)\.(\d+)\+""".r - val NumPlusPattern = """(\d+)\+""".r - val maxDigit = 5 - try { - revision match { - case "+" => "[0,)" - case DotPlusPattern(base) => plusRange(base) - // This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so - // we assume version ranges never go beyond 5 siginificant digits. - case NumPlusPattern(tail) => (0 until maxDigit).map(plusRange(tail, _)).mkString(",") - case DotNumPlusPattern(base, tail) => (0 until maxDigit).map(plusRange(base + "." + tail, _)).mkString(",") - case rev if rev endsWith "+" => sys.error(s"dynamic revision '$rev' cannot be translated to POM") - case rev if startSym(rev(0)) && stopSym(rev(rev.length - 1)) => - val start = rev(0) - val stop = rev(rev.length - 1) - val mid = rev.substring(1, rev.length - 1) - (if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop) - case _ => revision - } - } catch { - case e: NumberFormatException => - // TODO - if the version doesn't meet our expectations, maybe we just issue a hard - // error instead of softly ignoring the attempt to rewrite. - //sys.error(s"Could not fix version [$revision] into maven style version") - revision - } - } -} -class MakePom(val log: Logger) { - import MakePom._ - @deprecated("Use `write(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, XNode => XNode, MavenRepository => Boolean, Boolean, File)` instead", "0.11.2") - def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit = - write(ivy, module, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], Set(Artifact.DefaultType), extra, process, filterRepositories, allRepositories, output) - def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit = - write(process(toPom(ivy, module, moduleInfo, configurations, includeTypes, extra, filterRepositories, allRepositories)), output) - // use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n - def write(node: XNode, output: File): Unit = write(toString(node), output, "\n") - def write(xmlString: String, output: File, newline: String): Unit = - IO.write(output, "" + newline + xmlString) - - def toString(node: XNode): String = new PrettyPrinter(1000, 4).format(node) - @deprecated("Use `toPom(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, MavenRepository => Boolean, Boolean)` instead", "0.11.2") - def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode = - toPom(ivy, module, moduleInfo, configurations, Set(Artifact.DefaultType), extra, filterRepositories, allRepositories) - def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode = - ( - 4.0.0 - { makeModuleID(module) } - { moduleInfo.nameFormal } - { makeStartYear(moduleInfo) } - { makeOrganization(moduleInfo) } - { makeScmInfo(moduleInfo) } - { makeDeveloperInfo(moduleInfo) } - { extra } - { - val deps = depsInConfs(module, configurations) - makeProperties(module, deps) ++ - makeDependencies(deps, includeTypes, module.getAllExcludeRules) - } - { makeRepositories(ivy.getSettings, allRepositories, filterRepositories) } - ) - - def makeModuleID(module: ModuleDescriptor): NodeSeq = - { - val mrid = moduleDescriptor(module) - val a: NodeSeq = - ({ mrid.getOrganisation } - { mrid.getName } - { packaging(module) }) - val b: NodeSeq = - ((description(module.getDescription) ++ - homePage(module.getHomePage) ++ - revision(mrid.getRevision) ++ - licenses(module.getLicenses)): NodeSeq) - a ++ b - } - - def makeStartYear(moduleInfo: ModuleInfo): NodeSeq = - moduleInfo.startYear match { - case Some(y) => { y } - case _ => NodeSeq.Empty - } - def makeOrganization(moduleInfo: ModuleInfo): NodeSeq = - { - - { moduleInfo.organizationName } - { - moduleInfo.organizationHomepage match { - case Some(h)=> { h } - case _ => NodeSeq.Empty - } - } - - } - def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq = - { - moduleInfo.scmInfo match { - case Some(s) => - - { s.browseUrl } - { s.connection } - { - s.devConnection match { - case Some(d)=> { d } - case _=> NodeSeq.Empty - } - } - - case _ => NodeSeq.Empty - } - } - def makeDeveloperInfo(moduleInfo: ModuleInfo): NodeSeq = - { - if (moduleInfo.developers.nonEmpty) { - - { - moduleInfo.developers.map { developer: Developer => - - { developer.id } - { developer.name } - { developer.email } - { developer.url } - - } - } - - } else NodeSeq.Empty - } - def makeProperties(module: ModuleDescriptor, dependencies: Seq[DependencyDescriptor]): NodeSeq = - { - val extra = IvySbt.getExtraAttributes(module) - val depExtra = PomExtraDependencyAttributes.writeDependencyExtra(dependencies).mkString("\n") - val allExtra = if (depExtra.isEmpty) extra else extra.updated(PomExtraDependencyAttributes.ExtraAttributesKey, depExtra) - if (allExtra.isEmpty) NodeSeq.Empty else makeProperties(allExtra) - } - def makeProperties(extra: Map[String, String]): NodeSeq = { - def _extraAttributes(k: String) = if (k == PomExtraDependencyAttributes.ExtraAttributesKey) xmlSpacePreserve else scala.xml.Null - { - for ((key, value) <- extra) yield ({ value }).copy(label = key, attributes = _extraAttributes(key)) - } - } - - /** - * Attribute tag that PrettyPrinter won't ignore, saying "don't mess with my spaces" - * Without this, PrettyPrinter will flatten multiple entries for ExtraDependencyAttributes and make them - * unparseable. (e.g. a plugin that depends on multiple plugins will fail) - */ - def xmlSpacePreserve = new PrefixedAttribute("xml", "space", "preserve", scala.xml.Null) - - def description(d: String) = if ((d eq null) || d.isEmpty) NodeSeq.Empty else { d } - def licenses(ls: Array[License]) = if (ls == null || ls.isEmpty) NodeSeq.Empty else { ls.map(license) } - def license(l: License) = - - { l.getName } - { l.getUrl } - repo - - def homePage(homePage: String) = if (homePage eq null) NodeSeq.Empty else { homePage } - def revision(version: String) = if (version ne null) { version } else NodeSeq.Empty - def packaging(module: ModuleDescriptor) = - module.getAllArtifacts match { - case Array() => "pom" - case Array(x) => x.getType - case xs => - val types = xs.map(_.getType).toList.filterNot(IgnoreTypes) - types match { - case Nil => Artifact.PomType - case xs if xs.contains(Artifact.DefaultType) => Artifact.DefaultType - case x :: xs => x - } - } - val IgnoreTypes: Set[String] = Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType) - - @deprecated("Use `makeDependencies` variant which takes excludes", "0.13.9") - def makeDependencies(dependencies: Seq[DependencyDescriptor], includeTypes: Set[String]): NodeSeq = - makeDependencies(dependencies, includeTypes, Nil) - - def makeDependencies(dependencies: Seq[DependencyDescriptor], includeTypes: Set[String], excludes: Seq[ExcludeRule]): NodeSeq = - if (dependencies.isEmpty) - NodeSeq.Empty - else - - { dependencies.map(makeDependency(_, includeTypes, excludes)) } - - - @deprecated("Use `makeDependency` variant which takes excludes", "0.13.9") - def makeDependency(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq = - makeDependency(dependency, includeTypes, Nil) - - def makeDependency(dependency: DependencyDescriptor, includeTypes: Set[String], excludes: Seq[ExcludeRule]): NodeSeq = { - def warnIntransitve(): Unit = - if (!dependency.isTransitive) - log.warn( - s"""Translating intransitive dependency (${dependency.getDependencyId}) into pom.xml, but maven does not support intransitive dependencies. - | Please use exclusions instead so transitive dependencies will be correctly excluded in dependent projects. - """.stripMargin) - else () - val artifacts = dependency.getAllDependencyArtifacts - val includeArtifacts = artifacts.filter(d => includeTypes(d.getType)) - if (artifacts.isEmpty) { - val configs = dependency.getModuleConfigurations - if (!configs.forall(Set("sources", "docs"))) { - warnIntransitve() - val (scope, optional) = getScopeAndOptional(dependency.getModuleConfigurations) - makeDependencyElem(dependency, scope, optional, None, None, excludes) - } else NodeSeq.Empty - } else if (includeArtifacts.isEmpty) { - NodeSeq.Empty - } else { - warnIntransitve() - NodeSeq.fromSeq(artifacts.flatMap(a => makeDependencyElem(dependency, a, excludes))) - } - } - - @deprecated("Use `makeDependencyElem` variant which takes excludes", "0.13.9") - def makeDependencyElem(dependency: DependencyDescriptor, artifact: DependencyArtifactDescriptor): Option[Elem] = - makeDependencyElem(dependency, artifact, Nil) - - def makeDependencyElem(dependency: DependencyDescriptor, artifact: DependencyArtifactDescriptor, excludes: Seq[ExcludeRule]): Option[Elem] = - { - val configs = artifact.getConfigurations.toList match { - case Nil | "*" :: Nil => dependency.getModuleConfigurations - case x => x.toArray - } - if (!configs.forall(Set("sources", "docs"))) { - val (scope, optional) = getScopeAndOptional(configs) - val classifier = artifactClassifier(artifact) - val baseType = artifactType(artifact) - val tpe = (classifier, baseType) match { - case (Some(c), Some(tpe)) if Artifact.classifierType(c) == tpe => None - case _ => baseType - } - Some(makeDependencyElem(dependency, scope, optional, classifier, tpe, excludes)) - } else None - } - - @deprecated("Use `makeDependencyElem` variant which takes excludes", "0.13.9") - def makeDependencyElem(dependency: DependencyDescriptor, scope: Option[String], optional: Boolean, classifier: Option[String], tpe: Option[String]): Elem = - makeDependencyElem(dependency, scope, optional, classifier, tpe, Nil) - - def makeDependencyElem(dependency: DependencyDescriptor, scope: Option[String], optional: Boolean, classifier: Option[String], tpe: Option[String], excludes: Seq[ExcludeRule]): Elem = - { - val mrid = dependency.getDependencyRevisionId - - { mrid.getOrganisation } - { mrid.getName } - { makeDependencyVersion(mrid.getRevision) } - { scopeElem(scope) } - { optionalElem(optional) } - { classifierElem(classifier) } - { typeElem(tpe) } - { exclusions(dependency, excludes) } - - } - - @deprecated("No longer used and will be removed.", "0.12.1") - def classifier(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq = - { - val jarDep = dependency.getAllDependencyArtifacts.find(d => includeTypes(d.getType)) - jarDep match { - case Some(a) => classifierElem(artifactClassifier(a)) - case None => NodeSeq.Empty - } - } - def artifactType(artifact: DependencyArtifactDescriptor): Option[String] = - Option(artifact.getType).flatMap { tpe => if (tpe == "jar") None else Some(tpe) } - def typeElem(tpe: Option[String]): NodeSeq = - tpe match { - case Some(t) => { t } - case None => NodeSeq.Empty - } - - def artifactClassifier(artifact: DependencyArtifactDescriptor): Option[String] = - Option(artifact.getExtraAttribute("classifier")) - def classifierElem(classifier: Option[String]): NodeSeq = - classifier match { - case Some(c) => { c } - case None => NodeSeq.Empty - } - - @deprecated("No longer used and will be removed.", "0.12.1") - def scopeAndOptional(dependency: DependencyDescriptor): NodeSeq = - { - val (scope, opt) = getScopeAndOptional(dependency.getModuleConfigurations) - scopeElem(scope) ++ optionalElem(opt) - } - def scopeElem(scope: Option[String]): NodeSeq = scope match { - case None | Some(Configurations.Compile.name) => NodeSeq.Empty - case Some(s) => { s } - } - def optionalElem(opt: Boolean) = if (opt) true else NodeSeq.Empty - def moduleDescriptor(module: ModuleDescriptor) = module.getModuleRevisionId - - def getScopeAndOptional(confs: Array[String]): (Option[String], Boolean) = - { - val (opt, notOptional) = confs.partition(_ == Optional.name) - val defaultNotOptional = Configurations.defaultMavenConfigurations.find(notOptional contains _.name) - val scope = defaultNotOptional.map(_.name) - (scope, opt.nonEmpty) - } - - @deprecated("Use `exclusions` variant which takes excludes", "0.13.9") - def exclusions(dependency: DependencyDescriptor): NodeSeq = exclusions(dependency, Nil) - - def exclusions(dependency: DependencyDescriptor, excludes: Seq[ExcludeRule]): NodeSeq = - { - val excl = dependency.getExcludeRules(dependency.getModuleConfigurations) ++ excludes - val (warns, excls) = IvyUtil.separate(excl.map(makeExclusion)) - if (warns.nonEmpty) log.warn(warns.mkString(IO.Newline)) - if (excls.nonEmpty) { excls } - else NodeSeq.Empty - } - def makeExclusion(exclRule: ExcludeRule): Either[String, NodeSeq] = - { - val m = exclRule.getId.getModuleId - val (g, a) = (m.getOrganisation, m.getName) - if (g == null || g.isEmpty || g == "*" || a.isEmpty || a == "*") - Left("Skipped generating '' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema.".format(m)) - else - Right( - - { g } - { a } - - ) - } - - def makeRepositories(settings: IvySettings, includeAll: Boolean, filterRepositories: MavenRepository => Boolean) = - { - class MavenRepo(name: String, snapshots: Boolean, releases: Boolean) - val repositories = if (includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver) - val mavenRepositories = - repositories.flatMap { - // TODO - Would it be ok if bintray were in the pom? We should avoid it for now. - case m: CustomRemoteMavenResolver if m.repo.root == JCenterRepository.root => Nil - case m: IBiblioResolver if m.isM2compatible && m.getRoot == JCenterRepository.root => Nil - case m: CustomRemoteMavenResolver if m.repo.root != DefaultMavenRepository.root => - MavenRepository(m.repo.name, m.repo.root) :: Nil - case m: IBiblioResolver if m.isM2compatible && m.getRoot != DefaultMavenRepository.root => - MavenRepository(m.getName, m.getRoot) :: Nil - case _ => Nil - } - val repositoryElements = mavenRepositories.filter(filterRepositories).map(mavenRepository) - if (repositoryElements.isEmpty) repositoryElements else { repositoryElements } - } - def allResolvers(settings: IvySettings): Seq[DependencyResolver] = flatten(castResolvers(settings.getResolvers)).distinct - def flatten(rs: Seq[DependencyResolver]): Seq[DependencyResolver] = if (rs eq null) Nil else rs.flatMap(resolvers) - def resolvers(r: DependencyResolver): Seq[DependencyResolver] = - r match { case c: ChainResolver => flatten(castResolvers(c.getResolvers)); case _ => r :: Nil } - - // cast the contents of a pre-generics collection - private def castResolvers(s: java.util.Collection[_]): Seq[DependencyResolver] = - s.toArray.map(_.asInstanceOf[DependencyResolver]) - - def toID(name: String) = checkID(name.filter(isValidIDCharacter).mkString, name) - def isValidIDCharacter(c: Char) = c.isLetterOrDigit - private def checkID(id: String, name: String) = if (id.isEmpty) sys.error("Could not convert '" + name + "' to an ID") else id - def mavenRepository(repo: MavenRepository): XNode = - mavenRepository(toID(repo.name), repo.name, repo.root) - def mavenRepository(id: String, name: String, root: String): XNode = - - { id } - { name } - { root } - { if (name == JavaNet1Repository.name) "legacy" else "default" } - - - /** - * Retain dependencies only with the configurations given, or all public configurations of `module` if `configurations` is None. - * This currently only preserves the information required by makePom - */ - private def depsInConfs(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]): Seq[DependencyDescriptor] = - { - val keepConfigurations = IvySbt.getConfigurations(module, configurations) - val keepSet = Set(keepConfigurations.toSeq: _*) - def translate(dependency: DependencyDescriptor) = - { - val keep = dependency.getModuleConfigurations.filter(keepSet.contains) - if (keep.isEmpty) - None - else // TODO: translate the dependency to contain only configurations to keep - Some(dependency) - } - module.getDependencies flatMap translate - } -} diff --git a/ivy/src/main/scala/sbt/ModuleID.scala b/ivy/src/main/scala/sbt/ModuleID.scala deleted file mode 100644 index 10647dcad..000000000 --- a/ivy/src/main/scala/sbt/ModuleID.scala +++ /dev/null @@ -1,128 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.net.URL - -import sbt.mavenint.SbtPomExtraProperties -import sbt.serialization._ - -final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) { - override def toString: String = - organization + ":" + name + ":" + revision + - (configurations match { case Some(s) => ":" + s; case None => "" }) + - (if (extraAttributes.isEmpty) "" else " " + extraString) - - /** String representation of the extra attributes, excluding any information only attributes. */ - def extraString: String = extraDependencyAttributes.map { case (k, v) => k + "=" + v } mkString ("(", ", ", ")") - - /** Returns the extra attributes except for ones marked as information only (ones that typically would not be used for dependency resolution). */ - def extraDependencyAttributes: Map[String, String] = extraAttributes.filterKeys(!_.startsWith(SbtPomExtraProperties.POM_INFO_KEY_PREFIX)) - - @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") - def cross(v: Boolean): ModuleID = cross(if (v) CrossVersion.binary else CrossVersion.Disabled) - - @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") - def cross(v: Boolean, verRemap: String => String): ModuleID = cross(if (v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled) - - /** Specifies the cross-version behavior for this module. See [CrossVersion] for details.*/ - def cross(v: CrossVersion): ModuleID = copy(crossVersion = v) - - // () required for chaining - /** Do not follow dependencies of this module. Synonym for `intransitive`.*/ - def notTransitive() = intransitive() - - /** Do not follow dependencies of this module. Synonym for `notTransitive`.*/ - def intransitive() = copy(isTransitive = false) - - /** - * Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed, - * redownload it. sbt configures all -SNAPSHOT dependencies to be changing. - * - * See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details. - */ - def changing() = copy(isChanging = true) - - /** - * Indicates that conflict resolution should only select this module's revision. - * This prevents a newer revision from being pulled in by a transitive dependency, for example. - */ - def force() = copy(isForce = true) - - /** - * Specifies a URL from which the main artifact for this dependency can be downloaded. - * This value is only consulted if the module is not found in a repository. - * It is not included in published metadata. - */ - def from(url: String) = artifacts(Artifact(name, new URL(url))) - - /** Adds a dependency on the artifact for this module with classifier `c`. */ - def classifier(c: String) = artifacts(Artifact(name, c)) - - /** - * Declares the explicit artifacts for this module. If this ModuleID represents a dependency, - * these artifact definitions override the information in the dependency's published metadata. - */ - def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts) - - /** - * Applies the provided exclusions to dependencies of this module. Note that only exclusions that specify - * both the exact organization and name and nothing else will be included in a pom.xml. - */ - def excludeAll(rules: ExclusionRule*) = copy(exclusions = this.exclusions ++ rules) - - /** Excludes the dependency with organization `org` and `name` from being introduced by this dependency during resolution. */ - def exclude(org: String, name: String) = excludeAll(ExclusionRule(org, name)) - - /** - * Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed. - * This information will only be published in an ivy.xml and not in a pom.xml. - */ - def extra(attributes: (String, String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes)) - - /** - * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" - * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead. - */ - def sources() = artifacts(Artifact.sources(name)) - - /** - * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" - * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead. - */ - def javadoc() = artifacts(Artifact.javadoc(name)) - - def pomOnly() = artifacts(Artifact.pom(name)) - - /** - * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" - * classifier. If there is not already an explicit dependency on the main artifact, this adds one. - */ - def withSources() = jarIfEmpty.sources() - - /** - * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" - * classifier. If there is not already an explicit dependency on the main artifact, this adds one. - */ - def withJavadoc() = jarIfEmpty.javadoc() - - private def jarIfEmpty = if (explicitArtifacts.isEmpty) jar() else this - - /** - * Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such - * as when adding a dependency on an artifact with a classifier. - */ - def jar() = artifacts(Artifact(name)) -} -object ModuleID { - implicit val pickler: Pickler[ModuleID] with Unpickler[ModuleID] = PicklerUnpickler.generate[ModuleID] - - /** Prefixes all keys with `e:` if they are not already so prefixed. */ - def checkE(attributes: Seq[(String, String)]) = - for ((key, value) <- attributes) yield if (key.startsWith("e:")) (key, value) else ("e:" + key, value) -} diff --git a/ivy/src/main/scala/sbt/ProjectResolver.scala b/ivy/src/main/scala/sbt/ProjectResolver.scala deleted file mode 100644 index 61c811e7b..000000000 --- a/ivy/src/main/scala/sbt/ProjectResolver.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -import java.io.File -import java.util.Date - -import org.apache.ivy.core.{ cache, module, report, resolve, search } -import cache.{ ArtifactOrigin, RepositoryCacheManager } -import search.{ ModuleEntry, OrganisationEntry, RevisionEntry } -import module.id.ModuleRevisionId -import module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DependencyDescriptor, ModuleDescriptor } -import org.apache.ivy.plugins.namespace.Namespace -import org.apache.ivy.plugins.resolver.{ DependencyResolver, ResolverSettings } -import report.{ ArtifactDownloadReport, DownloadReport, DownloadStatus, MetadataArtifactDownloadReport } -import resolve.{ DownloadOptions, ResolveData, ResolvedModuleRevision } - -/** - * A Resolver that uses a predefined mapping from module ids to in-memory descriptors. - * It does not handle artifacts. - */ -class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]) extends ResolverAdapter { - def getName = name - def setName(name: String) = sys.error("Setting name not supported by ProjectResolver") - override def toString = "ProjectResolver(" + name + ", mapped: " + map.keys.mkString(", ") + ")" - - def getDependency(dd: DependencyDescriptor, data: ResolveData): ResolvedModuleRevision = - getDependency(dd.getDependencyRevisionId).orNull - - private[this] def getDependency(revisionId: ModuleRevisionId): Option[ResolvedModuleRevision] = - { - def constructResult(descriptor: ModuleDescriptor) = new ResolvedModuleRevision(this, this, descriptor, report(revisionId), true) - map get revisionId map constructResult - } - - private[sbt] def getModuleDescriptor(revisionId: ModuleRevisionId): Option[ModuleDescriptor] = map.get(revisionId) - - def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport = - { - val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date) - val r = new MetadataArtifactDownloadReport(artifact) - r.setSearched(false) - r.setDownloadStatus(DownloadStatus.FAILED) - r - } - - // this resolver nevers locates artifacts, only resolves dependencies - def exists(artifact: IArtifact) = false - def locate(artifact: IArtifact) = null - def download(artifacts: Array[IArtifact], options: DownloadOptions): DownloadReport = - { - val r = new DownloadReport - for (artifact <- artifacts) - if (getDependency(artifact.getModuleRevisionId).isEmpty) - r.addArtifactReport(notDownloaded(artifact)) - r - } - - def download(artifact: ArtifactOrigin, options: DownloadOptions): ArtifactDownloadReport = - notDownloaded(artifact.getArtifact) - def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData) = null - - def notDownloaded(artifact: IArtifact): ArtifactDownloadReport = - { - val r = new ArtifactDownloadReport(artifact) - r.setDownloadStatus(DownloadStatus.FAILED) - r - } - - // doesn't support publishing - def publish(artifact: IArtifact, src: File, overwrite: Boolean) = sys.error("Publish not supported by ProjectResolver") - def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean): Unit = () - def abortPublishTransaction(): Unit = () - def commitPublishTransaction(): Unit = () - - def reportFailure(): Unit = () - def reportFailure(art: IArtifact): Unit = () - - def listOrganisations() = new Array[OrganisationEntry](0) - def listModules(org: OrganisationEntry) = new Array[ModuleEntry](0) - def listRevisions(module: ModuleEntry) = new Array[RevisionEntry](0) - - def getNamespace = Namespace.SYSTEM_NAMESPACE - - private[this] var settings: Option[ResolverSettings] = None - - def dumpSettings(): Unit = () - def setSettings(settings: ResolverSettings): Unit = { this.settings = Some(settings) } - def getRepositoryCacheManager = settings match { case Some(s) => s.getDefaultRepositoryCacheManager; case None => sys.error("No settings defined for ProjectResolver") } -} - -object ProjectResolver { - private[sbt] val InterProject = "inter-project" -} diff --git a/ivy/src/main/scala/sbt/ResolutionCache.scala b/ivy/src/main/scala/sbt/ResolutionCache.scala deleted file mode 100644 index a68c52417..000000000 --- a/ivy/src/main/scala/sbt/ResolutionCache.scala +++ /dev/null @@ -1,90 +0,0 @@ -package sbt - -import java.io.File -import java.io.FileInputStream -import java.util.Properties -import org.apache.ivy.core -import org.apache.ivy.plugins.parser -import core.IvyPatternHelper -import core.settings.IvySettings -import core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, DefaultResolutionCacheManager, ResolutionCacheManager } -import core.module.id.ModuleRevisionId -import core.module.descriptor.ModuleDescriptor -import ResolutionCache.{ Name, ReportDirectory, ResolvedName, ResolvedPattern } -import parser.xml.XmlModuleDescriptorParser - -/** - * Replaces the standard Ivy resolution cache in order to: - * 1. Separate cached resolved Ivy files from resolution reports, making the resolution reports easier to find. - * 2. Have them per-project for easier cleaning (possible with standard cache, but central to this custom one). - * 3. Cache location includes extra attributes so that cross builds of a plugin do not overwrite each other. - */ -private[sbt] final class ResolutionCache(base: File, settings: IvySettings) extends ResolutionCacheManager { - private[this] def resolvedFileInCache(m: ModuleRevisionId, name: String, ext: String): File = { - val p = ResolvedPattern - val f = IvyPatternHelper.substitute(p, m.getOrganisation, m.getName, m.getBranch, m.getRevision, name, name, ext, null, null, m.getAttributes, null) - new File(base, f) - } - private[this] val reportBase: File = new File(base, ReportDirectory) - - def getResolutionCacheRoot: File = base - def clean(): Unit = IO.delete(base) - override def toString = Name - - def getResolvedIvyFileInCache(mrid: ModuleRevisionId): File = - resolvedFileInCache(mrid, ResolvedName, "xml") - def getResolvedIvyPropertiesInCache(mrid: ModuleRevisionId): File = - resolvedFileInCache(mrid, ResolvedName, "properties") - // name needs to be the same as Ivy's default because the ivy-report.xsl stylesheet assumes this - // when making links to reports for other configurations - def getConfigurationResolveReportInCache(resolveId: String, conf: String): File = - new File(reportBase, resolveId + "-" + conf + ".xml") - def getConfigurationResolveReportsInCache(resolveId: String): Array[File] = - IO.listFiles(reportBase).filter(_.getName.startsWith(resolveId + "-")) - - // XXX: this method is required by ResolutionCacheManager in Ivy 2.3.0 final, - // but it is apparently unused by Ivy as sbt uses Ivy. Therefore, it is - // unexercised in tests. Note that the implementation of this method in Ivy 2.3.0's - // DefaultResolutionCache also resolves parent properties for a given mrid - def getResolvedModuleDescriptor(mrid: ModuleRevisionId): ModuleDescriptor = { - val ivyFile = getResolvedIvyFileInCache(mrid) - if (!ivyFile.exists()) { - throw new IllegalStateException("Ivy file not found in cache for " + mrid + "!") - } - - XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFile.toURI.toURL, false) - } - - def saveResolvedModuleDescriptor(md: ModuleDescriptor): Unit = { - val mrid = md.getResolvedModuleRevisionId - val cachedIvyFile = getResolvedIvyFileInCache(mrid) - md.toIvyFile(cachedIvyFile) - } -} -private[sbt] object ResolutionCache { - /** - * Removes cached files from the resolution cache for the module with ID `mrid` - * and the resolveId (as set on `ResolveOptions`). - */ - private[sbt] def cleanModule(mrid: ModuleRevisionId, resolveId: String, manager: ResolutionCacheManager): Unit = { - val files = - Option(manager.getResolvedIvyFileInCache(mrid)).toList ::: - Option(manager.getResolvedIvyPropertiesInCache(mrid)).toList ::: - Option(manager.getConfigurationResolveReportsInCache(resolveId)).toList.flatten - IO.delete(files) - } - - private val ReportDirectory = "reports" - - // name of the file providing a dependency resolution report for a configuration - private val ReportFileName = "report.xml" - - // base name (name except for extension) of resolution report file - private val ResolvedName = "resolved.xml" - - // Cache name - private val Name = "sbt-resolution-cache" - - // use sbt-specific extra attributes so that resolved xml files do not get overwritten when using different Scala/sbt versions - private val ResolvedPattern = "[organisation]/[module]/" + Resolver.PluginPattern + "[revision]/[artifact].[ext]" -} diff --git a/ivy/src/main/scala/sbt/Resolver.scala b/ivy/src/main/scala/sbt/Resolver.scala deleted file mode 100644 index ab667e923..000000000 --- a/ivy/src/main/scala/sbt/Resolver.scala +++ /dev/null @@ -1,369 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.{ IOException, File } -import java.net.URL -import scala.xml.{ Text, NodeSeq, Elem, XML } -import org.apache.ivy.plugins.resolver.DependencyResolver -import org.xml.sax.SAXParseException - -sealed trait Resolver { - def name: String -} -final class RawRepository(val resolver: DependencyResolver) extends Resolver { - def name = resolver.getName - override def toString = "Raw(" + resolver.toString + ")" - - override def equals(o: Any): Boolean = o match { - case o: RawRepository => - this.name == o.name - case _ => false - } - - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.name.## - hash - } -} -sealed case class ChainedResolver(name: String, resolvers: Seq[Resolver]) extends Resolver - -/** An instance of a remote maven repository. Note: This will use Aether/Maven to resolve artifacts. */ -sealed case class MavenRepository(name: String, root: String) extends Resolver { - override def toString = s"$name: $root" - def isCache: Boolean = false - def localIfFile: Boolean = true - def withLocalIfFile(value: Boolean) = new MavenRepository(name, root) { override def localIfFile = value } -} - -/** - * An instance of maven CACHE directory. You cannot treat a cache directory the same as a a remote repository because - * the metadata is different (see Aether ML discussion). - */ -final class MavenCache(name: String, val rootFile: File) extends MavenRepository(name, rootFile.toURI.toURL.toString) { - override val toString = s"cache:$name: ${rootFile.getAbsolutePath}" - override def isCache: Boolean = true -} -object MavenCache { - def apply(name: String, rootFile: File): MavenCache = new MavenCache(name, rootFile) -} - -final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean, val descriptorOptional: Boolean, val skipConsistencyCheck: Boolean) { - private[sbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true) - private[sbt] def withDescriptorOptional(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, true, skipConsistencyCheck) - private[sbt] def withoutConsistencyCheck(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, true) - private[sbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible) - private[sbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible) - override def toString = "Patterns(ivyPatterns=%s, artifactPatterns=%s, isMavenCompatible=%s, descriptorOptional=%s, skipConsistencyCheck=%s)".format(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck) - override def equals(obj: Any): Boolean = { - obj match { - case other: Patterns => - ivyPatterns == other.ivyPatterns && artifactPatterns == other.artifactPatterns && isMavenCompatible == other.isMavenCompatible && descriptorOptional == other.descriptorOptional && skipConsistencyCheck == other.skipConsistencyCheck - case _ => false - } - } - override def hashCode: Int = (ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck).hashCode - - @deprecated - def this(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean) = this(ivyPatterns, artifactPatterns, isMavenCompatible, false, false) -} -object Patterns { - implicit def defaultPatterns: Patterns = Resolver.defaultPatterns - - def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns: _*) - def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(artifactPatterns, artifactPatterns, isMavenCompatible) - def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, false, false) - def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, descriptorOptional: Boolean, skipConsistencyCheck: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck) -} -object RepositoryHelpers { - final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) { - def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port) - } - /** Configuration specific to an Ivy filesystem resolver. */ - final case class FileConfiguration(isLocal: Boolean, isTransactional: Option[Boolean]) { - def transactional() = FileConfiguration(isLocal, Some(true)) - def nontransactional() = FileConfiguration(isLocal, Some(false)) - def nonlocal() = FileConfiguration(false, isTransactional) - } - sealed trait SshAuthentication extends NotNull - final case class PasswordAuthentication(user: String, password: Option[String]) extends SshAuthentication - final case class KeyFileAuthentication(user: String, keyfile: File, password: Option[String]) extends SshAuthentication -} -import RepositoryHelpers.{ SshConnection, FileConfiguration } -import RepositoryHelpers.{ KeyFileAuthentication, PasswordAuthentication, SshAuthentication } - -/** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/ -sealed abstract class PatternsBasedRepository extends Resolver { - type RepositoryType <: PatternsBasedRepository - /** Should be implemented to create a new copy of this repository but with `patterns` as given.*/ - protected def copy(patterns: Patterns): RepositoryType - - /** The object representing the configured patterns for this repository. */ - def patterns: Patterns - - /** Enables maven 2 compatibility for this repository. */ - def mavenStyle() = copy(patterns.mavenStyle()) - - /** Makes descriptor metadata optional for this repository. */ - def descriptorOptional() = copy(patterns.withDescriptorOptional()) - - /** Disables consistency checking for this repository. */ - def skipConsistencyCheck() = copy(patterns.withoutConsistencyCheck()) - - /** Adds the given patterns for resolving/publishing Ivy files.*/ - def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns)) - /** Adds the given patterns for resolving/publishing artifacts.*/ - def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns)) -} -/** sbt interface for an Ivy filesystem repository. More convenient construction is done using Resolver.file. */ -final case class FileRepository(name: String, configuration: FileConfiguration, patterns: Patterns) extends PatternsBasedRepository { - type RepositoryType = FileRepository - protected def copy(patterns: Patterns): FileRepository = FileRepository(name, configuration, patterns) - private def copy(configuration: FileConfiguration) = FileRepository(name, configuration, patterns) - def transactional() = copy(configuration.transactional()) - def nonlocal() = copy(configuration.nonlocal()) -} -final case class URLRepository(name: String, patterns: Patterns) extends PatternsBasedRepository { - type RepositoryType = URLRepository - protected def copy(patterns: Patterns): URLRepository = URLRepository(name, patterns) -} -/** sbt interface for an Ivy ssh-based repository (ssh and sftp). Requires the Jsch library.. */ -sealed abstract class SshBasedRepository extends PatternsBasedRepository { - type RepositoryType <: SshBasedRepository - protected def copy(connection: SshConnection): RepositoryType - private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication))) - - /** The object representing the configured ssh connection for this repository. */ - def connection: SshConnection - - /** Configures this to use the specified user name and password when connecting to the remote repository. */ - def as(user: String, password: String): RepositoryType = as(user, Some(password)) - def as(user: String): RepositoryType = as(user, None) - def as(user: String, password: Option[String]) = copy(new PasswordAuthentication(user, password)) - /** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */ - def as(user: String, keyfile: File): RepositoryType = as(user, keyfile, None) - def as(user: String, keyfile: File, password: String): RepositoryType = as(user, keyfile, Some(password)) - def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(new KeyFileAuthentication(user, keyfile, password)) -} -/** sbt interface for an Ivy repository over ssh. More convenient construction is done using Resolver.ssh. */ -final case class SshRepository(name: String, connection: SshConnection, patterns: Patterns, publishPermissions: Option[String]) extends SshBasedRepository { - type RepositoryType = SshRepository - protected def copy(patterns: Patterns): SshRepository = SshRepository(name, connection, patterns, publishPermissions) - protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions) - /** Defines the permissions to set when publishing to this repository. */ - def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions)) - def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions) -} -/** sbt interface for an Ivy repository over sftp. More convenient construction is done using Resolver.sftp. */ -final case class SftpRepository(name: String, connection: SshConnection, patterns: Patterns) extends SshBasedRepository { - type RepositoryType = SftpRepository - protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns) - protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns) -} - -import Resolver._ - -object DefaultMavenRepository extends MavenRepository("public", centralRepositoryRoot(useSecureResolvers)) -object JavaNet2Repository extends MavenRepository(JavaNet2RepositoryName, JavaNet2RepositoryRoot) -object JCenterRepository extends MavenRepository(JCenterRepositoryName, JCenterRepositoryRoot) -@deprecated("HTTP repository is no longer recommended.", "0.13.6") -object JavaNet1Repository extends JavaNet1Repository -@deprecated("HTTP repository is no longer recommended.", "0.13.6") -sealed trait JavaNet1Repository extends Resolver { - def name = "java.net Maven1 Repository" -} - -object Resolver { - private[sbt] def useSecureResolvers = sys.props.get("sbt.repository.secure") map { _.toLowerCase == "true" } getOrElse true - - val TypesafeRepositoryRoot = typesafeRepositoryRoot(useSecureResolvers) - val SbtPluginRepositoryRoot = sbtPluginRepositoryRoot(useSecureResolvers) - val SonatypeRepositoryRoot = "https://oss.sonatype.org/content/repositories" - val JavaNet2RepositoryName = "java.net Maven2 Repository" - val JavaNet2RepositoryRoot = javanet2RepositoryRoot(useSecureResolvers) - val JCenterRepositoryName = "jcenter" - val JCenterRepositoryRoot = "https://jcenter.bintray.com/" - val DefaultMavenRepositoryRoot = "https://repo1.maven.org/maven2/" - // TODO: This switch is only kept for backward compatibility. Hardcode to HTTPS in the future. - private[sbt] def centralRepositoryRoot(secure: Boolean) = (if (secure) "https" else "http") + "://repo1.maven.org/maven2/" - // TODO: This switch is only kept for backward compatibility. Hardcode to HTTPS in the future. - private[sbt] def javanet2RepositoryRoot(secure: Boolean) = - if (secure) "https://maven.java.net/content/repositories/public/" - else "http://download.java.net/maven/2" - // TODO: This switch is only kept for backward compatibility. Hardcode to HTTPS in the future. - private[sbt] def typesafeRepositoryRoot(secure: Boolean) = (if (secure) "https" else "http") + "://repo.typesafe.com/typesafe" - // TODO: This switch is only kept for backward compatibility. Hardcode to HTTPS in the future. - private[sbt] def sbtPluginRepositoryRoot(secure: Boolean) = (if (secure) "https" else "http") + "://repo.scala-sbt.org/scalasbt" - - // obsolete: kept only for launcher compatibility - private[sbt] val ScalaToolsReleasesName = "Sonatype OSS Releases" - private[sbt] val ScalaToolsSnapshotsName = "Sonatype OSS Snapshots" - private[sbt] val ScalaToolsReleasesRoot = SonatypeRepositoryRoot + "/releases" - private[sbt] val ScalaToolsSnapshotsRoot = SonatypeRepositoryRoot + "/snapshots" - private[sbt] val ScalaToolsReleases = new MavenRepository(ScalaToolsReleasesName, ScalaToolsReleasesRoot) - private[sbt] val ScalaToolsSnapshots = new MavenRepository(ScalaToolsSnapshotsName, ScalaToolsSnapshotsRoot) - - def typesafeRepo(status: String) = new MavenRepository("typesafe-" + status, TypesafeRepositoryRoot + "/" + status) - def typesafeIvyRepo(status: String) = url("typesafe-ivy-" + status, new URL(TypesafeRepositoryRoot + "/ivy-" + status + "/"))(ivyStylePatterns) - def sbtPluginRepo(status: String) = url("sbt-plugin-" + status, new URL(SbtPluginRepositoryRoot + "/sbt-plugin-" + status + "/"))(ivyStylePatterns) - def sonatypeRepo(status: String) = new MavenRepository("sonatype-" + status, SonatypeRepositoryRoot + "/" + status) - def bintrayRepo(owner: String, repo: String) = new MavenRepository(s"bintray-$owner-$repo", s"https://dl.bintray.com/$owner/$repo/") - def jcenterRepo = JCenterRepository - - /** Add the local and Maven Central repositories to the user repositories. */ - def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] = - withDefaultResolvers(userResolvers, true) - /** - * Add the local Ivy repository to the user repositories. - * If `mavenCentral` is true, add the Maven Central repository. - */ - def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean): Seq[Resolver] = - Seq(Resolver.defaultLocal) ++ - userResolvers ++ - single(DefaultMavenRepository, mavenCentral) - private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if (nonEmpty) Seq(value) else Nil - - /** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */ - sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull { - /** Subclasses should implement this method to */ - protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType - /** - * Constructs this repository type with the given `name`. `basePatterns` are the initial patterns to use. A ManagedProject - * has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, None, None, None) - /** - * Constructs this repository type with the given `name` and `hostname`. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), None, None) - /** - * Constructs this repository type with the given `name`, `hostname`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), None, Some(basePath)) - /** - * Constructs this repository type with the given `name`, `hostname`, and `port`. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), Some(port), None) - /** - * Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), Some(port), Some(basePath)) - /** - * Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. All but the `name` are optional (use None). - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns. - */ - def apply(name: String, hostname: Option[String], port: Option[Int], basePath: Option[String])(implicit basePatterns: Patterns): RepositoryType = - construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns)) - } - /** A factory to construct an interface to an Ivy SSH resolver.*/ - object ssh extends Define[SshRepository] { - protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None) - } - /** A factory to construct an interface to an Ivy SFTP resolver.*/ - object sftp extends Define[SftpRepository] { - protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns) - } - /** A factory to construct an interface to an Ivy filesystem resolver. */ - object file { - /** - * Constructs a file resolver with the given name. The patterns to use must be explicitly specified - * using the `ivys` or `artifacts` methods on the constructed resolver object. - */ - def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false)) - /** Constructs a file resolver with the given name and base directory. */ - def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository = - baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _)) - } - object url { - /** - * Constructs a URL resolver with the given name. The patterns to use must be explicitly specified - * using the `ivys` or `artifacts` methods on the constructed resolver object. - */ - def apply(name: String): URLRepository = URLRepository(name, Patterns(false)) - /** Constructs a file resolver with the given name and base directory. */ - def apply(name: String, baseURL: URL)(implicit basePatterns: Patterns): URLRepository = - baseRepository(baseURL.toURI.normalize.toString)(URLRepository(name, _)) - } - private def baseRepository[T](base: String)(construct: Patterns => T)(implicit basePatterns: Patterns): T = - construct(resolvePatterns(base, basePatterns)) - - /** - * If `base` is None, `patterns` is returned unchanged. - * Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. - */ - private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns = - base match { - case Some(path) => resolvePatterns(path, patterns) - case None => patterns - } - /** Resolves the ivy file and artifact patterns in `patterns` against the given base. */ - private def resolvePatterns(base: String, basePatterns: Patterns): Patterns = - { - def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p)) - Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible, basePatterns.descriptorOptional, basePatterns.skipConsistencyCheck) - } - private[sbt] def resolvePattern(base: String, pattern: String): String = - { - val normBase = base.replace('\\', '/') - if (normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern - } - def defaultFileConfiguration = FileConfiguration(true, None) - def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true) - def ivyStylePatterns = defaultIvyPatterns //Patterns(Nil, Nil, false) - - def defaultPatterns = mavenStylePatterns - def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]" - def localBasePattern = "[organisation]/[module]/" + PluginPattern + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]" - final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" - private[this] def mavenLocalDir: File = { - def loadHomeFromSettings(f: () => File): Option[File] = - try { - val file = f() - if (!file.exists) None - else ((XML.loadFile(file) \ "localRepository").text match { - case "" => None - case e @ _ => Some(new File(e)) - }) - } catch { - // Occurs inside File constructor when property or environment variable does not exist - case _: NullPointerException => None - // Occurs when File does not exist - case _: IOException => None - case e: SAXParseException => System.err.println(s"WARNING: Problem parsing ${f().getAbsolutePath}, ${e.getMessage}"); None - } - loadHomeFromSettings(() => new File(Path.userHome, ".m2/settings.xml")) orElse - loadHomeFromSettings(() => new File(new File(System.getenv("M2_HOME")), "conf/settings.xml")) getOrElse - new File(Path.userHome, ".m2/repository") - } - // TODO - should this just be the *exact* same as mavenLocal? probably... - def publishMavenLocal: MavenCache = new MavenCache("publish-m2-local", mavenLocalDir) - def mavenLocal: MavenRepository = new MavenCache("Maven2 Local", mavenLocalDir) - def defaultLocal = defaultUserFileRepository("local") - def defaultShared = defaultUserFileRepository("shared") - def defaultUserFileRepository(id: String) = - { - val pList = ("${ivy.home}/" + id + "/" + localBasePattern) :: Nil - FileRepository(id, defaultFileConfiguration, Patterns(pList, pList, false)) - } - def defaultIvyPatterns = - { - val pList = List(localBasePattern) - Patterns(pList, pList, false) - } -} diff --git a/ivy/src/main/scala/sbt/SbtExclusionRule.scala b/ivy/src/main/scala/sbt/SbtExclusionRule.scala deleted file mode 100644 index 6e897a16b..000000000 --- a/ivy/src/main/scala/sbt/SbtExclusionRule.scala +++ /dev/null @@ -1,44 +0,0 @@ -package sbt - -import sbt.impl.{ GroupID, GroupArtifactID } - -final class SbtExclusionRule( - val organization: String, - val name: String, - val artifact: String, - val configurations: Seq[String], - val crossVersion: CrossVersion) { - - def copy(organization: String = this.organization, - name: String = this.name, - artifact: String = this.artifact, - configurations: Seq[String] = this.configurations, - crossVersion: CrossVersion = this.crossVersion): SbtExclusionRule = - SbtExclusionRule(organization = organization, - name = name, - artifact = artifact, - configurations = configurations, - crossVersion = crossVersion) -} - -object SbtExclusionRule { - def apply(organization: String): SbtExclusionRule = - new SbtExclusionRule(organization, "*", "*", Nil, CrossVersion.Disabled) - - def apply(organization: String, name: String): SbtExclusionRule = - new SbtExclusionRule(organization, name, "*", Nil, CrossVersion.Disabled) - - def apply(organization: String, - name: String, - artifact: String, - configurations: Seq[String], - crossVersion: CrossVersion): SbtExclusionRule = - new SbtExclusionRule(organization, name, artifact, configurations, crossVersion) - - implicit def groupIdToExclusionRule(organization: GroupID): SbtExclusionRule = - SbtExclusionRule(organization.groupID) - implicit def stringToExclusionRule(organization: String): SbtExclusionRule = - SbtExclusionRule(organization) - implicit def groupArtifactIDToExcludsionRule(gaid: GroupArtifactID): SbtExclusionRule = - SbtExclusionRule(gaid.groupID, gaid.artifactID, "*", Nil, gaid.crossVersion) -} diff --git a/ivy/src/main/scala/sbt/StringUtilities.scala b/ivy/src/main/scala/sbt/StringUtilities.scala deleted file mode 100644 index 40018250e..000000000 --- a/ivy/src/main/scala/sbt/StringUtilities.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.util.Locale - -object StringUtilities { - @deprecated("Different use cases require different normalization. Use Project.normalizeModuleID or normalizeProjectID instead.", "0.13.0") - def normalize(s: String) = s.toLowerCase(Locale.ENGLISH).replaceAll("""\W+""", "-") - def nonEmpty(s: String, label: String): Unit = require(s.trim.length > 0, label + " cannot be empty.") - def appendable(s: String) = if (s.isEmpty) "" else "_" + s -} diff --git a/ivy/src/main/scala/sbt/UpdateOptions.scala b/ivy/src/main/scala/sbt/UpdateOptions.scala deleted file mode 100644 index 3a710026c..000000000 --- a/ivy/src/main/scala/sbt/UpdateOptions.scala +++ /dev/null @@ -1,82 +0,0 @@ -package sbt - -import java.io.File -import org.apache.ivy.plugins.resolver.DependencyResolver -import org.apache.ivy.core.settings.IvySettings - -/** - * Represents configurable options for update task. - * While UpdateConfiguration is passed into update at runtime, - * UpdateOption is intended to be used while setting up the Ivy object. - * - * See also UpdateConfiguration in IvyActions.scala. - */ -final class UpdateOptions private[sbt] ( - /** If set to CircularDependencyLevel.Error, halt the dependency resolution. */ - val circularDependencyLevel: CircularDependencyLevel, - /** If set to true, check all resolvers for snapshots. */ - val latestSnapshots: Boolean, - /** If set to true, use consolidated resolution. */ - val consolidatedResolution: Boolean, - /** If set to true, use cached resolution. */ - val cachedResolution: Boolean, - /** Extention point for an alternative resolver converter. */ - val resolverConverter: UpdateOptions.ResolverConverter) { - def withCircularDependencyLevel(circularDependencyLevel: CircularDependencyLevel): UpdateOptions = - copy(circularDependencyLevel = circularDependencyLevel) - def withLatestSnapshots(latestSnapshots: Boolean): UpdateOptions = - copy(latestSnapshots = latestSnapshots) - @deprecated("Use withCachedResolution instead.", "0.13.7") - def withConsolidatedResolution(consolidatedResolution: Boolean): UpdateOptions = - copy(consolidatedResolution = consolidatedResolution, - cachedResolution = consolidatedResolution) - def withCachedResolution(cachedResoluton: Boolean): UpdateOptions = - copy(cachedResolution = cachedResoluton, - consolidatedResolution = cachedResolution) - /** Extention point for an alternative resolver converter. */ - def withResolverConverter(resolverConverter: UpdateOptions.ResolverConverter): UpdateOptions = - copy(resolverConverter = resolverConverter) - - private[sbt] def copy( - circularDependencyLevel: CircularDependencyLevel = this.circularDependencyLevel, - latestSnapshots: Boolean = this.latestSnapshots, - consolidatedResolution: Boolean = this.consolidatedResolution, - cachedResolution: Boolean = this.cachedResolution, - resolverConverter: UpdateOptions.ResolverConverter = this.resolverConverter): UpdateOptions = - new UpdateOptions(circularDependencyLevel, - latestSnapshots, - consolidatedResolution, - cachedResolution, - resolverConverter) - - override def equals(o: Any): Boolean = o match { - case o: UpdateOptions => - this.circularDependencyLevel == o.circularDependencyLevel && - this.latestSnapshots == o.latestSnapshots && - this.cachedResolution == o.cachedResolution && - this.resolverConverter == o.resolverConverter - case _ => false - } - - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.circularDependencyLevel.## - hash = hash * 31 + this.latestSnapshots.## - hash = hash * 31 + this.cachedResolution.## - hash = hash * 31 + this.resolverConverter.## - hash - } -} - -object UpdateOptions { - type ResolverConverter = PartialFunction[(Resolver, IvySettings, Logger), DependencyResolver] - - def apply(): UpdateOptions = - new UpdateOptions( - circularDependencyLevel = CircularDependencyLevel.Warn, - latestSnapshots = false, - consolidatedResolution = false, - cachedResolution = false, - resolverConverter = PartialFunction.empty) -} diff --git a/ivy/src/main/scala/sbt/UpdateReport.scala b/ivy/src/main/scala/sbt/UpdateReport.scala deleted file mode 100644 index 5a866632a..000000000 --- a/ivy/src/main/scala/sbt/UpdateReport.scala +++ /dev/null @@ -1,348 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -import java.io.File -import java.net.URL -import java.{ util => ju } -import sbt.serialization._ - -/** - * Provides information about resolution of a single configuration. - * @param configuration the configuration this report is for. - * @param modules a sequence containing one report for each module resolved for this configuration. - * @param details a sequence containing one report for each org/name, which may or may not be part of the final resolution. - * @param evicted a sequence of evicted modules - */ -final class ConfigurationReport( - val configuration: String, - val modules: Seq[ModuleReport], - val details: Seq[OrganizationArtifactReport], - @deprecated("Use details instead to get better eviction info.", "0.13.6") val evicted: Seq[ModuleID]) { - def this(configuration: String, modules: Seq[ModuleReport], evicted: Seq[ModuleID]) = - this(configuration, modules, Nil, evicted) - - override def toString = s"\t$configuration:\n" + - (if (details.isEmpty) modules.mkString + evicted.map("\t\t(EVICTED) " + _ + "\n").mkString - else details.mkString) - - /** - * All resolved modules for this configuration. - * For a given organization and module name, there is only one revision/`ModuleID` in this sequence. - */ - def allModules: Seq[ModuleID] = modules.map(mr => addConfiguration(mr.module)) - private[this] def addConfiguration(mod: ModuleID): ModuleID = if (mod.configurations.isEmpty) mod.copy(configurations = Some(configuration)) else mod - - def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport = - new ConfigurationReport(configuration, modules map { _.retrieve((mid, art, file) => f(configuration, mid, art, file)) }, details, evicted) -} -object ConfigurationReport { - implicit val pickler: Pickler[ConfigurationReport] with Unpickler[ConfigurationReport] = PicklerUnpickler.generate[ConfigurationReport] -} - -/** - * OrganizationArtifactReport represents an organization+name entry in Ivy resolution report. - * In sbt's terminology, "module" consists of organization, name, and version. - * In Ivy's, "module" means just organization and name, and the one including version numbers - * are called revisions. - * - * A sequence of OrganizationArtifactReport called details is newly added to ConfigurationReport, replacing evicted. - * (Note old evicted was just a seq of ModuleIDs). - * OrganizationArtifactReport groups the ModuleReport of both winners and evicted reports by their organization and name, - * which can be used to calculate detailed evction warning etc. - */ -final class OrganizationArtifactReport private[sbt] ( - val organization: String, - val name: String, - val modules: Seq[ModuleReport]) { - override def toString: String = { - val details = modules map { _.detailReport } - s"\t$organization:$name\n${details.mkString}\n" - } -} -object OrganizationArtifactReport { - implicit val pickler: Pickler[OrganizationArtifactReport] with Unpickler[OrganizationArtifactReport] = PicklerUnpickler.generate[OrganizationArtifactReport] - - def apply(organization: String, name: String, modules: Seq[ModuleReport]): OrganizationArtifactReport = - new OrganizationArtifactReport(organization, name, modules) -} - -/** - * Provides information about the resolution of a module. - * This information is in the context of a specific configuration. - * @param module the `ModuleID` this report is for. - * @param artifacts the resolved artifacts for this module, paired with the File the artifact was retrieved to. - * @param missingArtifacts the missing artifacts for this module. - */ -final class ModuleReport( - val module: ModuleID, - val artifacts: Seq[(Artifact, File)], - val missingArtifacts: Seq[Artifact], - val status: Option[String], - val publicationDate: Option[ju.Date], - val resolver: Option[String], - val artifactResolver: Option[String], - val evicted: Boolean, - val evictedData: Option[String], - val evictedReason: Option[String], - val problem: Option[String], - val homepage: Option[String], - val extraAttributes: Map[String, String], - val isDefault: Option[Boolean], - val branch: Option[String], - val configurations: Seq[String], - val licenses: Seq[(String, Option[String])], - val callers: Seq[Caller]) { - - private[this] lazy val arts: Seq[String] = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art) - override def toString: String = { - s"\t\t$module: " + - (if (arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n" - } - def detailReport: String = - s"\t\t- ${module.revision}\n" + - (if (arts.size <= 1) "" else arts.mkString("\t\t\t", "\n\t\t\t", "\n")) + - reportStr("status", status) + - reportStr("publicationDate", publicationDate map { _.toString }) + - reportStr("resolver", resolver) + - reportStr("artifactResolver", artifactResolver) + - reportStr("evicted", Some(evicted.toString)) + - reportStr("evictedData", evictedData) + - reportStr("evictedReason", evictedReason) + - reportStr("problem", problem) + - reportStr("homepage", homepage) + - reportStr("textraAttributes", - if (extraAttributes.isEmpty) None - else { Some(extraAttributes.toString) }) + - reportStr("isDefault", isDefault map { _.toString }) + - reportStr("branch", branch) + - reportStr("configurations", - if (configurations.isEmpty) None - else { Some(configurations.mkString(", ")) }) + - reportStr("licenses", - if (licenses.isEmpty) None - else { Some(licenses.mkString(", ")) }) + - reportStr("callers", - if (callers.isEmpty) None - else { Some(callers.mkString(", ")) }) - private[sbt] def reportStr(key: String, value: Option[String]): String = - value map { x => s"\t\t\t$key: $x\n" } getOrElse "" - - def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport = - copy(artifacts = artifacts.map { case (art, file) => (art, f(module, art, file)) }) - - private[sbt] def copy( - module: ModuleID = module, - artifacts: Seq[(Artifact, File)] = artifacts, - missingArtifacts: Seq[Artifact] = missingArtifacts, - status: Option[String] = status, - publicationDate: Option[ju.Date] = publicationDate, - resolver: Option[String] = resolver, - artifactResolver: Option[String] = artifactResolver, - evicted: Boolean = evicted, - evictedData: Option[String] = evictedData, - evictedReason: Option[String] = evictedReason, - problem: Option[String] = problem, - homepage: Option[String] = homepage, - extraAttributes: Map[String, String] = extraAttributes, - isDefault: Option[Boolean] = isDefault, - branch: Option[String] = branch, - configurations: Seq[String] = configurations, - licenses: Seq[(String, Option[String])] = licenses, - callers: Seq[Caller] = callers): ModuleReport = - new ModuleReport(module, artifacts, missingArtifacts, status, publicationDate, resolver, artifactResolver, - evicted, evictedData, evictedReason, problem, homepage, extraAttributes, isDefault, branch, configurations, licenses, callers) -} - -object ModuleReport { - def apply(module: ModuleID, artifacts: Seq[(Artifact, File)], missingArtifacts: Seq[Artifact]): ModuleReport = - new ModuleReport(module, artifacts, missingArtifacts, None, None, None, None, - false, None, None, None, None, Map(), None, None, Nil, Nil, Nil) - implicit val pickler: Pickler[ModuleReport] with Unpickler[ModuleReport] = PicklerUnpickler.generate[ModuleReport] -} - -final class Caller( - val caller: ModuleID, - val callerConfigurations: Seq[String], - val callerExtraAttributes: Map[String, String], - val isForceDependency: Boolean, - val isChangingDependency: Boolean, - val isTransitiveDependency: Boolean, - val isDirectlyForceDependency: Boolean) { - override def toString: String = - s"$caller" -} -object Caller { - implicit val pickler: Pickler[Caller] with Unpickler[Caller] = PicklerUnpickler.generate[Caller] -} - -/** - * Provides information about dependency resolution. - * It does not include information about evicted modules, only about the modules ultimately selected by the conflict manager. - * This means that for a given configuration, there should only be one revision for a given organization and module name. - * @param cachedDescriptor the location of the resolved module descriptor in the cache - * @param configurations a sequence containing one report for each configuration resolved. - * @param stats information about the update that produced this report - * @see sbt.RichUpdateReport - */ -final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[ConfigurationReport], val stats: UpdateStats, private[sbt] val stamps: Map[File, Long]) { - @deprecated("Use the variant that provides timestamps of files.", "0.13.0") - def this(cachedDescriptor: File, configurations: Seq[ConfigurationReport], stats: UpdateStats) = - this(cachedDescriptor, configurations, stats, Map.empty) - - override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString - - /** All resolved modules in all configurations. */ - def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct - - def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport = - new UpdateReport(cachedDescriptor, configurations map { _ retrieve f }, stats, stamps) - - /** Gets the report for the given configuration, or `None` if the configuration was not resolved.*/ - def configuration(s: String) = configurations.find(_.configuration == s) - - /** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */ - def allConfigurations: Seq[String] = configurations.map(_.configuration) - - private[sbt] def withStats(us: UpdateStats): UpdateReport = - new UpdateReport(this.cachedDescriptor, - this.configurations, - us, - this.stamps) -} - -object UpdateReport { - implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report) - - /** Provides extra methods for filtering the contents of an `UpdateReport` and for obtaining references to a selected subset of the underlying files. */ - final class RichUpdateReport(report: UpdateReport) { - def recomputeStamps(): UpdateReport = - { - val files = report.cachedDescriptor +: allFiles - val stamps = files.map(f => (f, f.lastModified)).toMap - new UpdateReport(report.cachedDescriptor, report.configurations, report.stats, stamps) - } - - import DependencyFilter._ - /** Obtains all successfully retrieved files in all configurations and modules. */ - def allFiles: Seq[File] = matching(DependencyFilter.allPass) - - /** Obtains all successfully retrieved files in configurations, modules, and artifacts matching the specified filter. */ - def matching(f: DependencyFilter): Seq[File] = select0(f).distinct - - /** Obtains all successfully retrieved files matching all provided filters. An unspecified argument matches all files. */ - def select(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): Seq[File] = - matching(DependencyFilter.make(configuration, module, artifact)) - - private[this] def select0(f: DependencyFilter): Seq[File] = - for (cReport <- report.configurations; mReport <- cReport.modules; (artifact, file) <- mReport.artifacts if f(cReport.configuration, mReport.module, artifact)) yield { - if (file == null) sys.error("Null file: conf=" + cReport.configuration + ", module=" + mReport.module + ", art: " + artifact) - file - } - - /** Constructs a new report that only contains files matching the specified filter.*/ - def filter(f: DependencyFilter): UpdateReport = - moduleReportMap { (configuration, modReport) => - modReport.copy( - artifacts = modReport.artifacts filter { case (art, file) => f(configuration, modReport.module, art) }, - missingArtifacts = modReport.missingArtifacts filter { art => f(configuration, modReport.module, art) } - ) - } - def substitute(f: (String, ModuleID, Seq[(Artifact, File)]) => Seq[(Artifact, File)]): UpdateReport = - moduleReportMap { (configuration, modReport) => - val newArtifacts = f(configuration, modReport.module, modReport.artifacts) - modReport.copy( - artifacts = f(configuration, modReport.module, modReport.artifacts), - missingArtifacts = Nil - ) - } - - def toSeq: Seq[(String, ModuleID, Artifact, File)] = - for (confReport <- report.configurations; modReport <- confReport.modules; (artifact, file) <- modReport.artifacts) yield (confReport.configuration, modReport.module, artifact, file) - - def allMissing: Seq[(String, ModuleID, Artifact)] = - for (confReport <- report.configurations; modReport <- confReport.modules; artifact <- modReport.missingArtifacts) yield (confReport.configuration, modReport.module, artifact) - - def addMissing(f: ModuleID => Seq[Artifact]): UpdateReport = - moduleReportMap { (configuration, modReport) => - modReport.copy( - missingArtifacts = (modReport.missingArtifacts ++ f(modReport.module)).distinct - ) - } - - def moduleReportMap(f: (String, ModuleReport) => ModuleReport): UpdateReport = - { - val newConfigurations = report.configurations.map { confReport => - import confReport._ - val newModules = modules map { modReport => f(configuration, modReport) } - new ConfigurationReport(configuration, newModules, details, evicted) - } - new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps) - } - } - - private val vectorConfigurationReportPickler = implicitly[Pickler[Vector[ConfigurationReport]]] - private val vectorConfigurationReportUnpickler = implicitly[Unpickler[Vector[ConfigurationReport]]] - private val updateStatsPickler = implicitly[Pickler[UpdateStats]] - private val updateStatsUnpickler = implicitly[Unpickler[UpdateStats]] - private val flMapPickler = implicitly[Pickler[Map[File, Long]]] - private val flMapUnpickler = implicitly[Unpickler[Map[File, Long]]] - - implicit val pickler: Pickler[UpdateReport] with Unpickler[UpdateReport] = new Pickler[UpdateReport] with Unpickler[UpdateReport] { - val tag = implicitly[FastTypeTag[UpdateReport]] - val fileTag = implicitly[FastTypeTag[File]] - val vectorConfigurationReportTag = implicitly[FastTypeTag[Vector[ConfigurationReport]]] - val updateStatsTag = implicitly[FastTypeTag[UpdateStats]] - val flMapTag = implicitly[FastTypeTag[Map[File, Long]]] - def pickle(a: UpdateReport, builder: PBuilder): Unit = { - builder.pushHints() - builder.hintTag(tag) - builder.beginEntry(a) - builder.putField("cachedDescriptor", { b => - b.hintTag(fileTag) - filePickler.pickle(a.cachedDescriptor, b) - }) - builder.putField("configurations", { b => - b.hintTag(vectorConfigurationReportTag) - vectorConfigurationReportPickler.pickle(a.configurations.toVector, b) - }) - builder.putField("stats", { b => - b.hintTag(updateStatsTag) - updateStatsPickler.pickle(a.stats, b) - }) - builder.putField("stamps", { b => - b.hintTag(flMapTag) - flMapPickler.pickle(a.stamps, b) - }) - builder.endEntry() - builder.popHints() - } - - def unpickle(tpe: String, reader: PReader): Any = { - reader.pushHints() - reader.hintTag(tag) - reader.beginEntry() - val cachedDescriptor = filePickler.unpickleEntry(reader.readField("cachedDescriptor")).asInstanceOf[File] - val configurations = vectorConfigurationReportUnpickler.unpickleEntry(reader.readField("configurations")).asInstanceOf[Vector[ConfigurationReport]] - val stats = updateStatsUnpickler.unpickleEntry(reader.readField("stats")).asInstanceOf[UpdateStats] - val stamps = flMapUnpickler.unpickleEntry(reader.readField("stamps")).asInstanceOf[Map[File, Long]] - val result = new UpdateReport(cachedDescriptor, configurations, stats, stamps) - reader.endEntry() - reader.popHints() - result - } - } -} - -final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downloadSize: Long, val cached: Boolean) { - override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ") - private[sbt] def withCached(c: Boolean): UpdateStats = - new UpdateStats(resolveTime = this.resolveTime, - downloadTime = this.downloadTime, - downloadSize = this.downloadSize, - cached = c) -} -object UpdateStats { - implicit val pickler: Pickler[UpdateStats] with Unpickler[UpdateStats] = PicklerUnpickler.generate[UpdateStats] -} diff --git a/ivy/src/main/scala/sbt/VersionNumber.scala b/ivy/src/main/scala/sbt/VersionNumber.scala deleted file mode 100644 index a5d1d494b..000000000 --- a/ivy/src/main/scala/sbt/VersionNumber.scala +++ /dev/null @@ -1,152 +0,0 @@ -package sbt - -final class VersionNumber private[sbt] ( - val numbers: Seq[Long], - val tags: Seq[String], - val extras: Seq[String]) { - def _1: Option[Long] = get(0) - def _2: Option[Long] = get(1) - def _3: Option[Long] = get(2) - def _4: Option[Long] = get(3) - def get(idx: Int): Option[Long] = - if (size <= idx) None - else Some(numbers(idx)) - def size: Int = numbers.size - - /** The vector of version numbers from more to less specific from this version number. */ - lazy val cascadingVersions: Vector[VersionNumber] = - (Vector(this) ++ - (numbers.inits filter (_.length >= 2) map (VersionNumber(_, Nil, Nil)))).distinct - - private[this] val versionStr: String = - numbers.mkString(".") + - (tags match { - case Seq() => "" - case ts => "-" + ts.mkString("-") - }) + - extras.mkString("") - override def toString: String = versionStr - override def hashCode: Int = - numbers.hashCode * 41 * 41 + - tags.hashCode * 41 + - extras.hashCode - override def equals(o: Any): Boolean = - o match { - case v: VersionNumber => (this.numbers == v.numbers) && (this.tags == v.tags) && (this.extras == v.extras) - case _ => false - } -} - -object VersionNumber { - /** - * @param numbers numbers delimited by a dot. - * @param tags string prefixed by a dash. - * @param any other strings at the end. - */ - def apply(numbers: Seq[Long], tags: Seq[String], extras: Seq[String]): VersionNumber = - new VersionNumber(numbers, tags, extras) - def apply(v: String): VersionNumber = - unapply(v) match { - case Some((ns, ts, es)) => VersionNumber(ns, ts, es) - case _ => sys.error(s"Invalid version number: $v") - } - - def unapply(v: VersionNumber): Option[(Seq[Long], Seq[String], Seq[String])] = - Some((v.numbers, v.tags, v.extras)) - - def unapply(v: String): Option[(Seq[Long], Seq[String], Seq[String])] = { - def splitDot(s: String): Vector[Long] = - Option(s) match { - case Some(x) => x.split('.').toVector.filterNot(_ == "").map(_.toLong) - case _ => Vector() - } - def splitDash(s: String): Vector[String] = - Option(s) match { - case Some(x) => x.split('-').toVector.filterNot(_ == "") - case _ => Vector() - } - def splitPlus(s: String): Vector[String] = - Option(s) match { - case Some(x) => x.split('+').toVector.filterNot(_ == "").map("+" + _) - case _ => Vector() - } - val TaggedVersion = """(\d{1,14})([\.\d{1,14}]*)((?:-\w+)*)((?:\+.+)*)""".r - val NonSpaceString = """(\S+)""".r - v match { - case TaggedVersion(m, ns, ts, es) => Some((Vector(m.toLong) ++ splitDot(ns), splitDash(ts), splitPlus(es))) - case "" => None - case NonSpaceString(s) => Some((Vector(), Vector(), Vector(s))) - case _ => None - } - } - - /** - * Strict. Checks everythig. - */ - object Strict extends VersionNumberCompatibility { - def name: String = "Strict" - def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = v1 == v2 - } - - /** - * Semantic versioning. See http://semver.org/spec/v2.0.0.html - */ - object SemVer extends VersionNumberCompatibility { - def name: String = "Semantic Versioning" - def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = - doIsCompat(v1, v2) || doIsCompat(v2, v1) - private[this] def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1, v2) match { - case (v1, v2) if (v1.size >= 2) && (v2.size >= 2) => // A normal version number MUST take the form X.Y.Z - (v1._1.get, v1._2.get, v1._3.getOrElse(0), v1.tags, v2._1.get, v2._2.get, v2._3.getOrElse(0), v2.tags) match { - case (0L, _, _, _, 0L, _, _, _) => - // Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable. - equalsIgnoreExtra(v1, v2) - case (_, 0, 0, ts1, _, 0, 0, ts2) if ts1.nonEmpty || ts2.nonEmpty => - // A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers - equalsIgnoreExtra(v1, v2) - case (x1, _, _, _, x2, _, _, _) => - // Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards compatible bug fixes are introduced. - // Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backwards compatible functionality is introduced - x1 == x2 - case _ => equalsIgnoreExtra(v1, v2) - } - case _ => false - } - // Build metadata SHOULD be ignored when determining version precedence. - private[this] def equalsIgnoreExtra(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1.numbers == v2.numbers) && (v1.tags == v2.tags) - } - - /* A variant of SemVar that seems to be common among the Scala libraries. - * The second segment (y in x.y.z) increments breaks the binary compatibility even when x > 0. - * Also API comatibility is expected even when the first segment is zero. - */ - object SecondSegment extends VersionNumberCompatibility { - def name: String = "Second Segment Variant" - def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = - doIsCompat(v1, v2) || doIsCompat(v2, v1) - private[this] def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1, v2) match { - case (v1, v2) if (v1.size >= 3) && (v2.size >= 3) => // A normal version number MUST take the form X.Y.Z - (v1._1.get, v1._2.get, v1._3.get, v1.tags, v2._1.get, v2._2.get, v2._3.get, v2.tags) match { - case (x1, y1, 0, ts1, x2, y2, 0, ts2) if ts1.nonEmpty || ts2.nonEmpty => - // A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers - equalsIgnoreExtra(v1, v2) - case (x1, y1, _, _, x2, y2, _, _) => - // Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards compatible changes are introduced. - (x1 == x2) && (y1 == y2) - case _ => equalsIgnoreExtra(v1, v2) - } - case _ => false - } - // Build metadata SHOULD be ignored when determining version precedence. - private[this] def equalsIgnoreExtra(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1.numbers == v2.numbers) && (v1.tags == v2.tags) - } -} - -trait VersionNumberCompatibility { - def name: String - def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean -} diff --git a/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala b/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala deleted file mode 100755 index c449393f7..000000000 --- a/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009,2010 Mark Harrah - */ -package sbt -package impl - -import StringUtilities.nonEmpty - -trait DependencyBuilders { - final implicit def toGroupID(groupID: String): GroupID = - { - nonEmpty(groupID, "Group ID") - new GroupID(groupID) - } - final implicit def toRepositoryName(name: String): RepositoryName = - { - nonEmpty(name, "Repository name") - new RepositoryName(name) - } - final implicit def moduleIDConfigurable(m: ModuleID): ModuleIDConfigurable = - { - require(m.configurations.isEmpty, "Configurations already specified for module " + m) - new ModuleIDConfigurable(m) - } -} - -final class GroupID private[sbt] (private[sbt] val groupID: String) { - def %(artifactID: String) = groupArtifact(artifactID, CrossVersion.Disabled) - def %%(artifactID: String): GroupArtifactID = groupArtifact(artifactID, CrossVersion.binary) - - @deprecated(deprecationMessage, "0.12.0") - def %%(artifactID: String, crossVersion: String => String) = groupArtifact(artifactID, CrossVersion.binaryMapped(crossVersion)) - @deprecated(deprecationMessage, "0.12.0") - def %%(artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, CrossVersion.binaryMapped(Map(alternatives: _*) orElse { case s => s })) - - private def groupArtifact(artifactID: String, cross: CrossVersion) = - { - nonEmpty(artifactID, "Artifact ID") - new GroupArtifactID(groupID, artifactID, cross) - } - private[this] def deprecationMessage = """Use the cross method on the constructed ModuleID. For example: ("a" % "b" % "1").cross(...)""" -} -final class GroupArtifactID private[sbt] ( - private[sbt] val groupID: String, - private[sbt] val artifactID: String, - private[sbt] val crossVersion: CrossVersion) { - def %(revision: String): ModuleID = - { - nonEmpty(revision, "Revision") - ModuleID(groupID, artifactID, revision).cross(crossVersion) - } -} -final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID) { - def %(configuration: Configuration): ModuleID = %(configuration.name) - - def %(configurations: String): ModuleID = - { - nonEmpty(configurations, "Configurations") - val c = configurations - moduleID.copy(configurations = Some(c)) - } -} -final class RepositoryName private[sbt] (name: String) { - def at(location: String) = - { - nonEmpty(location, "Repository location") - new MavenRepository(name, location) - } -} diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala deleted file mode 100644 index a1fc5ddfc..000000000 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ /dev/null @@ -1,738 +0,0 @@ -package sbt -package ivyint - -import java.util.Date -import java.net.URL -import java.io.File -import java.text.SimpleDateFormat -import collection.concurrent -import collection.mutable -import collection.immutable.ListMap -import org.apache.ivy.Ivy -import org.apache.ivy.core -import core.resolve._ -import core.module.id.{ ModuleRevisionId, ModuleId => IvyModuleId } -import core.report.{ ResolveReport, ConfigurationResolveReport, DownloadReport } -import core.module.descriptor.{ DefaultModuleDescriptor, ModuleDescriptor, DefaultDependencyDescriptor, DependencyDescriptor, Configuration => IvyConfiguration, ExcludeRule, IncludeRule } -import core.module.descriptor.{ OverrideDependencyDescriptorMediator, DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor } -import core.{ IvyPatternHelper, LogOptions } -import org.apache.ivy.util.{ Message, MessageLogger } -import org.apache.ivy.plugins.latest.{ ArtifactInfo => IvyArtifactInfo } -import org.apache.ivy.plugins.matcher.{ MapMatcher, PatternMatcher } -import Configurations.{ System => _, _ } -import annotation.tailrec -import scala.concurrent.duration._ - -private[sbt] object CachedResolutionResolveCache { - def createID(organization: String, name: String, revision: String) = - ModuleRevisionId.newInstance(organization, name, revision) - def sbtOrgTemp = JsonUtil.sbtOrgTemp - def graphVersion = "0.13.9C" - val buildStartup: Long = System.currentTimeMillis - lazy val todayStr: String = toYyyymmdd(buildStartup) - lazy val tomorrowStr: String = toYyyymmdd(buildStartup + (1 day).toMillis) - lazy val yesterdayStr: String = toYyyymmdd(buildStartup - (1 day).toMillis) - def toYyyymmdd(timeSinceEpoch: Long): String = yyyymmdd.format(new Date(timeSinceEpoch)) - lazy val yyyymmdd: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd") -} - -private[sbt] class CachedResolutionResolveCache() { - import CachedResolutionResolveCache._ - val updateReportCache: concurrent.Map[ModuleRevisionId, Either[ResolveException, UpdateReport]] = concurrent.TrieMap() - // Used for subproject - val projectReportCache: concurrent.Map[(ModuleRevisionId, LogicalClock), Either[ResolveException, UpdateReport]] = concurrent.TrieMap() - val resolveReportCache: concurrent.Map[ModuleRevisionId, ResolveReport] = concurrent.TrieMap() - val resolvePropertiesCache: concurrent.Map[ModuleRevisionId, String] = concurrent.TrieMap() - val conflictCache: concurrent.Map[(ModuleID, ModuleID), (Vector[ModuleID], Vector[ModuleID], String)] = concurrent.TrieMap() - val maxConflictCacheSize: Int = 1024 - val maxUpdateReportCacheSize: Int = 1024 - - def clean(md0: ModuleDescriptor, prOpt: Option[ProjectResolver]): Unit = { - updateReportCache.clear - } - def directDependencies(md0: ModuleDescriptor): Vector[DependencyDescriptor] = - md0.getDependencies.toVector - // Returns a vector of (module descriptor, changing, dd) - def buildArtificialModuleDescriptors(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver], log: Logger): Vector[(DefaultModuleDescriptor, Boolean, DependencyDescriptor)] = - { - log.debug(s":: building artificial module descriptors from ${md0.getModuleRevisionId}") - // val expanded = expandInternalDependencies(md0, data, prOpt, log) - val rootModuleConfigs = md0.getConfigurations.toVector - directDependencies(md0) map { dd => - val arts = dd.getAllDependencyArtifacts.toVector map { x => s"""${x.getName}:${x.getType}:${x.getExt}:${x.getExtraAttributes}""" } - log.debug(s"::: dd: $dd (artifacts: ${arts.mkString(",")})") - buildArtificialModuleDescriptor(dd, rootModuleConfigs, md0, prOpt, log) - } - } - def internalDependency(dd: DependencyDescriptor, prOpt: Option[ProjectResolver]): Option[ModuleDescriptor] = - prOpt match { - case Some(pr) => pr.getModuleDescriptor(dd.getDependencyRevisionId) - case _ => None - } - def buildArtificialModuleDescriptor(dd: DependencyDescriptor, rootModuleConfigs: Vector[IvyConfiguration], - parent: ModuleDescriptor, prOpt: Option[ProjectResolver], log: Logger): (DefaultModuleDescriptor, Boolean, DependencyDescriptor) = - { - def excludeRuleString(rule: ExcludeRule): String = - s"""Exclude(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})""" - def includeRuleString(rule: IncludeRule): String = - s"""Include(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})""" - def artifactString(dad: DependencyArtifactDescriptor): String = - s"""Artifact(${dad.getName},${dad.getType},${dad.getExt},${dad.getUrl},${dad.getConfigurations.mkString(",")},${dad.getExtraAttributes})""" - val mrid = dd.getDependencyRevisionId - val confMap = (dd.getModuleConfigurations map { conf => - conf + "->(" + dd.getDependencyConfigurations(conf).mkString(",") + ")" - }) - val exclusions = (dd.getModuleConfigurations.toVector flatMap { conf => - dd.getExcludeRules(conf).toVector match { - case Vector() => None - case rules => Some(conf + "->(" + (rules map excludeRuleString).mkString(",") + ")") - } - }) - val inclusions = (dd.getModuleConfigurations.toVector flatMap { conf => - dd.getIncludeRules(conf).toVector match { - case Vector() => None - case rules => Some(conf + "->(" + (rules map includeRuleString).mkString(",") + ")") - } - }) - val explicitArtifacts = dd.getAllDependencyArtifacts.toVector map { artifactString } - val mes = parent.getAllExcludeRules.toVector - val mesStr = (mes map excludeRuleString).mkString(",") - val os = extractOverrides(parent) - val moduleLevel = s"""dependencyOverrides=${os.mkString(",")};moduleExclusions=$mesStr""" - val depsString = s"""$mrid;${confMap.mkString(",")};isForce=${dd.isForce};isChanging=${dd.isChanging};isTransitive=${dd.isTransitive};""" + - s"""exclusions=${exclusions.mkString(",")};inclusions=${inclusions.mkString(",")};explicitArtifacts=${explicitArtifacts.mkString(",")};$moduleLevel;""" - val sha1 = Hash.toHex(Hash(s"""graphVersion=${CachedResolutionResolveCache.graphVersion};$depsString""")) - val md1 = new DefaultModuleDescriptor(createID(sbtOrgTemp, "temp-resolve-" + sha1, "1.0"), "release", null, false) with ArtificialModuleDescriptor { - def targetModuleRevisionId: ModuleRevisionId = mrid - } - for { - conf <- rootModuleConfigs - } yield md1.addConfiguration(conf) - md1.addDependency(dd) - os foreach { ovr => - md1.addDependencyDescriptorMediator(ovr.moduleId, ovr.pm, ovr.ddm) - } - mes foreach { exclude => - md1.addExcludeRule(exclude) - } - (md1, IvySbt.isChanging(dd) || internalDependency(dd, prOpt).isDefined, dd) - } - def extractOverrides(md0: ModuleDescriptor): Vector[IvyOverride] = - { - import scala.collection.JavaConversions._ - (md0.getAllDependencyDescriptorMediators.getAllRules).toSeq.toVector sortBy { - case (k, v) => - k.toString - } collect { - case (k: MapMatcher, v: OverrideDependencyDescriptorMediator) => - val attr: Map[Any, Any] = k.getAttributes.toMap - val module = IvyModuleId.newInstance(attr(IvyPatternHelper.ORGANISATION_KEY).toString, attr(IvyPatternHelper.MODULE_KEY).toString) - val pm = k.getPatternMatcher - IvyOverride(module, pm, v) - } - } - def getOrElseUpdateMiniGraph(md: ModuleDescriptor, changing0: Boolean, logicalClock: LogicalClock, miniGraphPath: File, cachedDescriptor: File, log: Logger)(f: => Either[ResolveException, UpdateReport]): Either[ResolveException, UpdateReport] = - { - import Path._ - val mrid = md.getResolvedModuleRevisionId - val (pathOrg, pathName, pathRevision) = md match { - case x: ArtificialModuleDescriptor => - val tmrid = x.targetModuleRevisionId - (tmrid.getOrganisation, tmrid.getName, tmrid.getRevision + "_" + mrid.getName) - case _ => - (mrid.getOrganisation, mrid.getName, mrid.getRevision) - } - val staticGraphDirectory = miniGraphPath / "static" - val dynamicGraphDirectory = miniGraphPath / "dynamic" - val staticGraphPath = staticGraphDirectory / pathOrg / pathName / pathRevision / "graphs" / "graph.json" - val dynamicGraphPath = dynamicGraphDirectory / todayStr / logicalClock.toString / pathOrg / pathName / pathRevision / "graphs" / "graph.json" - def cleanDynamicGraph(): Unit = - { - val list = IO.listFiles(dynamicGraphDirectory, DirectoryFilter).toList - list filterNot { d => - (d.getName == todayStr) || (d.getName == tomorrowStr) || (d.getName == yesterdayStr) - } foreach { d => - log.debug(s"deleting old graphs $d...") - IO.delete(d) - } - } - def loadMiniGraphFromFile: Option[Either[ResolveException, UpdateReport]] = - (if (staticGraphPath.exists) Some(staticGraphPath) - else if (dynamicGraphPath.exists) Some(dynamicGraphPath) - else None) match { - case Some(path) => - log.debug(s"parsing ${path.getAbsolutePath.toString}") - val ur = JsonUtil.parseUpdateReport(md, path, cachedDescriptor, log) - if (ur.allFiles forall { _.exists }) { - updateReportCache(md.getModuleRevisionId) = Right(ur) - Some(Right(ur)) - } else { - log.debug(s"some files are missing from the cache, so invalidating the minigraph") - IO.delete(path) - None - } - case _ => None - } - (updateReportCache.get(mrid) orElse loadMiniGraphFromFile) match { - case Some(result) => - result match { - case Right(ur) => Right(ur.withStats(ur.stats.withCached(true))) - case x => x - } - case None => - f match { - case Right(ur) => - val changing = changing0 || (ur.configurations exists { cr => - cr.details exists { oar => - oar.modules exists { mr => - IvySbt.isChanging(mr.module) || (mr.callers exists { _.isChangingDependency }) - } - } - }) - IO.createDirectory(miniGraphPath) - val gp = if (changing) dynamicGraphPath - else staticGraphPath - log.debug(s"saving minigraph to $gp") - if (changing) { - cleanDynamicGraph() - } - JsonUtil.writeUpdateReport(ur, gp) - // limit the update cache size - if (updateReportCache.size > maxUpdateReportCacheSize) { - updateReportCache.remove(updateReportCache.head._1) - } - // don't cache dynamic graphs in memory. - if (!changing) { - updateReportCache(md.getModuleRevisionId) = Right(ur) - } - Right(ur) - case Left(re) => - if (!changing0) { - updateReportCache(md.getModuleRevisionId) = Left(re) - } - Left(re) - } - } - } - - def getOrElseUpdateConflict(cf0: ModuleID, cf1: ModuleID, conflicts: Vector[ModuleReport])(f: => (Vector[ModuleReport], Vector[ModuleReport], String)): (Vector[ModuleReport], Vector[ModuleReport]) = - { - def reconstructReports(surviving: Vector[ModuleID], evicted: Vector[ModuleID], mgr: String): (Vector[ModuleReport], Vector[ModuleReport]) = { - val moduleIdMap = Map(conflicts map { x => x.module -> x }: _*) - (surviving map moduleIdMap, evicted map moduleIdMap map { _.copy(evicted = true, evictedReason = Some(mgr.toString)) }) - } - (conflictCache get ((cf0, cf1))) match { - case Some((surviving, evicted, mgr)) => reconstructReports(surviving, evicted, mgr) - case _ => - (conflictCache get ((cf1, cf0))) match { - case Some((surviving, evicted, mgr)) => reconstructReports(surviving, evicted, mgr) - case _ => - val (surviving, evicted, mgr) = f - if (conflictCache.size > maxConflictCacheSize) { - conflictCache.remove(conflictCache.head._1) - } - conflictCache((cf0, cf1)) = (surviving map { _.module }, evicted map { _.module }, mgr) - (surviving, evicted) - } - } - } - def getOrElseUpdateProjectReport(mrid: ModuleRevisionId, logicalClock: LogicalClock)(f: => Either[ResolveException, UpdateReport]): Either[ResolveException, UpdateReport] = - if (projectReportCache contains (mrid -> logicalClock)) projectReportCache((mrid, logicalClock)) - else { - val oldKeys = projectReportCache.keys filter { case (x, clk) => clk != logicalClock } - projectReportCache --= oldKeys - projectReportCache.getOrElseUpdate((mrid, logicalClock), f) - } -} - -private[sbt] trait ArtificialModuleDescriptor { self: DefaultModuleDescriptor => - def targetModuleRevisionId: ModuleRevisionId -} - -private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { - import CachedResolutionResolveCache._ - - private[sbt] def cachedResolutionResolveCache: CachedResolutionResolveCache - private[sbt] def projectResolver: Option[ProjectResolver] - private[sbt] def makeInstance: Ivy - private[sbt] val ignoreTransitiveForce: Boolean = true - - def withIvy[A](log: Logger)(f: Ivy => A): A = - withIvy(new IvyLoggerInterface(log))(f) - def withIvy[A](log: MessageLogger)(f: Ivy => A): A = - withDefaultLogger(log) { - val ivy = makeInstance - ivy.pushContext() - ivy.getLoggerEngine.pushLogger(log) - try { f(ivy) } - finally { - ivy.getLoggerEngine.popLogger() - ivy.popContext() - } - } - def withDefaultLogger[A](log: MessageLogger)(f: => A): A = - { - val originalLogger = Message.getDefaultLogger - Message.setDefaultLogger(log) - try { f } - finally { Message.setDefaultLogger(originalLogger) } - } - - /** - * This returns sbt's UpdateReport structure. - * missingOk allows sbt to call this with classifiers that may or may not exist, and grab the JARs. - */ - def customResolve(md0: ModuleDescriptor, missingOk: Boolean, logicalClock: LogicalClock, options0: ResolveOptions, depDir: File, log: Logger): Either[ResolveException, UpdateReport] = - cachedResolutionResolveCache.getOrElseUpdateProjectReport(md0.getModuleRevisionId, logicalClock) { - import Path._ - val start = System.currentTimeMillis - val miniGraphPath = depDir / "module" - val cachedDescriptor = getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md0.getModuleRevisionId) - val cache = cachedResolutionResolveCache - val os = cache.extractOverrides(md0) - val options1 = new ResolveOptions(options0) - val data = new ResolveData(this, options1) - val mds = cache.buildArtificialModuleDescriptors(md0, data, projectResolver, log) - - def doWork(md: ModuleDescriptor, dd: DependencyDescriptor): Either[ResolveException, UpdateReport] = - cache.internalDependency(dd, projectResolver) match { - case Some(md1) => - log.debug(s":: call customResolve recursively: $dd") - customResolve(md1, missingOk, logicalClock, options0, depDir, log) match { - case Right(ur) => Right(remapInternalProject(new IvyNode(data, md1), ur, md0, dd, os, log)) - case Left(e) => Left(e) - } - case None => - log.debug(s":: call ivy resolution: $dd") - doWorkUsingIvy(md) - } - def doWorkUsingIvy(md: ModuleDescriptor): Either[ResolveException, UpdateReport] = - { - val options1 = new ResolveOptions(options0) - var rr = withIvy(log) { ivy => - ivy.resolve(md, options1) - } - if (!rr.hasError || missingOk) Right(IvyRetrieve.updateReport(rr, cachedDescriptor)) - else { - val messages = rr.getAllProblemMessages.toArray.map(_.toString).distinct - val failedPaths = ListMap(rr.getUnresolvedDependencies map { node => - val m = IvyRetrieve.toModuleID(node.getId) - val path = IvyRetrieve.findPath(node, md.getModuleRevisionId) map { x => - IvyRetrieve.toModuleID(x.getId) - } - log.debug("- Unresolved path " + path.toString) - m -> path - }: _*) - val failed = failedPaths.keys.toSeq - Left(new ResolveException(messages, failed, failedPaths)) - } - } - val results = mds map { - case (md, changing, dd) => - cache.getOrElseUpdateMiniGraph(md, changing, logicalClock, miniGraphPath, cachedDescriptor, log) { - doWork(md, dd) - } - } - val uReport = mergeResults(md0, results, missingOk, System.currentTimeMillis - start, os, log) - val cacheManager = getSettings.getResolutionCacheManager - cacheManager.saveResolvedModuleDescriptor(md0) - val prop0 = "" - val ivyPropertiesInCache0 = cacheManager.getResolvedIvyPropertiesInCache(md0.getResolvedModuleRevisionId) - IO.write(ivyPropertiesInCache0, prop0) - uReport - } - - def mergeResults(md0: ModuleDescriptor, results: Vector[Either[ResolveException, UpdateReport]], missingOk: Boolean, resolveTime: Long, - os: Vector[IvyOverride], log: Logger): Either[ResolveException, UpdateReport] = - if (!missingOk && (results exists { _.isLeft })) Left(mergeErrors(md0, results collect { case Left(re) => re }, log)) - else Right(mergeReports(md0, results collect { case Right(ur) => ur }, resolveTime, os, log)) - def mergeErrors(md0: ModuleDescriptor, errors: Vector[ResolveException], log: Logger): ResolveException = - { - val messages = errors flatMap { _.messages } - val failed = errors flatMap { _.failed } - val failedPaths = errors flatMap { - _.failedPaths.toList map { - case (failed, paths) => - if (paths.isEmpty) (failed, paths) - else (failed, List(IvyRetrieve.toModuleID(md0.getResolvedModuleRevisionId)) ::: paths.toList.tail) - } - } - new ResolveException(messages, failed, ListMap(failedPaths: _*)) - } - def mergeReports(md0: ModuleDescriptor, reports: Vector[UpdateReport], resolveTime: Long, os: Vector[IvyOverride], log: Logger): UpdateReport = - { - log.debug(s":: merging update reports") - val cachedDescriptor = getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md0.getModuleRevisionId) - val rootModuleConfigs = md0.getConfigurations.toVector - val cachedReports = reports filter { !_.stats.cached } - val stats = new UpdateStats(resolveTime, (cachedReports map { _.stats.downloadTime }).sum, (cachedReports map { _.stats.downloadSize }).sum, false) - val configReports = rootModuleConfigs map { conf => - log.debug("::: -----------") - val crs = reports flatMap { _.configurations filter { _.configuration == conf.getName } } - mergeConfigurationReports(conf.getName, crs, os, log) - } - new UpdateReport(cachedDescriptor, configReports, stats, Map.empty) - } - // memory usage 62%, of which 58% is in mergeOrganizationArtifactReports - def mergeConfigurationReports(rootModuleConf: String, reports: Vector[ConfigurationReport], os: Vector[IvyOverride], log: Logger): ConfigurationReport = - { - // get the details right, and the rest could be derived - val details = mergeOrganizationArtifactReports(rootModuleConf, reports flatMap { _.details }, os, log) - val modules = details flatMap { - _.modules filter { mr => - !mr.evicted && mr.problem.isEmpty - } - } - val evicted = details flatMap { - _.modules filter { mr => - mr.evicted - } - } map { _.module } - new ConfigurationReport(rootModuleConf, modules, details, evicted) - } - /** - * Returns a tuple of (merged org + name combo, newly evicted modules) - */ - def mergeOrganizationArtifactReports(rootModuleConf: String, reports0: Vector[OrganizationArtifactReport], os: Vector[IvyOverride], log: Logger): Vector[OrganizationArtifactReport] = - { - // filter out evicted modules from further logic - def filterReports(report0: OrganizationArtifactReport): Option[OrganizationArtifactReport] = - report0.modules.toVector flatMap { mr => - if (mr.evicted || mr.problem.nonEmpty) None - else - // https://github.com/sbt/sbt/issues/1763 - Some(mr.copy(callers = JsonUtil.filterOutArtificialCallers(mr.callers))) - } match { - case Vector() => None - case ms => Some(OrganizationArtifactReport(report0.organization, report0.name, ms)) - } - - // group by takes up too much memory. trading space with time. - val orgNamePairs: Vector[(String, String)] = (reports0 map { oar => (oar.organization, oar.name) }).distinct - // this might take up some memory, but it's limited to a single - val reports1 = reports0 flatMap { filterReports } - val allModules0: Map[(String, String), Vector[OrganizationArtifactReport]] = - Map(orgNamePairs map { - case (organization, name) => - val xs = reports1 filter { oar => oar.organization == organization && oar.name == name } - ((organization, name), xs) - }: _*) - // this returns a List of Lists of (org, name). should be deterministic - def detectLoops(allModules: Map[(String, String), Vector[OrganizationArtifactReport]]): List[List[(String, String)]] = - { - val loopSets: mutable.Set[Set[(String, String)]] = mutable.Set.empty - val loopLists: mutable.ListBuffer[List[(String, String)]] = mutable.ListBuffer.empty - def testLoop(m: (String, String), current: (String, String), history: List[(String, String)]): Unit = - { - val callers = - (for { - oar <- allModules.getOrElse(current, Vector()) - mr <- oar.modules - c <- mr.callers - } yield (c.caller.organization, c.caller.name)).distinct - callers foreach { c => - if (history contains c) { - val loop = (c :: history.takeWhile(_ != c)) ::: List(c) - if (!loopSets(loop.toSet)) { - loopSets += loop.toSet - loopLists += loop - val loopStr = (loop map { case (o, n) => s"$o:$n" }).mkString("->") - log.warn(s"""avoid circular dependency while using cached resolution: $loopStr""") - } - } else testLoop(m, c, c :: history) - } - } - orgNamePairs map { orgname => - testLoop(orgname, orgname, List(orgname)) - } - loopLists.toList - } - val allModules2: mutable.Map[(String, String), Vector[OrganizationArtifactReport]] = - mutable.Map(allModules0.toSeq: _*) - @tailrec def breakLoops(loops: List[List[(String, String)]]): Unit = - loops match { - case Nil => () - case loop :: rest => - loop match { - case Nil => - breakLoops(rest) - case loop => - val sortedLoop = loop sortBy { x => - (for { - oar <- allModules0(x) - mr <- oar.modules - c <- mr.callers - } yield c).size - } - val moduleWithMostCallers = sortedLoop.reverse.head - val next: (String, String) = loop(loop.indexOf(moduleWithMostCallers) + 1) - // remove the module with most callers as the caller of next. - // so, A -> C, B -> C, and C -> A. C has the most callers, and C -> A will be removed. - allModules2 foreach { - case (k: (String, String), oars0) if k == next => - val oars: Vector[OrganizationArtifactReport] = oars0 map { oar => - val mrs = oar.modules map { mr => - val callers0 = mr.callers - val callers = callers0 filterNot { c => (c.caller.organization, c.caller.name) == moduleWithMostCallers } - if (callers.size == callers0.size) mr - else { - log.debug(s":: $rootModuleConf: removing caller $moduleWithMostCallers -> $next for sorting") - mr.copy(callers = callers) - } - } - OrganizationArtifactReport(oar.organization, oar.name, mrs) - } - allModules2(k) = oars - case (k, v) => // do nothing - } - - breakLoops(rest) - } - } - val loop = detectLoops(allModules0) - log.debug(s":: $rootModuleConf: loop: $loop") - breakLoops(loop) - - // sort the all modules such that less called modules comes earlier - @tailrec def sortModules(cs: Vector[(String, String)], - acc: Vector[(String, String)], extra: Vector[(String, String)], - n: Int, guard: Int): Vector[(String, String)] = - { - // println(s"sortModules: $n / $guard") - val keys = cs.toSet - val (called, notCalled) = cs partition { k => - val reports = allModules2(k) - reports exists { - _.modules.exists { - _.callers exists { caller => - val m = caller.caller - keys((m.organization, m.name)) - } - } - } - } - lazy val result0 = acc ++ notCalled ++ called ++ extra - def warnCircular(): Unit = { - log.warn(s"""unexpected circular dependency while using cached resolution: ${cs.mkString(",")}""") - } - (if (n > guard) { - warnCircular - result0 - } else if (called.isEmpty) result0 - else if (notCalled.isEmpty) { - warnCircular - sortModules(cs.tail, acc, extra :+ cs.head, n + 1, guard) - } else sortModules(called, acc ++ notCalled, extra, 0, called.size * called.size + 1)) - } - def resolveConflicts(cs: List[(String, String)], - allModules: Map[(String, String), Vector[OrganizationArtifactReport]]): List[OrganizationArtifactReport] = - cs match { - case Nil => Nil - case (organization, name) :: rest => - val reports = allModules((organization, name)) - reports match { - case Vector() => resolveConflicts(rest, allModules) - case Vector(oa) if (oa.modules.isEmpty) => resolveConflicts(rest, allModules) - case Vector(oa) if (oa.modules.size == 1 && !oa.modules.head.evicted) => - log.debug(s":: no conflict $rootModuleConf: ${oa.organization}:${oa.name}") - oa :: resolveConflicts(rest, allModules) - case oas => - (mergeModuleReports(rootModuleConf, oas flatMap { _.modules }, os, log) match { - case (survivor, newlyEvicted) => - val evicted = (survivor ++ newlyEvicted) filter { m => m.evicted } - val notEvicted = (survivor ++ newlyEvicted) filter { m => !m.evicted } - log.debug("::: adds " + (notEvicted map { _.module }).mkString(", ")) - log.debug("::: evicted " + (evicted map { _.module }).mkString(", ")) - val x = new OrganizationArtifactReport(organization, name, survivor ++ newlyEvicted) - val nextModules = transitivelyEvict(rootModuleConf, rest, allModules, evicted, log) - x :: resolveConflicts(rest, nextModules) - }) - } - } - val guard0 = (orgNamePairs.size * orgNamePairs.size) + 1 - val sorted: Vector[(String, String)] = sortModules(orgNamePairs, Vector(), Vector(), 0, guard0) - val sortedStr = (sorted map { case (o, n) => s"$o:$n" }).mkString(", ") - log.debug(s":: sort result: $sortedStr") - val result = resolveConflicts(sorted.toList, allModules0) - result.toVector - } - /** - * Merges ModuleReports, which represents orgnization, name, and version. - * Returns a touple of (surviving modules ++ non-conflicting modules, newly evicted modules). - */ - def mergeModuleReports(rootModuleConf: String, modules: Seq[ModuleReport], os: Vector[IvyOverride], log: Logger): (Vector[ModuleReport], Vector[ModuleReport]) = - { - if (modules.nonEmpty) { - log.debug(s":: merging module reports for $rootModuleConf: ${modules.head.module.organization}:${modules.head.module.name}") - } - def mergeModuleReports(org: String, name: String, version: String, xs: Seq[ModuleReport]): ModuleReport = { - val completelyEvicted = xs forall { _.evicted } - val allCallers = xs flatMap { _.callers } - // Caller info is often repeated across the subprojects. We only need ModuleID info for later, so xs.head is ok. - val distinctByModuleId = allCallers.groupBy({ _.caller }).toList map { case (k, xs) => xs.head } - val allArtifacts = (xs flatMap { _.artifacts }).distinct - xs.head.copy(artifacts = allArtifacts, evicted = completelyEvicted, callers = distinctByModuleId) - } - val merged = (modules groupBy { m => (m.module.organization, m.module.name, m.module.revision) }).toSeq.toVector flatMap { - case ((org, name, version), xs) => - if (xs.size < 2) xs - else Vector(mergeModuleReports(org, name, version, xs)) - } - val conflicts = merged filter { m => !m.evicted && m.problem.isEmpty } - if (conflicts.size < 2) (merged, Vector()) - else resolveConflict(rootModuleConf, conflicts, os, log) match { - case (survivor, evicted) => - (survivor ++ (merged filter { m => m.evicted || m.problem.isDefined }), evicted) - } - } - /** - * This transitively evicts any non-evicted modules whose only callers are newly evicted. - */ - def transitivelyEvict(rootModuleConf: String, pairs: List[(String, String)], - reports0: Map[(String, String), Vector[OrganizationArtifactReport]], - evicted0: Vector[ModuleReport], log: Logger): Map[(String, String), Vector[OrganizationArtifactReport]] = - { - val em = (evicted0 map { _.module }).toSet - def isTransitivelyEvicted(mr: ModuleReport): Boolean = - mr.callers forall { c => em(c.caller) } - val reports: Seq[((String, String), Vector[OrganizationArtifactReport])] = reports0.toSeq flatMap { - case (k, v) if !(pairs contains k) => Seq() - case ((organization, name), oars0) => - val oars = oars0 map { oar => - val (affected, unaffected) = oar.modules partition { mr => - val x = !mr.evicted && mr.problem.isEmpty && isTransitivelyEvicted(mr) - if (x) { - log.debug(s""":::: transitively evicted $rootModuleConf: ${mr.module}""") - } - x - } - val newlyEvicted = affected map { _.copy(evicted = true, evictedReason = Some("transitive-evict")) } - if (affected.isEmpty) oar - else new OrganizationArtifactReport(organization, name, unaffected ++ newlyEvicted) - } - Seq(((organization, name), oars)) - } - Map(reports: _*) - } - /** - * resolves dependency resolution conflicts in which multiple candidates are found for organization+name combos. - * The main input is conflicts, which is a Vector of ModuleReport, which contains full info on the modulerevision, including its callers. - * Conflict resolution could be expensive, so this is first cached to `cachedResolutionResolveCache` if the conflict is between 2 modules. - * Otherwise, the default "latest" resolution takes the following precedence: - * 1. overrides passed in to `os`. - * 2. diretly forced dependency within the artificial module. - * 3. latest revision. - * Note transitively forced dependencies are not respected. This seems to be the case for stock Ivy's behavior as well, - * which may be because Ivy makes all Maven dependencies as forced="true". - */ - def resolveConflict(rootModuleConf: String, conflicts: Vector[ModuleReport], os: Vector[IvyOverride], log: Logger): (Vector[ModuleReport], Vector[ModuleReport]) = - { - import org.apache.ivy.plugins.conflict.{ NoConflictManager, StrictConflictManager, LatestConflictManager } - val head = conflicts.head - val organization = head.module.organization - val name = head.module.name - log.debug(s"::: resolving conflict in $rootModuleConf:$organization:$name " + (conflicts map { _.module }).mkString("(", ", ", ")")) - def useLatest(lcm: LatestConflictManager): (Vector[ModuleReport], Vector[ModuleReport], String) = - (conflicts find { m => - m.callers.exists { _.isDirectlyForceDependency } - }) match { - case Some(m) => - log.debug(s"- directly forced dependency: $m ${m.callers}") - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some("direct-force")) }, "direct-force") - case None => - (conflicts find { m => - m.callers.exists { _.isForceDependency } - }) match { - // Ivy translates pom.xml dependencies to forced="true", so transitive force is broken. - case Some(m) if !ignoreTransitiveForce => - log.debug(s"- transitively forced dependency: $m ${m.callers}") - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some("transitive-force")) }, "transitive-force") - case _ => - val strategy = lcm.getStrategy - val infos = conflicts map { ModuleReportArtifactInfo(_) } - Option(strategy.findLatest(infos.toArray, None.orNull)) match { - case Some(ModuleReportArtifactInfo(m)) => - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some(lcm.toString)) }, lcm.toString) - case _ => (conflicts, Vector(), lcm.toString) - } - } - } - def doResolveConflict: (Vector[ModuleReport], Vector[ModuleReport], String) = - os find { ovr => ovr.moduleId.getOrganisation == organization && ovr.moduleId.getName == name } match { - case Some(ovr) if Option(ovr.ddm.getVersion).isDefined => - val ovrVersion = ovr.ddm.getVersion - conflicts find { mr => - mr.module.revision == ovrVersion - } match { - case Some(m) => - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some("override")) }, "override") - case None => - sys.error(s"override dependency specifies $ovrVersion but no candidates were found: " + (conflicts map { _.module }).mkString("(", ", ", ")")) - } - case None => - getSettings.getConflictManager(IvyModuleId.newInstance(organization, name)) match { - case ncm: NoConflictManager => (conflicts, Vector(), ncm.toString) - case _: StrictConflictManager => sys.error((s"conflict was found in $rootModuleConf:$organization:$name " + (conflicts map { _.module }).mkString("(", ", ", ")"))) - case lcm: LatestConflictManager => useLatest(lcm) - case conflictManager => sys.error(s"Unsupported conflict manager $conflictManager") - } - } - if (conflicts.size == 2 && os.isEmpty) { - val (cf0, cf1) = (conflicts(0).module, conflicts(1).module) - val cache = cachedResolutionResolveCache - val (surviving, evicted) = cache.getOrElseUpdateConflict(cf0, cf1, conflicts) { doResolveConflict } - (surviving, evicted) - } else { - val (surviving, evicted, mgr) = doResolveConflict - (surviving, evicted) - } - } - def remapInternalProject(node: IvyNode, ur: UpdateReport, - md0: ModuleDescriptor, dd: DependencyDescriptor, - os: Vector[IvyOverride], log: Logger): UpdateReport = - { - def parentConfigs(c: String): Vector[String] = - Option(md0.getConfiguration(c)) match { - case Some(config) => - config.getExtends.toVector ++ - (config.getExtends.toVector flatMap parentConfigs) - case None => Vector() - } - // These are the configurations from the original project we want to resolve. - val rootModuleConfs = md0.getConfigurations.toVector - val configurations0 = ur.configurations.toVector - // This is how md looks from md0 via dd's mapping. - val remappedConfigs0: Map[String, Vector[String]] = Map(rootModuleConfs map { conf0 => - val remapped: Vector[String] = dd.getDependencyConfigurations(conf0.getName).toVector flatMap { conf => - node.getRealConfs(conf).toVector - } - conf0.getName -> remapped - }: _*) - // This emulates test-internal extending test configuration etc. - val remappedConfigs: Map[String, Vector[String]] = - (remappedConfigs0 /: rootModuleConfs) { (acc0, c) => - val ps = parentConfigs(c.getName) - (acc0 /: ps) { (acc, parent) => - val vs0 = acc.getOrElse(c.getName, Vector()) - val vs = acc.getOrElse(parent, Vector()) - acc.updated(c.getName, (vs0 ++ vs).distinct) - } - } - log.debug(s"::: remapped configs $remappedConfigs") - val configurations = rootModuleConfs map { conf0 => - val remappedCRs = configurations0 filter { cr => - remappedConfigs(conf0.getName) contains cr.configuration - } - mergeConfigurationReports(conf0.getName, remappedCRs, os, log) - } - new UpdateReport(ur.cachedDescriptor, configurations, ur.stats, ur.stamps) - } -} - -private[sbt] case class ModuleReportArtifactInfo(moduleReport: ModuleReport) extends IvyArtifactInfo { - override def getLastModified: Long = moduleReport.publicationDate map { _.getTime } getOrElse 0L - override def getRevision: String = moduleReport.module.revision -} -private[sbt] case class IvyOverride(moduleId: IvyModuleId, pm: PatternMatcher, ddm: OverrideDependencyDescriptorMediator) { - override def toString: String = s"""IvyOverride($moduleId,$pm,${ddm.getVersion},${ddm.getBranch})""" -} diff --git a/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala b/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala deleted file mode 100644 index e3e41148e..000000000 --- a/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala +++ /dev/null @@ -1,11 +0,0 @@ -package sbt -package ivyint - -import org.apache.ivy.plugins.resolver.DependencyResolver - -// These are placeholder traits for sbt-aether-resolver -trait CustomMavenResolver extends DependencyResolver { -} -trait CustomRemoteMavenResolver extends CustomMavenResolver { - def repo: MavenRepository -} diff --git a/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala b/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala deleted file mode 100644 index 5d067d313..000000000 --- a/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala +++ /dev/null @@ -1,129 +0,0 @@ -package sbt -package ivyint - -import java.lang.reflect.Field -import java.lang.reflect.Method -import java.net.Authenticator -import java.net.PasswordAuthentication -import org.apache.ivy.util.Credentials -import org.apache.ivy.util.Message -import org.apache.ivy.util.url.IvyAuthenticator -import org.apache.ivy.util.url.CredentialsStore - -/** - * Helper to install an Authenticator that works with the IvyAuthenticator to provide better error messages when - * credentials don't line up. - */ -object ErrorMessageAuthenticator { - private var securityWarningLogged = false - - private def originalAuthenticator: Option[Authenticator] = { - try { - val f = classOf[Authenticator].getDeclaredField("theAuthenticator"); - f.setAccessible(true); - Option(f.get(null).asInstanceOf[Authenticator]) - } catch { - // TODO - Catch more specific errors. - case t: Throwable => - Message.debug("Error occurred while getting the original authenticator: " + t.getMessage) - None - } - } - - private lazy val ivyOriginalField = { - val field = classOf[IvyAuthenticator].getDeclaredField("original") - field.setAccessible(true) - field - } - // Attempts to get the original authenticator form the ivy class or returns null. - private def installIntoIvy(ivy: IvyAuthenticator): Option[Authenticator] = { - // Here we install ourselves as the IvyAuthenticator's default so we get called AFTER Ivy has a chance to run. - def installIntoIvyImpl(original: Option[Authenticator]): Unit = { - val newOriginal = new ErrorMessageAuthenticator(original) - ivyOriginalField.set(ivy, newOriginal) - } - - try Option(ivyOriginalField.get(ivy).asInstanceOf[Authenticator]) match { - case Some(alreadyThere: ErrorMessageAuthenticator) => // We're already installed, no need to do the work again. - case originalOpt => installIntoIvyImpl(originalOpt) - } catch { - case t: Throwable => - Message.debug("Error occurred will trying to install debug messages into Ivy Authentication" + t.getMessage) - } - Some(ivy) - } - - /** Installs the error message authenticator so we have nicer error messages when using java's URL for downloading. */ - def install(): Unit = { - // Actually installs the error message authenticator. - def doInstall(original: Option[Authenticator]): Unit = - try Authenticator.setDefault(new ErrorMessageAuthenticator(original)) - catch { - case e: SecurityException if !securityWarningLogged => - securityWarningLogged = true; - Message.warn("Not enough permissions to set the ErorrMessageAuthenticator. " - + "Helpful debug messages disabled!"); - } - // We will try to use the original authenticator as backup authenticator. - // Since there is no getter available, so try to use some reflection to - // obtain it. If that doesn't work, assume there is no original authenticator - def doInstallIfIvy(original: Option[Authenticator]): Unit = - original match { - case Some(installed: ErrorMessageAuthenticator) => // Ignore, we're already installed - case Some(ivy: IvyAuthenticator) => installIntoIvy(ivy) - case original => doInstall(original) - } - doInstallIfIvy(originalAuthenticator) - } -} -/** - * An authenticator which just delegates to a previous authenticator and issues *nice* - * error messages on failure to find credentials. - * - * Since ivy installs its own credentials handler EVERY TIME it resolves or publishes, we want to - * install this one at some point and eventually ivy will capture it and use it. - */ -private[sbt] final class ErrorMessageAuthenticator(original: Option[Authenticator]) extends Authenticator { - - protected override def getPasswordAuthentication(): PasswordAuthentication = { - // We're guaranteed to only get here if Ivy's authentication fails - if (!isProxyAuthentication) { - val host = getRequestingHost - // TODO - levenshtein distance "did you mean" message. - Message.error(s"Unable to find credentials for [${getRequestingPrompt} @ ${host}].") - val configuredRealms = IvyCredentialsLookup.realmsForHost.getOrElse(host, Set.empty) - if (configuredRealms.nonEmpty) { - Message.error(s" Is one of these realms mispelled for host [${host}]:") - configuredRealms foreach { realm => - Message.error(s" * ${realm}") - } - } - } - // TODO - Maybe we should work on a helpful proxy message... - - // TODO - To be more maven friendly, we may want to also try to grab the "first" authentication that shows up for a server and try it. - // or maybe allow that behavior to be configured, since maven users aren't used to realms (which they should be). - - // Grabs the authentication that would have been provided had we not been installed... - def originalAuthentication: Option[PasswordAuthentication] = { - Authenticator.setDefault(original.getOrElse(null)) - try Option(Authenticator.requestPasswordAuthentication( - getRequestingHost, - getRequestingSite, - getRequestingPort, - getRequestingProtocol, - getRequestingPrompt, - getRequestingScheme)) - finally Authenticator.setDefault(this) - } - originalAuthentication.orNull - } - - /** - * Returns true if this authentication if for a proxy and not for an HTTP server. - * We want to display different error messages, depending. - */ - private def isProxyAuthentication: Boolean = - getRequestorType == Authenticator.RequestorType.PROXY - -} diff --git a/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala b/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala deleted file mode 100644 index aeef4d428..000000000 --- a/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala +++ /dev/null @@ -1,64 +0,0 @@ -package sbt -package ivyint - -import org.apache.ivy.util.url.CredentialsStore -import collection.JavaConverters._ - -/** A key used to store credentials in the ivy credentials store. */ -private[sbt] sealed trait CredentialKey -/** Represents a key in the ivy credentials store that is only specific to a host. */ -private[sbt] case class Host(name: String) extends CredentialKey -/** Represents a key in the ivy credentials store that is keyed to both a host and a "realm". */ -private[sbt] case class Realm(host: String, realm: String) extends CredentialKey - -/** - * Helper mechanism to improve credential related error messages. - * - * This evil class exposes to us the necessary information to warn on credential failure and offer - * spelling/typo suggestions. - */ -private[sbt] object IvyCredentialsLookup { - - /** Helper extractor for Ivy's key-value store of credentials. */ - private object KeySplit { - def unapply(key: String): Option[(String, String)] = { - key.indexOf('@') match { - case -1 => None - case n => Some(key.take(n) -> key.drop(n + 1)) - } - } - } - - /** - * Here we cheat runtime private so we can look in the credentials store. - * - * TODO - Don't bomb at class load time... - */ - private val credKeyringField = { - val tmp = classOf[CredentialsStore].getDeclaredField("KEYRING") - tmp.setAccessible(true) - tmp - } - - /** All the keys for credentials in the ivy configuration store. */ - def keyringKeys: Set[CredentialKey] = { - val map = credKeyringField.get(null).asInstanceOf[java.util.HashMap[String, Any]] - // make a clone of the set... - (map.keySet.asScala.map { - case KeySplit(realm, host) => Realm(host, realm) - case host => Host(host) - })(collection.breakOut) - } - - /** - * A mapping of host -> realms in the ivy credentials store. - */ - def realmsForHost: Map[String, Set[String]] = - keyringKeys collect { - case x: Realm => x - } groupBy { realm => - realm.host - } mapValues { realms => - realms map (_.realm) - } -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala b/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala deleted file mode 100644 index 76bb4d2af..000000000 --- a/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala +++ /dev/null @@ -1,134 +0,0 @@ -package sbt -package ivyint - -import java.io.File -import java.net.URI -import java.util.{ Collection, Collections => CS } -import CS.singleton - -import org.apache.ivy.{ core, plugins, util, Ivy } -import core.module.descriptor.{ DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor } -import core.module.descriptor.{ DefaultDependencyDescriptor => DDD, DependencyDescriptor } -import core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } -import plugins.namespace.Namespace -import util.extendable.ExtendableItem - -private[sbt] object MergeDescriptors { - def mergeable(a: DependencyDescriptor, b: DependencyDescriptor): Boolean = - a.isForce == b.isForce && - a.isChanging == b.isChanging && - a.isTransitive == b.isTransitive && - a.getParentRevisionId == b.getParentRevisionId && - a.getNamespace == b.getNamespace && { - val amrid = a.getDependencyRevisionId - val bmrid = b.getDependencyRevisionId - amrid == bmrid - } && { - val adyn = a.getDynamicConstraintDependencyRevisionId - val bdyn = b.getDynamicConstraintDependencyRevisionId - adyn == bdyn - } - - def apply(a: DependencyDescriptor, b: DependencyDescriptor): DependencyDescriptor = - { - assert(mergeable(a, b)) - new MergedDescriptors(a, b) - } -} - -// combines the artifacts, configurations, includes, and excludes for DependencyDescriptors `a` and `b` -// that otherwise have equal IDs -private final class MergedDescriptors(a: DependencyDescriptor, b: DependencyDescriptor) extends DependencyDescriptor { - def getDependencyId = a.getDependencyId - def isForce = a.isForce - def isChanging = a.isChanging - def isTransitive = a.isTransitive - def getNamespace = a.getNamespace - def getParentRevisionId = a.getParentRevisionId - def getDependencyRevisionId = a.getDependencyRevisionId - def getDynamicConstraintDependencyRevisionId = a.getDynamicConstraintDependencyRevisionId - - def getModuleConfigurations = concat(a.getModuleConfigurations, b.getModuleConfigurations) - - def getDependencyConfigurations(moduleConfiguration: String, requestedConfiguration: String) = - concat(a.getDependencyConfigurations(moduleConfiguration, requestedConfiguration), b.getDependencyConfigurations(moduleConfiguration)) - - def getDependencyConfigurations(moduleConfiguration: String) = - concat(a.getDependencyConfigurations(moduleConfiguration), b.getDependencyConfigurations(moduleConfiguration)) - - def getDependencyConfigurations(moduleConfigurations: Array[String]) = - concat(a.getDependencyConfigurations(moduleConfigurations), b.getDependencyConfigurations(moduleConfigurations)) - - def getAllDependencyArtifacts = concatArtifacts(a, a.getAllDependencyArtifacts, b, b.getAllDependencyArtifacts) - - def getDependencyArtifacts(moduleConfigurations: String) = - concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations)) - - def getDependencyArtifacts(moduleConfigurations: Array[String]) = - concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations)) - - def getAllIncludeRules = concat(a.getAllIncludeRules, b.getAllIncludeRules) - - def getIncludeRules(moduleConfigurations: String) = - concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations)) - - def getIncludeRules(moduleConfigurations: Array[String]) = - concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations)) - - private[this] def concatArtifacts(a: DependencyDescriptor, as: Array[DependencyArtifactDescriptor], b: DependencyDescriptor, bs: Array[DependencyArtifactDescriptor]) = - { - if (as.isEmpty) - if (bs.isEmpty) as - else defaultArtifact(a) +: explicitConfigurations(b, bs) - else if (bs.isEmpty) explicitConfigurations(a, as) :+ defaultArtifact(b) - else concat(explicitConfigurations(a, as), explicitConfigurations(b, bs)) - } - private[this] def explicitConfigurations(base: DependencyDescriptor, arts: Array[DependencyArtifactDescriptor]): Array[DependencyArtifactDescriptor] = - arts map { art => explicitConfigurations(base, art) } - private[this] def explicitConfigurations(base: DependencyDescriptor, art: DependencyArtifactDescriptor): DependencyArtifactDescriptor = - { - val aConfs = art.getConfigurations - if (aConfs == null || aConfs.isEmpty) - copyWithConfigurations(art, base.getModuleConfigurations) - else - art - } - private[this] def defaultArtifact(a: DependencyDescriptor): DependencyArtifactDescriptor = - { - val dd = new DefaultDependencyArtifactDescriptor(a, a.getDependencyRevisionId.getName, "jar", "jar", null, null) - addConfigurations(dd, a.getModuleConfigurations) - dd - } - private[this] def copyWithConfigurations(dd: DependencyArtifactDescriptor, confs: Seq[String]): DependencyArtifactDescriptor = - { - val dextra = dd.getQualifiedExtraAttributes - val newd = new DefaultDependencyArtifactDescriptor(dd.getDependencyDescriptor, dd.getName, dd.getType, dd.getExt, dd.getUrl, dextra) - addConfigurations(newd, confs) - newd - } - private[this] def addConfigurations(dd: DefaultDependencyArtifactDescriptor, confs: Seq[String]): Unit = - confs foreach dd.addConfiguration - - private[this] def concat[T: reflect.ClassTag](a: Array[T], b: Array[T]): Array[T] = (a ++ b).distinct - - def getAllExcludeRules = concat(a.getAllExcludeRules, b.getAllExcludeRules) - - def getExcludeRules(moduleConfigurations: String) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations)) - - def getExcludeRules(moduleConfigurations: Array[String]) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations)) - - def doesExclude(moduleConfigurations: Array[String], artifactId: ArtifactId) = a.doesExclude(moduleConfigurations, artifactId) || b.doesExclude(moduleConfigurations, artifactId) - - def canExclude = a.canExclude || b.canExclude - - def asSystem = this - - def clone(revision: ModuleRevisionId) = new MergedDescriptors(a.clone(revision), b.clone(revision)) - - def getAttribute(name: String): String = a.getAttribute(name) - def getAttributes = a.getAttributes - def getExtraAttribute(name: String) = a.getExtraAttribute(name) - def getExtraAttributes = a.getExtraAttributes - def getQualifiedExtraAttributes = a.getQualifiedExtraAttributes - def getSourceModule = a.getSourceModule -} diff --git a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala b/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala deleted file mode 100644 index 6b1f59a15..000000000 --- a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala +++ /dev/null @@ -1,305 +0,0 @@ -package sbt -package ivyint - -import java.io.File -import java.text.ParseException -import java.util.Date - -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.core.{ IvyContext, LogOptions } -import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultModuleDescriptor, ModuleDescriptor, DependencyDescriptor } -import org.apache.ivy.core.resolve.{ ResolvedModuleRevision, ResolveData } -import org.apache.ivy.plugins.latest.LatestStrategy -import org.apache.ivy.plugins.repository.file.{ FileRepository => IFileRepository, FileResource } -import org.apache.ivy.plugins.repository.url.URLResource -import org.apache.ivy.plugins.resolver._ -import org.apache.ivy.plugins.resolver.util.{ HasLatestStrategy, ResolvedResource } -import org.apache.ivy.util.{ Message, StringUtils => IvyStringUtils } - -private[sbt] case class SbtChainResolver( - name: String, - resolvers: Seq[DependencyResolver], - settings: IvySettings, - updateOptions: UpdateOptions, - log: Logger) extends ChainResolver { - - override def equals(o: Any): Boolean = o match { - case o: SbtChainResolver => - this.name == o.name && - this.resolvers == o.resolvers && - this.settings == o.settings && - this.updateOptions == o.updateOptions - case _ => false - } - - override def hashCode: Int = - { - var hash = 1 - hash = hash * 31 + this.name.## - hash = hash * 31 + this.resolvers.## - hash = hash * 31 + this.settings.## - hash = hash * 31 + this.updateOptions.## - hash - } - - // TODO - We need to special case the project resolver so it always "wins" when resolving with inter-project dependencies. - - // Initialize ourselves. - setName(name) - setReturnFirst(true) - setCheckmodified(false) - // Here we append all the resolvers we were passed *AND* look for - // a project resolver, which we will special-case. - resolvers.foreach(add) - - // Technically, this should be applied to module configurations. - // That would require custom subclasses of all resolver types in ConvertResolver (a delegation approach does not work). - // It would be better to get proper support into Ivy. - // A workaround is to configure the ModuleConfiguration resolver to be a ChainResolver. - // - // This method is only used by the pom parsing code in Ivy to find artifacts it doesn't know about. - // In particular, a) it looks up source and javadoc classifiers b) it looks up a main artifact for packaging="pom" - // sbt now provides the update-classifiers or requires explicitly specifying classifiers explicitly - // Providing a main artifact for packaging="pom" does not seem to be correct and the lookup can be expensive. - // - // Ideally this could just skip the lookup, but unfortunately several artifacts in practice do not follow the - // correct behavior for packaging="pom" and so it is only skipped for source/javadoc classifiers. - override def locate(artifact: IArtifact) = if (IvySbt.hasImplicitClassifier(artifact)) null else super.locate(artifact) - - override def getDependency(dd: DependencyDescriptor, data: ResolveData) = - { - if (data.getOptions.getLog != LogOptions.LOG_QUIET) - Message.info("Resolving " + dd.getDependencyRevisionId + " ...") - val gd = doGetDependency(dd, data) - val mod = IvySbt.resetArtifactResolver(gd) - mod - } - // Modified implementation of ChainResolver#getDependency. - // When the dependency is changing, it will check all resolvers on the chain - // regardless of what the "latest strategy" is set, and look for the published date - // or the module descriptor to sort them. - // This implementation also skips resolution if "return first" is set to true, - // and if a previously resolved or cached revision has been found. - def doGetDependency(dd: DependencyDescriptor, data0: ResolveData): ResolvedModuleRevision = - { - // useLatest - Means we should always download the JARs from the internet, no matter what. - // This will only be true *IF* the depenendency is dynamic/changing *and* latestSnapshots is true. - // If you find multiple candidates, - // - If `isReturnFirst` is true, you return the first value found - // - If not, we will ATTEMPT to look at the publish date, which is not correctly discovered for Maven modules and - // leads to undefined behavior. - val useLatest = (dd.isChanging || IvySbt.isChanging(dd.getDependencyRevisionId)) && updateOptions.latestSnapshots - if (useLatest) { - Message.verbose(s"$getName is changing. Checking all resolvers on the chain") - } - val data = new ResolveData(data0, doValidate(data0)) - // Returns the value if we've already been resolved from some other branch of the resolution tree. - val resolved = Option(data.getCurrentResolvedModuleRevision) - // If we don't have any previously resolved date, we try to pull the value from the cache. - val resolvedOrCached = - resolved orElse { - Message.verbose(getName + ": Checking cache for: " + dd) - Option(findModuleInCache(dd, data, true)) map { mr => - Message.verbose(getName + ": module revision found in cache: " + mr.getId) - forcedRevision(mr) - } - } - - // Default value for resolution. We use this while we loop... - // If useLatest is true, we want to try to download from the internet so we DO NOT start with a valid value. - var temp: Option[ResolvedModuleRevision] = - if (useLatest) None - else resolvedOrCached - // Cast resolvers to something useful. TODO - we dropping anything here? - val resolvers = getResolvers.toArray.toVector collect { case x: DependencyResolver => x } - - // Here we do an attempt to resolve the artifact from each of the resolvers in the chain. - // - If we have a return value already, AND isReturnFirst is true AND useLatest is false, we DO NOT resolve anything - // - If we do not, try to resolve. - // RETURNS: Left -> Error - // Right -> Some(resolved module) // Found in this resolver, can use this result. - // Right -> None // Do not use this resolver - val results = resolvers map { x => - // if the revision is cached and isReturnFirst is set, don't bother hitting any resolvers, just return None for this guy. - if (isReturnFirst && temp.isDefined && !useLatest) Right(None) - else { - // We actually do resolution. - val resolver = x - val oldLatest: Option[LatestStrategy] = setLatestIfRequired(resolver, Option(getLatestStrategy)) - try { - val previouslyResolved = temp - // if the module qualifies as changing, then resolve all resolvers - if (useLatest) data.setCurrentResolvedModuleRevision(null) - else data.setCurrentResolvedModuleRevision(temp.orNull) - temp = Option(resolver.getDependency(dd, data)) - Right( - if (temp eq previouslyResolved) None - else if (useLatest) temp map { x => - (reparseModuleDescriptor(dd, data, resolver, x), resolver) - } - else temp map { x => (forcedRevision(x), resolver) } - ) - } catch { - case ex: Exception => - Message.verbose("problem occurred while resolving " + dd + " with " + resolver - + ": " + IvyStringUtils.getStackTrace(ex)) - Left(ex) - } finally { - oldLatest map { _ => doSetLatestStrategy(resolver, oldLatest) } - checkInterrupted() - } - } - } - val errors = results collect { case Left(e) => e } - val foundRevisions: Vector[(ResolvedModuleRevision, DependencyResolver)] = results collect { case Right(Some(x)) => x } - val sorted = - if (useLatest) (foundRevisions.sortBy { - case (rmr, resolver) => - Message.warn(s"Sorrting results from $rmr, using ${rmr.getPublicationDate} and ${rmr.getDescriptor.getPublicationDate}") - // Just issue warning about issues with publication date, and fake one on it for now. - Option(rmr.getPublicationDate) orElse Option(rmr.getDescriptor.getPublicationDate) match { - case None => - (resolver.findIvyFileRef(dd, data), rmr.getDescriptor) match { - case (null, _) => - // In this instance, the dependency is specified by a direct URL or some other sort of "non-ivy" file - if (dd.isChanging) - Message.warn(s"Resolving a changing dependency (${rmr.getId}) with no ivy/pom file!, resolution order is undefined!") - 0L - case (ivf, dmd: DefaultModuleDescriptor) => - val lmd = new java.util.Date(ivf.getLastModified) - Message.debug(s"Getting no publication date from resolver: ${resolver} for ${rmr.getId}, setting to: ${lmd}") - dmd.setPublicationDate(lmd) - ivf.getLastModified - case _ => - Message.warn(s"Getting null publication date from resolver: ${resolver} for ${rmr.getId}, resolution order is undefined!") - 0L - } - case Some(date) => // All other cases ok - date.getTime - } - }).reverse.headOption map { - case (rmr, resolver) => - Message.warn(s"Choosing $resolver for ${rmr.getId}") - // Now that we know the real latest revision, let's force Ivy to use it - val artifactOpt = findFirstArtifactRef(rmr.getDescriptor, dd, data, resolver) - artifactOpt match { - case None if resolver.getName == "inter-project" => // do nothing - case None if resolver.isInstanceOf[CustomMavenResolver] => - // do nothing for now.... - // We want to see if the maven caching is sufficient and we do not need to duplicate within the ivy cache... - case None => throw new RuntimeException(s"\t${resolver.getName}: no ivy file nor artifact found for $rmr") - case Some(artifactRef) => - val systemMd = toSystem(rmr.getDescriptor) - getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef, - toSystem(dd), systemMd.getAllArtifacts.head, None.orNull, getCacheOptions(data)) - } - rmr - } - else foundRevisions.reverse.headOption map { _._1 } // we have to reverse because resolvers are hit in reverse order. - // If the value is arleady in cache, SORTED will be a Seq(None, None, ...) which means we'll fall over to the prevously cached or resolved version. - val mrOpt: Option[ResolvedModuleRevision] = sorted orElse resolvedOrCached - mrOpt match { - case None if errors.size == 1 => - errors.head match { - case e: RuntimeException => throw e - case e: ParseException => throw e - case e: Throwable => throw new RuntimeException(e.toString, e) - } - case None if errors.size > 1 => - val err = (errors.toList map { IvyStringUtils.getErrorMessage }).mkString("\n\t", "\n\t", "\n") - throw new RuntimeException(s"several problems occurred while resolving $dd:$err") - case _ => - if (resolved == mrOpt) resolved.orNull - else (mrOpt map { resolvedRevision }).orNull - } - } - // Ivy seem to not want to use the module descriptor found at the latest resolver - private[this] def reparseModuleDescriptor(dd: DependencyDescriptor, data: ResolveData, resolver: DependencyResolver, rmr: ResolvedModuleRevision): ResolvedModuleRevision = - // TODO - Redownloading/parsing the ivy file is not really the best way to make this correct. - // We should figure out a better alternative, or directly attack the resolvers Ivy uses to - // give them correct behavior around -SNAPSHOT. - Option(resolver.findIvyFileRef(dd, data)) flatMap { ivyFile => - ivyFile.getResource match { - case r: FileResource => - try { - val parser = rmr.getDescriptor.getParser - val md = parser.parseDescriptor(settings, r.getFile.toURI.toURL, r, false) - Some(new ResolvedModuleRevision(resolver, resolver, md, rmr.getReport, true)) - } catch { - case _: ParseException => None - } - case _ => None - } - } getOrElse { - Message.warn(s"Unable to reparse ${dd.getDependencyRevisionId} from $resolver, using ${rmr.getPublicationDate}") - rmr - } - /** Ported from BasicResolver#findFirstAirfactRef. */ - private[this] def findFirstArtifactRef(md: ModuleDescriptor, dd: DependencyDescriptor, data: ResolveData, resolver: DependencyResolver): Option[ResolvedResource] = - { - def artifactRef(artifact: IArtifact, date: Date): Option[ResolvedResource] = - resolver match { - case resolver: BasicResolver => - IvyContext.getContext.set(resolver.getName + ".artifact", artifact) - try { - Option(resolver.doFindArtifactRef(artifact, date)) orElse { - Option(artifact.getUrl) map { url => - Message.verbose("\tusing url for " + artifact + ": " + url) - val resource = - if ("file" == url.getProtocol) new FileResource(new IFileRepository(), new File(url.getPath)) - else new URLResource(url) - new ResolvedResource(resource, artifact.getModuleRevisionId.getRevision) - } - } - } finally { - IvyContext.getContext.set(resolver.getName + ".artifact", null) - } - case _ => - None - } - val artifactRefs = md.getConfigurations.toIterator flatMap { conf => - md.getArtifacts(conf.getName).toIterator flatMap { af => - artifactRef(af, data.getDate).toIterator - } - } - if (artifactRefs.hasNext) Some(artifactRefs.next()) - else None - } - /** Ported from ChainResolver#forcedRevision. */ - private[this] def forcedRevision(rmr: ResolvedModuleRevision): ResolvedModuleRevision = - new ResolvedModuleRevision(rmr.getResolver, rmr.getArtifactResolver, rmr.getDescriptor, rmr.getReport, true) - /** Ported from ChainResolver#resolvedRevision. */ - private[this] def resolvedRevision(rmr: ResolvedModuleRevision): ResolvedModuleRevision = - if (isDual) new ResolvedModuleRevision(rmr.getResolver, this, rmr.getDescriptor, rmr.getReport, rmr.isForce) - else rmr - /** Ported from ChainResolver#setLatestIfRequired. */ - private[this] def setLatestIfRequired(resolver: DependencyResolver, latest: Option[LatestStrategy]): Option[LatestStrategy] = - latestStrategyName(resolver) match { - case Some(latestName) if latestName != "default" => - val oldLatest = latestStrategy(resolver) - doSetLatestStrategy(resolver, latest) - oldLatest - case _ => None - } - /** Ported from ChainResolver#getLatestStrategyName. */ - private[this] def latestStrategyName(resolver: DependencyResolver): Option[String] = - resolver match { - case r: HasLatestStrategy => Some(r.getLatest) - case _ => None - } - /** Ported from ChainResolver#getLatest. */ - private[this] def latestStrategy(resolver: DependencyResolver): Option[LatestStrategy] = - resolver match { - case r: HasLatestStrategy => Some(r.getLatestStrategy) - case _ => None - } - /** Ported from ChainResolver#setLatest. */ - private[this] def doSetLatestStrategy(resolver: DependencyResolver, latest: Option[LatestStrategy]): Option[LatestStrategy] = - resolver match { - case r: HasLatestStrategy => - val oldLatest = latestStrategy(resolver) - r.setLatestStrategy(latest.orNull) - oldLatest - case _ => None - } -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/ivyint/SbtDefaultDependencyDescriptor.scala b/ivy/src/main/scala/sbt/ivyint/SbtDefaultDependencyDescriptor.scala deleted file mode 100644 index 0a3338fac..000000000 --- a/ivy/src/main/scala/sbt/ivyint/SbtDefaultDependencyDescriptor.scala +++ /dev/null @@ -1,9 +0,0 @@ -package sbt -package ivyint - -import org.apache.ivy.core -import core.module.descriptor.DefaultDependencyDescriptor - -trait SbtDefaultDependencyDescriptor { self: DefaultDependencyDescriptor => - def dependencyModuleId: ModuleID -} diff --git a/ivy/src/main/scala/sbt/mavenint/PomExtraDependencyAttributes.scala b/ivy/src/main/scala/sbt/mavenint/PomExtraDependencyAttributes.scala deleted file mode 100644 index ab87cae75..000000000 --- a/ivy/src/main/scala/sbt/mavenint/PomExtraDependencyAttributes.scala +++ /dev/null @@ -1,111 +0,0 @@ -package sbt.mavenint - -import java.util.Properties -import java.util.regex.Pattern - -import org.apache.ivy.core.module.descriptor.DependencyDescriptor -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.util.extendable.ExtendableItem - -/** - * This class contains all the logic for dealing with the extra attributes in pom files relating to extra attributes - * on dependency declarations. - * - * Specifically, if we have a dependency on an sbt plugin, there are two properties that need to propogate: - * - `sbtVersion` - * - `scalaVersion` - * - * These need to exist on the *dependency declaration*. Maven/Aether has no way to inject these into - * the section of pom files, so we use Ivy's Extra attribute hackery to inject a lookup table - * of extra attributes by dependency id into POM files and later we read these back. - */ -object PomExtraDependencyAttributes { - - val ExtraAttributesKey = "extraDependencyAttributes" - val SbtVersionKey = "sbtVersion" - val ScalaVersionKey = "scalaVersion" - /** - * Reads the extra dependency attributes out of a maven property. - * @param props The properties from an Aether resolution. - * @return - * A map of module id to extra dependency attributes associated with dependencies on that module. - */ - def readFromAether(props: java.util.Map[String, AnyRef]): Map[ModuleRevisionId, Map[String, String]] = { - import scala.collection.JavaConverters._ - (props.asScala get ExtraAttributesKey) match { - case None => Map.empty - case Some(str) => - def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true)) - (for { - (id, props) <- readDependencyExtra(str.toString).map(processDep) - } yield id -> props).toMap - } - } - - /** - * Mutates the to collection with the extra depdendency attributes from the incoming pom properties list. - * - * @param from The properties directly off a maven POM file - * @param to The aaether properties where we can write whatever we want. - * - * TODO - maybe we can just parse this directly here. Note the `readFromAether` method uses - * whatever we set here. - */ - def transferDependencyExtraAttributes(from: Properties, to: java.util.Map[String, AnyRef]): Unit = { - Option(from.getProperty(ExtraAttributesKey, null)) match { - case Some(str) => to.put(ExtraAttributesKey, str) - case None => - } - } - - /** - * Reads the extra dependency information out of Ivy's notion of POM properties and returns - * the map of ID -> Extra Properties. - */ - def getDependencyExtra(m: Map[String, String]): Map[ModuleRevisionId, Map[String, String]] = - (m get ExtraAttributesKey) match { - case None => Map.empty - case Some(str) => - def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true)) - readDependencyExtra(str).map(processDep).toMap - } - - def qualifiedExtra(item: ExtendableItem): Map[String, String] = { - import scala.collection.JavaConverters._ - item.getQualifiedExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap - } - def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] = - (qualifiedExtra(item) filterKeys { k => qualifiedIsExtra(k) == include }) - - def qualifiedIsExtra(k: String): Boolean = k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey) - - // Reduces the id to exclude custom extra attributes - // This makes the id suitable as a key to associate a dependency parsed from a element - // with the extra attributes from the section - def simplify(id: ModuleRevisionId): ModuleRevisionId = { - import scala.collection.JavaConverters._ - ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, filterCustomExtra(id, include = false).asJava) - } - - /** parses the sequence of dependencies with extra attribute information, with one dependency per line */ - def readDependencyExtra(s: String): Seq[ModuleRevisionId] = - LinesP.split(s).map(_.trim).filter(!_.isEmpty).map(ModuleRevisionId.decode) - - private[this] val LinesP = Pattern.compile("(?m)^") - - /** - * Creates the "extra" property values for DependencyDescriptors that can be written into a maven pom - * so we don't loose the information. - * @param s - * @return - */ - def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] = - s.flatMap { dd => - val revId = dd.getDependencyRevisionId - if (filterCustomExtra(revId, include = true).isEmpty) - Nil - else - revId.encodeToString :: Nil - } - -} diff --git a/ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.jar b/ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.jar deleted file mode 100644 index e69de29bb..000000000 diff --git a/ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom b/ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom deleted file mode 100644 index 7884c5684..000000000 --- a/ivy/src/test/resources/test-maven-repo/com/test/test-artifact/1.0.0-SNAPSHOT/test-artifact-1.0.0-SNAPSHOT.pom +++ /dev/null @@ -1,15 +0,0 @@ - - - 4.0.0 - - com.test - test-artifact - 1.0.0-SNAPSHOT - scala-jar - - - UTF-8 - - - diff --git a/ivy/src/test/scala/BaseIvySpecification.scala b/ivy/src/test/scala/BaseIvySpecification.scala deleted file mode 100644 index a8a19ea76..000000000 --- a/ivy/src/test/scala/BaseIvySpecification.scala +++ /dev/null @@ -1,85 +0,0 @@ -package sbt - -import Path._, Configurations._ -import java.io.File -import org.specs2._ -import cross.CrossVersionUtil -import sbt.ivyint.SbtChainResolver - -trait BaseIvySpecification extends Specification { - def currentBase: File = new File(".") - def currentTarget: File = currentBase / "target" / "ivyhome" - def currentManaged: File = currentBase / "target" / "lib_managed" - def currentDependency: File = currentBase / "target" / "dependency" - def defaultModuleId: ModuleID = ModuleID("com.example", "foo", "0.1.0", Some("compile")) - lazy val log = ConsoleLogger() - - def configurations = Seq(Compile, Test, Runtime) - def module(moduleId: ModuleID, deps: Seq[ModuleID], scalaFullVersion: Option[String], - uo: UpdateOptions = UpdateOptions()): IvySbt#Module = { - val ivyScala = scalaFullVersion map { fv => - new IvyScala( - scalaFullVersion = fv, - scalaBinaryVersion = CrossVersionUtil.binaryScalaVersion(fv), - configurations = Nil, - checkExplicit = true, - filterImplicit = false, - overrideScalaVersion = false) - } - - val moduleSetting: ModuleSettings = InlineConfiguration( - module = moduleId, - moduleInfo = ModuleInfo("foo"), - dependencies = deps, - configurations = configurations, - ivyScala = ivyScala) - val ivySbt = new IvySbt(mkIvyConfiguration(uo)) - new ivySbt.Module(moduleSetting) - } - - def resolvers: Seq[Resolver] = Seq(DefaultMavenRepository) - - def chainResolver = ChainedResolver("sbt-chain", resolvers) - - def mkIvyConfiguration(uo: UpdateOptions): IvyConfiguration = { - val paths = new IvyPaths(currentBase, Some(currentTarget)) - val other = Nil - val moduleConfs = Seq(ModuleConfiguration("*", chainResolver)) - val off = false - val check = Nil - val resCacheDir = currentTarget / "resolution-cache" - new InlineIvyConfiguration(paths, resolvers, other, moduleConfs, off, None, check, Some(resCacheDir), uo, log) - } - - def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = { - // IO.delete(currentTarget) - val retrieveConfig = new RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern, false) - val config = new UpdateConfiguration(Some(retrieveConfig), false, UpdateLogging.Full) - IvyActions.updateEither(module, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, Some(currentDependency), log) - } - - def cleanIvyCache(): Unit = IO.delete(currentTarget / "cache") - - def cleanCachedResolutionCache(module: IvySbt#Module): Unit = IvyActions.cleanCachedResolutionCache(module, log) - - def ivyUpdate(module: IvySbt#Module) = - ivyUpdateEither(module) match { - case Right(r) => r - case Left(w) => - throw w.resolveException - } - - def mkPublishConfiguration(resolver: Resolver, artifacts: Map[Artifact, File]): PublishConfiguration = { - new PublishConfiguration( - ivyFile = None, - resolverName = resolver.name, - artifacts = artifacts, - checksums = Seq(), - logging = UpdateLogging.Full, - overwrite = true) - } - - def ivyPublish(module: IvySbt#Module, config: PublishConfiguration) = { - IvyActions.publish(module, config, log) - } -} diff --git a/ivy/src/test/scala/CachedResolutionSpec.scala b/ivy/src/test/scala/CachedResolutionSpec.scala deleted file mode 100644 index 719431282..000000000 --- a/ivy/src/test/scala/CachedResolutionSpec.scala +++ /dev/null @@ -1,83 +0,0 @@ -package sbt - -import org.specs2._ - -class CachedResolutionSpec extends BaseIvySpecification { - def is = args(sequential = true) ^ s2""" - - This is a specification to check the cached resolution - - Resolving the same module twice should - work $e1 - - Resolving the unsolvable module should - not work $e2 - - Resolving a module with a pseudo-conflict should - work $e3 - """ - - def commonsIo13 = ModuleID("commons-io", "commons-io", "1.3", Some("compile")) - def mavenCayennePlugin302 = ModuleID("org.apache.cayenne.plugins", "maven-cayenne-plugin", "3.0.2", Some("compile")) - def avro177 = ModuleID("org.apache.avro", "avro", "1.7.7", Some("compile")) - def dataAvro1940 = ModuleID("com.linkedin.pegasus", "data-avro", "1.9.40", Some("compile")) - def netty320 = ModuleID("org.jboss.netty", "netty", "3.2.0.Final", Some("compile")) - - def defaultOptions = EvictionWarningOptions.default - - import ShowLines._ - - def e1 = { - cleanIvyCache() - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), - Seq(commonsIo13), Some("2.10.2"), UpdateOptions().withCachedResolution(true)) - val report = ivyUpdate(m) - cleanCachedResolutionCache(m) - val report2 = ivyUpdate(m) - // first resolution creates the minigraph - println(report) - // second resolution reads from the minigraph - println(report.configurations.head.modules.head.artifacts) - report.configurations.size must_== 3 - } - - def e2 = { - // log.setLevel(Level.Debug) - val m = module(ModuleID("com.example", "foo", "0.2.0", Some("compile")), - Seq(mavenCayennePlugin302), Some("2.10.2"), UpdateOptions().withCachedResolution(true)) - ivyUpdateEither(m) match { - case Right(_) => sys.error("this should've failed") - case Left(uw) => - println(uw.lines.mkString("\n")) - } - ivyUpdateEither(m) match { - case Right(_) => sys.error("this should've failed 2") - case Left(uw) => - uw.lines must contain(allOf("\n\tNote: Unresolved dependencies path:", - "\t\tfoundrylogic.vpp:vpp:2.2.1", - "\t\t +- org.apache.cayenne:cayenne-tools:3.0.2", - "\t\t +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2", - "\t\t +- com.example:foo:0.2.0")) - } - } - - // https://github.com/sbt/sbt/issues/2046 - // data-avro:1.9.40 depends on avro:1.4.0, which depends on netty:3.2.1.Final. - // avro:1.4.0 will be evicted by avro:1.7.7. - // #2046 says that netty:3.2.0.Final is incorrectly evicted by netty:3.2.1.Final - def e3 = { - // log.setLevel(Level.Debug) - cleanIvyCache() - val m = module(ModuleID("com.example", "foo", "0.3.0", Some("compile")), - Seq(avro177, dataAvro1940, netty320), - Some("2.10.2"), UpdateOptions().withCachedResolution(true)) - // first resolution creates the minigraph - val report0 = ivyUpdate(m) - cleanCachedResolutionCache(m) - // second resolution reads from the minigraph - val report = ivyUpdate(m) - val modules = report.configurations.head.modules - (modules must containMatch("""org\.jboss\.netty:netty:3\.2\.0.Final""")) and - (modules must not containMatch ("""org\.jboss\.netty:netty:3\.2\.1.Final""")) - } -} diff --git a/ivy/src/test/scala/ComponentManagerTest.scala b/ivy/src/test/scala/ComponentManagerTest.scala deleted file mode 100644 index 165bdf9e2..000000000 --- a/ivy/src/test/scala/ComponentManagerTest.scala +++ /dev/null @@ -1,127 +0,0 @@ -package sbt - -import java.io.File -import java.util.concurrent.Callable -import org.specs2._ -import mutable.Specification -import IO.{ createDirectory, delete, touch, withTemporaryDirectory } -import org.apache.ivy.util.ChecksumHelper -import IfMissing.Fail -import xsbti.ComponentProvider - -// TODO - We need to re-enable this test. Right now, we dont' have a "stub" launcher for this. -// This is testing something which uses a launcher interface, but was grabbing the underlying class directly -// when it really should, instead, be stubbing out the underyling class. - -object ComponentManagerTest extends Specification { - val TestID = "manager-test" - "Component manager" should { - "throw an exception if 'file' is called for a non-existing component" in { - withManager { _.file(TestID)(Fail) must throwA[InvalidComponent] } - } - "throw an exception if 'file' is called for an empty component" in { - withManager { manager => - manager.define(TestID, Nil) - (manager.file(TestID)(Fail)) must throwA[InvalidComponent] - } - } - "return the file for a single-file component" in { - withManager { manager => - val hash = defineFile(manager, TestID, "a") - checksum(manager.file(TestID)(Fail)) must beEqualTo(hash) - } - } - - "throw an exception if 'file' is called for multi-file component" in { - withManager { manager => - defineFiles(manager, TestID, "a", "b") - (manager.file(TestID)(Fail)) must throwA[InvalidComponent] - } - } - "return the files for a multi-file component" in { - withManager { manager => - val hashes = defineFiles(manager, TestID, "a", "b") - checksum(manager.files(TestID)(Fail)) must containTheSameElementsAs(hashes) - } - } - "return the files for a single-file component" in { - withManager { manager => - val hashes = defineFiles(manager, TestID, "a") - checksum(manager.files(TestID)(Fail)) must containTheSameElementsAs(hashes) - } - } - "throw an exception if 'files' is called for a non-existing component" in { - withManager { _.files(TestID)(Fail) must throwA[InvalidComponent] } - } - - "properly cache a file and then retrieve it to an unresolved component" in { - withTemporaryDirectory { ivyHome => - withManagerHome(ivyHome) { definingManager => - val hash = defineFile(definingManager, TestID, "a") - try { - definingManager.cache(TestID) - withManagerHome(ivyHome) { usingManager => - checksum(usingManager.file(TestID)(Fail)) must beEqualTo(hash) - } - } finally { definingManager.clearCache(TestID) } - } - } - } - } - private def checksum(files: Iterable[File]): Seq[String] = files.map(checksum).toSeq - private def checksum(file: File): String = if (file.exists) ChecksumHelper.computeAsString(file, "sha1") else "" - private def defineFile(manager: ComponentManager, id: String, name: String): String = createFile(manager, id, name)(checksum) - private def defineFiles(manager: ComponentManager, id: String, names: String*): Seq[String] = createFiles(manager, id, names: _*)(checksum) - private def createFile[T](manager: ComponentManager, id: String, name: String)(f: File => T): T = createFiles(manager, id, name)(files => f(files.toList.head)) - private def createFiles[T](manager: ComponentManager, id: String, names: String*)(f: Seq[File] => T): T = - withTemporaryDirectory { dir => - val files = names.map(name => new File(dir, name)) - files.foreach(writeRandomContent) - manager.define(id, files) - f(files) - } - private def writeRandomContent(file: File) = IO.write(file, randomString) - private def randomString = "asdf" - private def withManager[T](f: ComponentManager => T): T = - withTemporaryDirectory { ivyHome => withManagerHome(ivyHome)(f) } - - private def withManagerHome[T](ivyHome: File)(f: ComponentManager => T): T = - TestLogger { logger => - withTemporaryDirectory { temp => - // The actual classes we'll use at runtime. - //val mgr = new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp, true), Some(ivyHome), logger) - - // A stub component manager - object provider extends ComponentProvider { - override def componentLocation(id: String): File = new File(temp, id) - override def lockFile(): File = { - IO.createDirectory(temp) - new java.io.File(temp, "sbt.components.lock") - } - override def defineComponent(id: String, files: Array[File]): Unit = { - val location = componentLocation(id) - if (location.exists) - throw new RuntimeException(s"Cannot redefine component. ID: $id, files: ${files.mkString(",")}") - else - IO.copy(files.map { f => f -> new java.io.File(location, f.getName) }) - } - override def addToComponent(id: String, files: Array[File]): Boolean = { - val location = componentLocation(id) - IO.copy(files.map { f => f -> new java.io.File(location, f.getName) }) - true - } - override def component(id: String): Array[File] = - Option(componentLocation(id).listFiles()).map(_.filter(_.isFile)).getOrElse(Array.empty) - } - // A stubbed locking API. - object locks extends xsbti.GlobalLock { - override def apply[T](lockFile: File, run: Callable[T]): T = { - // TODO - do we need to lock? - run.call() - } - } - val mgr = new ComponentManager(locks, provider, Some(ivyHome), logger) - f(mgr) - } - } -} diff --git a/ivy/src/test/scala/CrossVersionTest.scala b/ivy/src/test/scala/CrossVersionTest.scala deleted file mode 100644 index 364d6c67e..000000000 --- a/ivy/src/test/scala/CrossVersionTest.scala +++ /dev/null @@ -1,122 +0,0 @@ -package sbt - -import java.io.File -import org.specs2._ -import mutable.Specification - -object CrossVersionTest extends Specification { - "Cross version" should { - "return sbt API for xyz as None" in { - CrossVersion.sbtApiVersion("xyz") must_== None - } - "return sbt API for 0.12 as None" in { - CrossVersion.sbtApiVersion("0.12") must_== None - } - "return sbt API for 0.12.0-SNAPSHOT as None" in { - CrossVersion.sbtApiVersion("0.12.0-SNAPSHOT") must_== None - } - "return sbt API for 0.12.0-RC1 as Some((0, 12))" in { - CrossVersion.sbtApiVersion("0.12.0-RC1") must_== Some((0, 12)) - } - "return sbt API for 0.12.0 as Some((0, 12))" in { - CrossVersion.sbtApiVersion("0.12.0") must_== Some((0, 12)) - } - "return sbt API for 0.12.1-SNAPSHOT as Some((0, 12))" in { - CrossVersion.sbtApiVersion("0.12.1-SNAPSHOT") must_== Some((0, 12)) - } - "return sbt API for 0.12.1-RC1 as Some((0, 12))" in { - CrossVersion.sbtApiVersion("0.12.1-RC1") must_== Some((0, 12)) - } - "return sbt API for 0.12.1 as Some((0, 12))" in { - CrossVersion.sbtApiVersion("0.12.1") must_== Some((0, 12)) - } - "return sbt API compatibility for 0.12.0-M1 as false" in { - CrossVersion.isSbtApiCompatible("0.12.0-M1") must_== false - } - "return sbt API compatibility for 0.12.0-RC1 as true" in { - CrossVersion.isSbtApiCompatible("0.12.0-RC1") must_== true - } - "return sbt API compatibility for 0.12.1-RC1 as true" in { - CrossVersion.isSbtApiCompatible("0.12.1-RC1") must_== true - } - "return binary sbt version for 0.11.3 as 0.11.3" in { - CrossVersion.binarySbtVersion("0.11.3") must_== "0.11.3" - } - "return binary sbt version for 0.12.0-M1 as 0.12.0-M1" in { - CrossVersion.binarySbtVersion("0.12.0-M1") must_== "0.12.0-M1" - } - "return binary sbt version for 0.12.0-RC1 as 0.12" in { - CrossVersion.binarySbtVersion("0.12.0-RC1") must_== "0.12" - } - "return binary sbt version for 0.12.0 as 0.12" in { - CrossVersion.binarySbtVersion("0.12.0") must_== "0.12" - } - "return binary sbt version for 0.12.1-SNAPSHOT as 0.12" in { - CrossVersion.binarySbtVersion("0.12.1-SNAPSHOT") must_== "0.12" - } - "return binary sbt version for 0.12.1-RC1 as 0.12" in { - CrossVersion.binarySbtVersion("0.12.1-RC1") must_== "0.12" - } - "return binary sbt version for 0.12.1 as 0.12" in { - CrossVersion.binarySbtVersion("0.12.1") must_== "0.12" - } - - "return Scala API for xyz as None" in { - CrossVersion.scalaApiVersion("xyz") must_== None - } - "return Scala API for 2.10 as None" in { - CrossVersion.scalaApiVersion("2.10") must_== None - } - "return Scala API for 2.10.0-SNAPSHOT as None" in { - CrossVersion.scalaApiVersion("2.10.0-SNAPSHOT") must_== None - } - "return Scala API for 2.10.0-RC1 as None" in { - CrossVersion.scalaApiVersion("2.10.0-RC1") must_== None - } - "return Scala API for 2.10.0 as Some((2, 10))" in { - CrossVersion.scalaApiVersion("2.10.0") must_== Some((2, 10)) - } - "return Scala API for 2.10.0-1 as Some((2, 10))" in { - CrossVersion.scalaApiVersion("2.10.0-1") must_== Some((2, 10)) - } - "return Scala API for 2.10.1-SNAPSHOT as Some((2, 10))" in { - CrossVersion.scalaApiVersion("2.10.1-SNAPSHOT") must_== Some((2, 10)) - } - "return Scala API for 2.10.1-RC1 as Some((2, 10))" in { - CrossVersion.scalaApiVersion("2.10.1-RC1") must_== Some((2, 10)) - } - "return Scala API for 2.10.1 as Some((2, 10))" in { - CrossVersion.scalaApiVersion("2.10.1") must_== Some((2, 10)) - } - "return Scala API compatibility for 2.10.0-M1 as false" in { - CrossVersion.isScalaApiCompatible("2.10.0-M1") must_== false - } - "return Scala API compatibility for 2.10.0-RC1 as false" in { - CrossVersion.isScalaApiCompatible("2.10.0-RC1") must_== false - } - "return Scala API compatibility for 2.10.1-RC1 as false" in { - CrossVersion.isScalaApiCompatible("2.10.1-RC1") must_== true - } - "return binary Scala version for 2.9.2 as 2.9.2" in { - CrossVersion.binaryScalaVersion("2.9.2") must_== "2.9.2" - } - "return binary Scala version for 2.10.0-M1 as 2.10.0-M1" in { - CrossVersion.binaryScalaVersion("2.10.0-M1") must_== "2.10.0-M1" - } - "return binary Scala version for 2.10.0-RC1 as 2.10.0-RC1" in { - CrossVersion.binaryScalaVersion("2.10.0-RC1") must_== "2.10.0-RC1" - } - "return binary Scala version for 2.10.0 as 2.10" in { - CrossVersion.binaryScalaVersion("2.10.0") must_== "2.10" - } - "return binary Scala version for 2.10.1-M1 as 2.10" in { - CrossVersion.binaryScalaVersion("2.10.1-M1") must_== "2.10" - } - "return binary Scala version for 2.10.1-RC1 as 2.10" in { - CrossVersion.binaryScalaVersion("2.10.1-RC1") must_== "2.10" - } - "return binary Scala version for 2.10.1 as 2.10" in { - CrossVersion.binaryScalaVersion("2.10.1") must_== "2.10" - } - } -} diff --git a/ivy/src/test/scala/CustomPomParserTest.scala b/ivy/src/test/scala/CustomPomParserTest.scala deleted file mode 100644 index 1bc39a120..000000000 --- a/ivy/src/test/scala/CustomPomParserTest.scala +++ /dev/null @@ -1,37 +0,0 @@ -import java.io.File -import org.apache.ivy.core.module.descriptor.{ Artifact => IvyArtifact } -import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.ivy.core.resolve.ResolveOptions -import org.specs2.mutable.Specification -import sbt._ -import IO.withTemporaryDirectory - -object CustomPomParserTest extends Specification { - - "CustomPomParser" should { - "resolve an artifact with packaging 'scala-jar' as a regular jar file." in { - val log = ConsoleLogger() - withTemporaryDirectory { cacheDir => - val repoUrl = getClass.getResource("/test-maven-repo") - val local = MavenRepository("Test Repo", repoUrl.toExternalForm) - val paths = new IvyPaths(new File("."), Some(cacheDir)) - val conf = new InlineIvyConfiguration(paths, Seq(local), Nil, Nil, false, None, Seq("sha1", "md5"), None, log) - val ivySbt = new IvySbt(conf) - val resolveOpts = new ResolveOptions().setConfs(Array("default")) - val mrid = ModuleRevisionId.newInstance("com.test", "test-artifact", "1.0.0-SNAPSHOT") - - val resolveReport = ivySbt.withIvy(log) { ivy => - ivy.resolve(mrid, resolveOpts, true) - } - - resolveReport.hasError must beFalse - resolveReport.getArtifacts.size() must beEqualTo(1) - val artifact: IvyArtifact = resolveReport.getArtifacts.asInstanceOf[java.util.List[IvyArtifact]].get(0) - artifact.getModuleRevisionId must beEqualTo(mrid) - artifact.getExt must beEqualTo("jar") - } - - } - } - -} diff --git a/ivy/src/test/scala/DMSerializationSpec.scala b/ivy/src/test/scala/DMSerializationSpec.scala deleted file mode 100644 index bf62de10b..000000000 --- a/ivy/src/test/scala/DMSerializationSpec.scala +++ /dev/null @@ -1,67 +0,0 @@ -package sbt - -import org.specs2._ -import matcher.MatchResult -import java.net.URL -import java.io.File -import sbt.serialization._ - -class DMSerializationSpec extends Specification { - def is = sequential ^ s2""" - - This is a specification to check the serialization of dependency graph. - - CrossVersion.full should - ${roundtripStr(CrossVersion.full: sbt.CrossVersion)} - CrossVersion.binary should - ${roundtripStr(CrossVersion.binary: sbt.CrossVersion)} - CrossVersion.Disabled should - ${roundtrip(CrossVersion.Disabled: sbt.CrossVersion)} - - Artifact("foo") should - ${roundtrip(Artifact("foo"))} - Artifact("foo", "sources") should - ${roundtrip(Artifact("foo", "sources"))} - Artifact.pom("foo") should - ${roundtrip(Artifact.pom("foo"))} - Artifact("foo", url("http://example.com/")) should - ${roundtrip(Artifact("foo", new URL("http://example.com/")))} - Artifact("foo").extra(("key", "value")) should - ${roundtrip(Artifact("foo").extra(("key", "value")))} - - ModuleID("org", "name", "1.0") should - ${roundtrip(ModuleID("org", "name", "1.0"))} - - ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil) should - ${roundtripStr(ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil))} - Organization artifact report should - ${roundtripStr(organizationArtifactReportExample)} - Configuration report should - ${roundtripStr(configurationReportExample)} - Update report should - ${roundtripStr(updateReportExample)} - """ - - lazy val updateReportExample = - new UpdateReport(new File("./foo"), Vector(configurationReportExample), - new UpdateStats(0, 0, 0, false), Map(new File("./foo") -> 0)) - lazy val configurationReportExample = - new ConfigurationReport("compile", Vector(moduleReportExample), - Vector(organizationArtifactReportExample), Nil) - lazy val organizationArtifactReportExample = - new OrganizationArtifactReport("org", "name", Vector(moduleReportExample)) - lazy val moduleReportExample = - ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil) - - def roundtrip[A: Pickler: Unpickler](a: A) = - roundtripBuilder(a) { _ must_== _ } - def roundtripStr[A: Pickler: Unpickler](a: A) = - roundtripBuilder(a) { _.toString must_== _.toString } - def roundtripBuilder[A: Pickler: Unpickler](a: A)(f: (A, A) => MatchResult[Any]): MatchResult[Any] = - { - val json = toJsonString(a) - println(json) - val obj = fromJsonString[A](json).get - f(a, obj) - } -} diff --git a/ivy/src/test/scala/EvictionWarningSpec.scala b/ivy/src/test/scala/EvictionWarningSpec.scala deleted file mode 100644 index 0e1e3dd2c..000000000 --- a/ivy/src/test/scala/EvictionWarningSpec.scala +++ /dev/null @@ -1,228 +0,0 @@ -package sbt - -import org.specs2._ - -class EvictionWarningSpec extends BaseIvySpecification { - def is = s2""" - - This is a specification to check the eviction warnings - - Eviction of scala-library whose scalaVersion should - be detected $scalaVersionWarn1 - not be detected if it's diabled $scalaVersionWarn2 - print out message about the eviction $scalaVersionWarn3 - print out message about the eviction with callers $scalaVersionWarn4 - - Including two (suspect) binary incompatible Java libraries to - direct dependencies should - be detected as eviction $javaLibWarn1 - not be detected if it's disabled $javaLibWarn2 - print out message about the eviction $javaLibWarn3 - print out message about the eviction with callers $javaLibWarn4 - - Including two (suspect) binary compatible Java libraries to - direct dependencies should - not be detected as eviction $javaLibNoWarn1 - print out message about the eviction $javaLibNoWarn2 - - Including two (suspect) transitively binary incompatible Java libraries to - direct dependencies should - be not detected as eviction $javaLibTransitiveWarn1 - be detected if it's enabled $javaLibTransitiveWarn2 - print out message about the eviction if it's enabled $javaLibTransitiveWarn3 - - Including two (suspect) binary incompatible Scala libraries to - direct dependencies should - be detected as eviction $scalaLibWarn1 - print out message about the eviction $scalaLibWarn2 - - Including two (suspect) binary compatible Scala libraries to - direct dependencies should - not be detected as eviction $scalaLibNoWarn1 - print out message about the eviction $scalaLibNoWarn2 - - Including two (suspect) transitively binary incompatible Scala libraries to - direct dependencies should - be not detected as eviction $scalaLibTransitiveWarn1 - be detected if it's enabled $scalaLibTransitiveWarn2 - print out message about the eviction if it's enabled $scalaLibTransitiveWarn3 - """ - - def akkaActor214 = ModuleID("com.typesafe.akka", "akka-actor", "2.1.4", Some("compile")) cross CrossVersion.binary - def akkaActor230 = ModuleID("com.typesafe.akka", "akka-actor", "2.3.0", Some("compile")) cross CrossVersion.binary - def akkaActor234 = ModuleID("com.typesafe.akka", "akka-actor", "2.3.4", Some("compile")) cross CrossVersion.binary - def scala2102 = ModuleID("org.scala-lang", "scala-library", "2.10.2", Some("compile")) - def scala2103 = ModuleID("org.scala-lang", "scala-library", "2.10.3", Some("compile")) - def scala2104 = ModuleID("org.scala-lang", "scala-library", "2.10.4", Some("compile")) - def commonsIo13 = ModuleID("commons-io", "commons-io", "1.3", Some("compile")) - def commonsIo14 = ModuleID("commons-io", "commons-io", "1.4", Some("compile")) - def commonsIo24 = ModuleID("commons-io", "commons-io", "2.4", Some("compile")) - def bnfparser10 = ModuleID("ca.gobits.bnf", "bnfparser", "1.0", Some("compile")) // uses commons-io 2.4 - def unfilteredUploads080 = ModuleID("net.databinder", "unfiltered-uploads", "0.8.0", Some("compile")) cross CrossVersion.binary // uses commons-io 1.4 - def bananaSesame04 = ModuleID("org.w3", "banana-sesame", "0.4", Some("compile")) cross CrossVersion.binary // uses akka-actor 2.1.4 - def akkaRemote234 = ModuleID("com.typesafe.akka", "akka-remote", "2.3.4", Some("compile")) cross CrossVersion.binary // uses akka-actor 2.3.4 - - def defaultOptions = EvictionWarningOptions.default - - import ShowLines._ - - def scalaVersionDeps = Seq(scala2102, akkaActor230) - - def scalaVersionWarn1 = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).scalaEvictions must have size (1) - } - - def scalaVersionWarn2 = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnScalaVersionEviction(false), report, log).scalaEvictions must have size (0) - } - - def scalaVersionWarn3 = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).lines must_== - List("Scala version was updated by one of library dependencies:", - "\t* org.scala-lang:scala-library:2.10.2 -> 2.10.3", - "To force scalaVersion, add the following:", - "\tivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) }", - "Run 'evicted' to see detailed eviction warnings") - } - - def scalaVersionWarn4 = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withShowCallers(true), report, log).lines must_== - List("Scala version was updated by one of library dependencies:", - "\t* org.scala-lang:scala-library:2.10.2 -> 2.10.3 (caller: com.typesafe.akka:akka-actor_2.10:2.3.0, com.example:foo:0.1.0)", - "To force scalaVersion, add the following:", - "\tivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) }") - } - - def javaLibDirectDeps = Seq(commonsIo14, commonsIo24) - - def javaLibWarn1 = { - val m = module(defaultModuleId, javaLibDirectDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (1) - } - - def javaLibWarn2 = { - val m = module(defaultModuleId, javaLibDirectDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnDirectEvictions(false), report, log).reportedEvictions must have size (0) - } - - def javaLibWarn3 = { - val m = module(defaultModuleId, javaLibDirectDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).lines must_== - List("There may be incompatibilities among your library dependencies.", - "Here are some of the libraries that were evicted:", - "\t* commons-io:commons-io:1.4 -> 2.4", - "Run 'evicted' to see detailed eviction warnings") - } - - def javaLibWarn4 = { - val m = module(defaultModuleId, javaLibDirectDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withShowCallers(true), report, log).lines must_== - List("There may be incompatibilities among your library dependencies.", - "Here are some of the libraries that were evicted:", - "\t* commons-io:commons-io:1.4 -> 2.4 (caller: com.example:foo:0.1.0)") - } - - def javaLibNoWarn1 = { - val deps = Seq(commonsIo14, commonsIo13) - val m = module(defaultModuleId, deps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (0) - } - - def javaLibNoWarn2 = { - val deps = Seq(commonsIo14, commonsIo13) - val m = module(defaultModuleId, deps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).lines must_== Nil - } - - def javaLibTransitiveDeps = Seq(unfilteredUploads080, bnfparser10) - - def javaLibTransitiveWarn1 = { - val m = module(defaultModuleId, javaLibTransitiveDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (0) - } - - def javaLibTransitiveWarn2 = { - val m = module(defaultModuleId, javaLibTransitiveDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnTransitiveEvictions(true), report, log).reportedEvictions must have size (1) - } - - def javaLibTransitiveWarn3 = { - val m = module(defaultModuleId, javaLibTransitiveDeps, Some("2.10.3")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnTransitiveEvictions(true).withShowCallers(true), report, log).lines must_== - List("There may be incompatibilities among your library dependencies.", - "Here are some of the libraries that were evicted:", - "\t* commons-io:commons-io:1.4 -> 2.4 (caller: ca.gobits.bnf:bnfparser:1.0, net.databinder:unfiltered-uploads_2.10:0.8.0)") - } - - def scalaLibWarn1 = { - val deps = Seq(scala2104, akkaActor214, akkaActor234) - val m = module(defaultModuleId, deps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (1) - } - - def scalaLibWarn2 = { - val deps = Seq(scala2104, akkaActor214, akkaActor234) - val m = module(defaultModuleId, deps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).lines must_== - List("There may be incompatibilities among your library dependencies.", - "Here are some of the libraries that were evicted:", - "\t* com.typesafe.akka:akka-actor_2.10:2.1.4 -> 2.3.4", - "Run 'evicted' to see detailed eviction warnings") - } - - def scalaLibNoWarn1 = { - val deps = Seq(scala2104, akkaActor230, akkaActor234) - val m = module(defaultModuleId, deps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (0) - } - - def scalaLibNoWarn2 = { - val deps = Seq(scala2104, akkaActor230, akkaActor234) - val m = module(defaultModuleId, deps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).lines must_== Nil - } - - def scalaLibTransitiveDeps = Seq(scala2104, bananaSesame04, akkaRemote234) - - def scalaLibTransitiveWarn1 = { - val m = module(defaultModuleId, scalaLibTransitiveDeps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions, report, log).reportedEvictions must have size (0) - } - - def scalaLibTransitiveWarn2 = { - val m = module(defaultModuleId, scalaLibTransitiveDeps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnTransitiveEvictions(true), report, log).reportedEvictions must have size (1) - } - - def scalaLibTransitiveWarn3 = { - val m = module(defaultModuleId, scalaLibTransitiveDeps, Some("2.10.4")) - val report = ivyUpdate(m) - EvictionWarning(m, defaultOptions.withWarnTransitiveEvictions(true).withShowCallers(true), report, log).lines must_== - List("There may be incompatibilities among your library dependencies.", - "Here are some of the libraries that were evicted:", - "\t* com.typesafe.akka:akka-actor_2.10:2.1.4 -> 2.3.4 (caller: com.typesafe.akka:akka-remote_2.10:2.3.4, org.w3:banana-sesame_2.10:0.4, org.w3:banana-rdf_2.10:0.4)") - } -} diff --git a/ivy/src/test/scala/InconsistentDuplicateSpec.scala b/ivy/src/test/scala/InconsistentDuplicateSpec.scala deleted file mode 100644 index b9f4a05ef..000000000 --- a/ivy/src/test/scala/InconsistentDuplicateSpec.scala +++ /dev/null @@ -1,28 +0,0 @@ -package sbt - -import org.specs2._ - -class InconsistentDuplicateSpec extends Specification { - def is = s2""" - - This is a specification to check the inconsistent duplicate warnings - - Duplicate with different version should - be warned $warn1 - - Duplicate with same version should - not be warned $nodupe1 - """ - - def akkaActor214 = ModuleID("com.typesafe.akka", "akka-actor", "2.1.4", Some("compile")) cross CrossVersion.binary - def akkaActor230 = ModuleID("com.typesafe.akka", "akka-actor", "2.3.0", Some("compile")) cross CrossVersion.binary - def akkaActor230Test = ModuleID("com.typesafe.akka", "akka-actor", "2.3.0", Some("test")) cross CrossVersion.binary - - def warn1 = - IvySbt.inconsistentDuplicateWarning(Seq(akkaActor214, akkaActor230)) must_== - List("Multiple dependencies with the same organization/name but different versions. To avoid conflict, pick one version:", - " * com.typesafe.akka:akka-actor:(2.1.4, 2.3.0)") - - def nodupe1 = - IvySbt.inconsistentDuplicateWarning(Seq(akkaActor230Test, akkaActor230)) must_== Nil -} diff --git a/ivy/src/test/scala/MakePomSpec.scala b/ivy/src/test/scala/MakePomSpec.scala deleted file mode 100644 index 68ede8bc8..000000000 --- a/ivy/src/test/scala/MakePomSpec.scala +++ /dev/null @@ -1,73 +0,0 @@ -package sbt - -import java.io.File -import org.specs2._ - -// http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html -// http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html -class MakePomSpec extends Specification { - def is = s2""" - - This is a specification to check the Ivy revision number conversion to pom. - - 1.0 should - ${convertTo("1.0", "1.0")} - - [1.0,2.0] should - ${convertTo("[1.0,2.0]", "[1.0,2.0]")} - - [1.0,2.0[ should - ${convertTo("[1.0,2.0[", "[1.0,2.0)")} - - ]1.0,2.0] should - ${convertTo("]1.0,2.0]", "(1.0,2.0]")} - - ]1.0,2.0[ should - ${convertTo("]1.0,2.0[", "(1.0,2.0)")} - - [1.0,) should - ${convertTo("[1.0,)", "[1.0,)")} - - ]1.0,) should - ${convertTo("]1.0,)", "(1.0,)")} - - (,2.0] should - ${convertTo("(,2.0]", "(,2.0]")} - - (,2.0[ should - ${convertTo("(,2.0[", "(,2.0)")} - - 1.+ should - ${convertTo("1.+", "[1,2)")} - - 1.2.3.4.+ should - ${convertTo("1.2.3.4.+", "[1.2.3.4,1.2.3.5)")} - - 12.31.42.+ should - ${convertTo("12.31.42.+", "[12.31.42,12.31.43)")} - - 1.1+ should - ${convertTo("1.1+", "[1.1,1.2),[1.10,1.20),[1.100,1.200),[1.1000,1.2000),[1.10000,1.20000)")} - - 1+ should - ${convertTo("1+", "[1,2),[10,20),[100,200),[1000,2000),[10000,20000)")} - - + should - ${convertTo("+", "[0,)")} - - foo+ should - ${beParsedAsError("foo+")} - """ - - val mp = new MakePom(ConsoleLogger()) - def convertTo(s: String, expected: String) = - MakePom.makeDependencyVersion(s) must_== expected - def beParsedAsError(s: String) = - try { - MakePom.makeDependencyVersion(s) - failure - } catch { - case e: Throwable => success - } -} - diff --git a/ivy/src/test/scala/ResolverTest.scala b/ivy/src/test/scala/ResolverTest.scala deleted file mode 100644 index 64c6ab777..000000000 --- a/ivy/src/test/scala/ResolverTest.scala +++ /dev/null @@ -1,24 +0,0 @@ -import java.net.URL - -import org.specs2.mutable.Specification -import sbt._ - -object ResolverTest extends Specification { - - "Resolver" should { - "url" should { - "propagate pattern descriptorOptional and skipConsistencyCheck." in { - val pats = Seq("[orgPath]") - val patsExpected = Seq("http://foo.com/test/[orgPath]") - val patterns = Resolver.url("test", new URL("http://foo.com/test"))(Patterns(pats, pats, isMavenCompatible = false, descriptorOptional = true, skipConsistencyCheck = true)).patterns - - patterns.ivyPatterns must equalTo(patsExpected) - patterns.artifactPatterns must equalTo(patsExpected) - patterns.isMavenCompatible must beFalse - patterns.skipConsistencyCheck must beTrue - patterns.descriptorOptional must beTrue - } - } - } - -} diff --git a/ivy/src/test/scala/VersionNumberSpec.scala b/ivy/src/test/scala/VersionNumberSpec.scala deleted file mode 100644 index 68bc2ad6e..000000000 --- a/ivy/src/test/scala/VersionNumberSpec.scala +++ /dev/null @@ -1,140 +0,0 @@ -package sbt - -import org.specs2._ - -class VersionNumberSpec extends Specification { - def is = s2""" - - This is a specification to check the version number parsing. - - 1 should - ${beParsedAs("1", Seq(1), Seq(), Seq())} - ${breakDownTo("1", Some(1))} - ${generateCorrectCascadingNumbers("1", Seq("1"))} - - 1.0 should - ${beParsedAs("1.0", Seq(1, 0), Seq(), Seq())} - ${breakDownTo("1.0", Some(1), Some(0))} - ${generateCorrectCascadingNumbers("1.0", Seq("1.0"))} - - 1.0.0 should - ${beParsedAs("1.0.0", Seq(1, 0, 0), Seq(), Seq())} - ${breakDownTo("1.0.0", Some(1), Some(0), Some(0))} - ${generateCorrectCascadingNumbers("1.0.0", Seq("1.0.0", "1.0"))} - - ${beSemVerCompatWith("1.0.0", "1.0.1")} - ${beSemVerCompatWith("1.0.0", "1.1.1")} - ${notBeSemVerCompatWith("1.0.0", "2.0.0")} - ${notBeSemVerCompatWith("1.0.0", "1.0.0-M1")} - - ${beSecSegCompatWith("1.0.0", "1.0.1")} - ${notBeSecSegCompatWith("1.0.0", "1.1.1")} - ${notBeSecSegCompatWith("1.0.0", "2.0.0")} - ${notBeSecSegCompatWith("1.0.0", "1.0.0-M1")} - - 1.0.0.0 should - ${beParsedAs("1.0.0.0", Seq(1, 0, 0, 0), Seq(), Seq())} - ${breakDownTo("1.0.0.0", Some(1), Some(0), Some(0), Some(0))} - ${generateCorrectCascadingNumbers("1.0.0.0", Seq("1.0.0.0", "1.0.0", "1.0"))} - - 0.12.0 should - ${beParsedAs("0.12.0", Seq(0, 12, 0), Seq(), Seq())} - ${breakDownTo("0.12.0", Some(0), Some(12), Some(0))} - ${generateCorrectCascadingNumbers("0.12.0", Seq("0.12.0", "0.12"))} - - ${notBeSemVerCompatWith("0.12.0", "0.12.0-RC1")} - ${notBeSemVerCompatWith("0.12.0", "0.12.1")} - ${notBeSemVerCompatWith("0.12.0", "0.12.1-M1")} - - ${notBeSecSegCompatWith("0.12.0", "0.12.0-RC1")} - ${beSecSegCompatWith("0.12.0", "0.12.1")} - ${beSecSegCompatWith("0.12.0", "0.12.1-M1")} - - 0.1.0-SNAPSHOT should - ${beParsedAs("0.1.0-SNAPSHOT", Seq(0, 1, 0), Seq("SNAPSHOT"), Seq())} - ${generateCorrectCascadingNumbers("0.1.0-SNAPSHOT", Seq("0.1.0-SNAPSHOT", "0.1.0", "0.1"))} - - ${beSemVerCompatWith("0.1.0-SNAPSHOT", "0.1.0-SNAPSHOT")} - ${notBeSemVerCompatWith("0.1.0-SNAPSHOT", "0.1.0")} - ${beSemVerCompatWith("0.1.0-SNAPSHOT", "0.1.0-SNAPSHOT+001")} - - ${beSecSegCompatWith("0.1.0-SNAPSHOT", "0.1.0-SNAPSHOT")} - ${notBeSecSegCompatWith("0.1.0-SNAPSHOT", "0.1.0")} - ${beSecSegCompatWith("0.1.0-SNAPSHOT", "0.1.0-SNAPSHOT+001")} - - 0.1.0-M1 should - ${beParsedAs("0.1.0-M1", Seq(0, 1, 0), Seq("M1"), Seq())} - ${generateCorrectCascadingNumbers("0.1.0-M1", Seq("0.1.0-M1", "0.1.0", "0.1"))} - - 0.1.0-RC1 should - ${beParsedAs("0.1.0-RC1", Seq(0, 1, 0), Seq("RC1"), Seq())} - ${generateCorrectCascadingNumbers("0.1.0-RC1", Seq("0.1.0-RC1", "0.1.0", "0.1"))} - - 0.1.0-MSERVER-1 should - ${beParsedAs("0.1.0-MSERVER-1", Seq(0, 1, 0), Seq("MSERVER", "1"), Seq())} - ${generateCorrectCascadingNumbers("0.1.0-MSERVER-1", Seq("0.1.0-MSERVER-1", "0.1.0", "0.1"))} - - - 2.10.4-20140115-000117-b3a-sources should - ${beParsedAs("2.10.4-20140115-000117-b3a-sources", Seq(2, 10, 4), Seq("20140115", "000117", "b3a", "sources"), Seq())} - ${generateCorrectCascadingNumbers("2.10.4-20140115-000117-b3a-sources", Seq("2.10.4-20140115-000117-b3a-sources", "2.10.4", "2.10"))} - - ${beSemVerCompatWith("2.10.4-20140115-000117-b3a-sources", "2.0.0")} - - ${notBeSecSegCompatWith("2.10.4-20140115-000117-b3a-sources", "2.0.0")} - - 20140115000117-b3a-sources should - ${beParsedAs("20140115000117-b3a-sources", Seq(20140115000117L), Seq("b3a", "sources"), Seq())} - ${generateCorrectCascadingNumbers("20140115000117-b3a-sources", Seq("20140115000117-b3a-sources"))} - - 1.0.0-alpha+001+002 should - ${beParsedAs("1.0.0-alpha+001+002", Seq(1, 0, 0), Seq("alpha"), Seq("+001", "+002"))} - ${generateCorrectCascadingNumbers("1.0.0-alpha+001+002", Seq("1.0.0-alpha+001+002", "1.0.0", "1.0"))} - - non.space.!?string should - ${beParsedAs("non.space.!?string", Seq(), Seq(), Seq("non.space.!?string"))} - ${generateCorrectCascadingNumbers("non.space.!?string", Seq("non.space.!?string"))} - - space !?string should - ${beParsedAsError("space !?string")} - - blank string should - ${beParsedAsError("")} - """ - - def beParsedAs(s: String, ns: Seq[Long], ts: Seq[String], es: Seq[String]) = - s match { - case VersionNumber(ns1, ts1, es1) if (ns1 == ns && ts1 == ts && es1 == es) => - (VersionNumber(ns, ts, es).toString must_== s) and - (VersionNumber(ns, ts, es) == VersionNumber(ns, ts, es)) - case VersionNumber(ns1, ts1, es1) => - sys.error(s"$ns1, $ts1, $es1") - } - def breakDownTo(s: String, major: Option[Long], minor: Option[Long] = None, - patch: Option[Long] = None, buildNumber: Option[Long] = None) = - s match { - case VersionNumber(ns, ts, es) => - val v = VersionNumber(ns, ts, es) - (v._1 must_== major) and - (v._2 must_== minor) and - (v._3 must_== patch) and - (v._4 must_== buildNumber) - } - def beParsedAsError(s: String) = - s match { - case VersionNumber(ns1, ts1, es1) => failure - case _ => success - } - def beSemVerCompatWith(v1: String, v2: String) = - VersionNumber.SemVer.isCompatible(VersionNumber(v1), VersionNumber(v2)) must_== true - def notBeSemVerCompatWith(v1: String, v2: String) = - VersionNumber.SemVer.isCompatible(VersionNumber(v1), VersionNumber(v2)) must_== false - def beSecSegCompatWith(v1: String, v2: String) = - VersionNumber.SecondSegment.isCompatible(VersionNumber(v1), VersionNumber(v2)) must_== true - def notBeSecSegCompatWith(v1: String, v2: String) = - VersionNumber.SecondSegment.isCompatible(VersionNumber(v1), VersionNumber(v2)) must_== false - def generateCorrectCascadingNumbers(s: String, ns: Seq[String]) = { - val versionNumbers = ns.toVector map VersionNumber.apply - VersionNumber(s).cascadingVersions must_== versionNumbers - } -} diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala deleted file mode 100644 index a2f1e4e47..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ /dev/null @@ -1,260 +0,0 @@ -package sbt -package appmacro - -import scala.reflect._ -import macros._ -import scala.tools.nsc.Global -import ContextUtil.{ DynamicDependencyError, DynamicReferenceError } - -object ContextUtil { - final val DynamicDependencyError = "Illegal dynamic dependency" - final val DynamicReferenceError = "Illegal dynamic reference" - - /** - * Constructs an object with utility methods for operating in the provided macro context `c`. - * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. - */ - def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) - - /** - * Helper for implementing a no-argument macro that is introduced via an implicit. - * This method removes the implicit conversion and evaluates the function `f` on the target of the conversion. - * - * Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this - * method, the result of this method is `f()`. - */ - def selectMacroImpl[T: c.WeakTypeTag](c: Context)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = - { - import c.universe._ - c.macroApplication match { - case s @ Select(Apply(_, t :: Nil), tp) => f(c.Expr[Any](t), s.pos) - case x => unexpectedTree(x) - } - } - - def unexpectedTree[C <: Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree) -} - -// TODO 2.11 Remove this after dropping 2.10.x support. -private object HasCompat { val compat = ??? }; import HasCompat._ - -/** - * Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). - * This is not thread safe due to the underlying Context and related data structures not being thread safe. - * Use `ContextUtil[c.type](c)` to construct. - */ -final class ContextUtil[C <: Context](val ctx: C) { - import ctx.universe.{ Apply => ApplyTree, _ } - import compat._ - - val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] - val global: powerContext.universe.type = powerContext.universe - def callsiteTyper: global.analyzer.Typer = powerContext.callsiteTyper - val initialOwner: Symbol = callsiteTyper.context.owner.asInstanceOf[ctx.universe.Symbol] - - lazy val alistType = ctx.typeOf[AList[KList]] - lazy val alist: Symbol = alistType.typeSymbol.companionSymbol - lazy val alistTC: Type = alistType.typeConstructor - - /** Modifiers for a local val.*/ - lazy val localModifiers = Modifiers(NoFlags) - - def getPos(sym: Symbol) = if (sym eq null) NoPosition else sym.pos - - /** - * Constructs a unique term name with the given prefix within this Context. - * (The current implementation uses Context.fresh, which increments - */ - def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) - - /** - * Constructs a new, synthetic, local ValDef Type `tpe`, a unique name, - * Position `pos`, an empty implementation (no rhs), and owned by `owner`. - */ - def freshValDef(tpe: Type, pos: Position, owner: Symbol): ValDef = - { - val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet] - val sym = owner.newTermSymbol(freshTermName("q"), pos, SYNTHETIC) - setInfo(sym, tpe) - val vd = ValDef(sym, EmptyTree) - vd.setPos(pos) - vd - } - - lazy val parameterModifiers = Modifiers(Flag.PARAM) - - /** - * Collects all definitions in the tree for use in checkReferences. - * This excludes definitions in wrapped expressions because checkReferences won't allow nested dereferencing anyway. - */ - def collectDefs(tree: Tree, isWrapper: (String, Type, Tree) => Boolean): collection.Set[Symbol] = - { - val defs = new collection.mutable.HashSet[Symbol] - // adds the symbols for all non-Ident subtrees to `defs`. - val process = new Traverser { - override def traverse(t: Tree) = t match { - case _: Ident => () - case ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) if isWrapper(nme.decoded, tpe.tpe, qual) => () - case tree => - if (tree.symbol ne null) defs += tree.symbol; - super.traverse(tree) - } - } - process.traverse(tree) - defs - } - - /** - * A reference is illegal if it is to an M instance defined within the scope of the macro call. - * As an approximation, disallow referenced to any local definitions `defs`. - */ - def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean = - sym != null && sym != NoSymbol && defs.contains(sym) - - /** - * A function that checks the provided tree for illegal references to M instances defined in the - * expression passed to the macro and for illegal dereferencing of M instances. - */ - def checkReferences(defs: collection.Set[Symbol], isWrapper: (String, Type, Tree) => Boolean): Tree => Unit = { - case s @ ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => - if (isWrapper(nme.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) - case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) - case _ => () - } - - /** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */ - def freshMethodParameter(tpe: Type): ValDef = - ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) - - /** Constructs a ValDef with local modifiers and a unique name. */ - def localValDef(tpt: Tree, rhs: Tree): ValDef = - ValDef(localModifiers, freshTermName("q"), tpt, rhs) - - /** Constructs a tuple value of the right TupleN type from the provided inputs.*/ - def mkTuple(args: List[Tree]): Tree = - global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] - - def setSymbol[Tree](t: Tree, sym: Symbol): Unit = - t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) - def setInfo[Tree](sym: Symbol, tpe: Type): Unit = - sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) - - /** Creates a new, synthetic type variable with the specified `owner`. */ - def newTypeVariable(owner: Symbol, prefix: String = "T0"): TypeSymbol = - owner.asInstanceOf[global.Symbol].newSyntheticTypeParam(prefix, 0L).asInstanceOf[ctx.universe.TypeSymbol] - - /** The type representing the type constructor `[X] X` */ - lazy val idTC: Type = - { - val tvar = newTypeVariable(NoSymbol) - polyType(tvar :: Nil, refVar(tvar)) - } - /** A Type that references the given type variable. */ - def refVar(variable: TypeSymbol): Type = variable.toTypeConstructor - /** Constructs a new, synthetic type variable that is a type constructor. For example, in type Y[L[x]], L is such a type variable. */ - def newTCVariable(owner: Symbol): TypeSymbol = - { - val tc = newTypeVariable(owner) - val arg = newTypeVariable(tc, "x") - tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) - tc - } - /** >: Nothing <: Any */ - def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType) - - /** Creates a new anonymous function symbol with Position `pos`. */ - def functionSymbol(pos: Position): Symbol = - callsiteTyper.context.owner.newAnonymousFunctionValue(pos.asInstanceOf[global.Position]).asInstanceOf[ctx.universe.Symbol] - - def functionType(args: List[Type], result: Type): Type = - { - val tpe = global.definitions.functionType(args.asInstanceOf[List[global.Type]], result.asInstanceOf[global.Type]) - tpe.asInstanceOf[Type] - } - - /** Create a Tree that references the `val` represented by `vd`, copying attributes from `replaced`. */ - def refVal(replaced: Tree, vd: ValDef): Tree = - treeCopy.Ident(replaced, vd.name).setSymbol(vd.symbol) - - /** Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to `functionSym` in `body`.*/ - def createFunction(params: List[ValDef], body: Tree, functionSym: Symbol): Tree = - { - changeOwner(body, initialOwner, functionSym) - val f = Function(params, body) - setSymbol(f, functionSym) - f - } - - def changeOwner(tree: Tree, prev: Symbol, next: Symbol): Unit = - new ChangeOwnerAndModuleClassTraverser(prev.asInstanceOf[global.Symbol], next.asInstanceOf[global.Symbol]).traverse(tree.asInstanceOf[global.Tree]) - - // Workaround copied from scala/async:can be removed once https://github.com/scala/scala/pull/3179 is merged. - private[this] class ChangeOwnerAndModuleClassTraverser(oldowner: global.Symbol, newowner: global.Symbol) extends global.ChangeOwnerTraverser(oldowner, newowner) { - override def traverse(tree: global.Tree): Unit = { - tree match { - case _: global.DefTree => change(tree.symbol.moduleClass) - case _ => - } - super.traverse(tree) - } - } - - /** Returns the Symbol that references the statically accessible singleton `i`. */ - def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol = - it.tpe match { - case SingleType(_, sym) if !sym.isFreeTerm && sym.isStatic => sym - case x => sys.error("Instance must be static (was " + x + ").") - } - - def select(t: Tree, name: String): Tree = Select(t, newTermName(name)) - - /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ - def method(obj: Symbol, name: String): Symbol = { - val ts: Type = obj.typeSignature - val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name)) - m.asInstanceOf[Symbol] - } - - /** - * Returns a Type representing the type constructor tcp.. For example, given - * `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing - * the type constructor `[x] List[x]`. - */ - def extractTC(tcp: AnyRef with Singleton, name: String)(implicit it: ctx.TypeTag[tcp.type]): ctx.Type = - { - val itTpe = it.tpe.asInstanceOf[global.Type] - val m = itTpe.nonPrivateMember(global.newTypeName(name)) - val tc = itTpe.memberInfo(m).asInstanceOf[ctx.universe.Type] - assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc) - tc - } - - /** - * Substitutes wrappers in tree `t` with the result of `subWrapper`. - * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. - * Typically, `f` is a `Select` or `Ident`. - * The wrapper is replaced with the result of `subWrapper(, , )` - */ - def transformWrappers(t: Tree, subWrapper: (String, Type, Tree, Tree) => Converted[ctx.type]): Tree = - { - // the main tree transformer that replaces calls to InputWrapper.wrap(x) with - // plain Idents that reference the actual input value - object appTransformer extends Transformer { - override def transform(tree: Tree): Tree = - tree match { - case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => - subWrapper(nme.decoded, targ.tpe, qual, tree) match { - case Converted.Success(t, finalTx) => - changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150 - finalTx(t) - case Converted.Failure(p, m) => ctx.abort(p, m) - case _: Converted.NotApplicable[_] => super.transform(tree) - } - case _ => super.transform(tree) - } - } - appTransformer.atOwner(initialOwner) { - appTransformer.transform(t) - } - } -} diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala deleted file mode 100644 index 3a2e562a6..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala +++ /dev/null @@ -1,37 +0,0 @@ -package sbt -package appmacro - -import scala.reflect._ -import macros._ -import Types.idFun - -abstract class Convert { - def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] - def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = - (n, tpe, tree) => { - val tag = c.WeakTypeTag(tpe) - apply(c)(n, tree)(tag).isSuccess - } -} -sealed trait Converted[C <: Context with Singleton] { - def isSuccess: Boolean - def transform(f: C#Tree => C#Tree): Converted[C] -} -object Converted { - def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] - final case class Failure[C <: Context with Singleton](position: C#Position, message: String) extends Converted[C] { - def isSuccess = false - def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message) - } - final class NotApplicable[C <: Context with Singleton] extends Converted[C] { - def isSuccess = false - def transform(f: C#Tree => C#Tree): Converted[C] = this - } - final case class Success[C <: Context with Singleton](tree: C#Tree, finalTransform: C#Tree => C#Tree) extends Converted[C] { - def isSuccess = true - def transform(f: C#Tree => C#Tree): Converted[C] = Success(f(tree), finalTransform) - } - object Success { - def apply[C <: Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun) - } -} \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala deleted file mode 100644 index 7a63feca5..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ /dev/null @@ -1,210 +0,0 @@ -package sbt -package appmacro - -import Classes.Applicative -import Types.Id - -/** - * The separate hierarchy from Applicative/Monad is for two reasons. - * - * 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly. - * 2. The applicative interface is uncurried. - */ -trait Instance { - type M[x] - def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit a: AList[K]): M[Z] - def map[S, T](in: M[S], f: S => T): M[T] - def pure[T](t: () => T): M[T] -} - -trait MonadInstance extends Instance { - def flatten[T](in: M[M[T]]): M[T] -} - -import scala.reflect._ -import macros._ -import reflect.internal.annotations.compileTimeOnly - -object Instance { - final val ApplyName = "app" - final val FlattenName = "flatten" - final val PureName = "pure" - final val MapName = "map" - final val InstanceTCName = "M" - - final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) - trait Transform[C <: Context with Singleton, N[_]] { - def apply(in: C#Tree): C#Tree - } - def idTransform[C <: Context with Singleton]: Transform[C, Id] = new Transform[C, Id] { - def apply(in: C#Tree): C#Tree = in - } - - /** - * Implementation of a macro that provides a direct syntax for applicative functors and monads. - * It is intended to be used in conjunction with another macro that conditions the inputs. - * - * This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` - * This form is typically constructed by another macro that pretends to be able to get a value of type `T` - * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. - * First, it identifies the inputs that should be transformed. - * Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`. - * This wrapping is necessary because applying the first macro must preserve the original type, - * but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by - * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` - * so that it is not actually called at runtime. - * - * Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`. - * This transformation converts the input Tree to a Tree of type `M[T]`. - * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. - * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. - * The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals - * that unpacks the tuple containing the results of the inputs. - * - * The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`, - * which is an implementation of `Instance` that is statically accessible. - * An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad. - * Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module). - * The `with Singleton` part of the type verifies some cases at macro compilation time, - * while the full check for static accessibility is done at macro expansion time. - * Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`. - * With the various dependent types involved, this is not worth it. - * - * The `t` argument is the argument of the macro that will be transformed as described above. - * If the macro that calls this method is for a multi-input map (app followed by map), - * `t` should be the argument wrapped in Left. - * If this is for multi-input flatMap (app followed by flatMap), - * this should be the argument wrapped in Right. - */ - def contImpl[T, N[_]](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]], inner: Transform[c.type, N])( - implicit tt: c.WeakTypeTag[T], nt: c.WeakTypeTag[N[T]], it: c.TypeTag[i.type]): c.Expr[i.M[N[T]]] = - { - import c.universe.{ Apply => ApplyTree, _ } - - val util = ContextUtil[c.type](c) - val mTC: Type = util.extractTC(i, InstanceTCName) - val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).normalize - - // the tree for the macro argument - val (tree, treeType) = t match { - case Left(l) => (l.tree, nt.tpe.normalize) - case Right(r) => (r.tree, mttpe) - } - // the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method - // this Symbol needs to be known up front so that it can be used as the owner of synthetic vals - val functionSym = util.functionSymbol(tree.pos) - - val instanceSym = util.singleton(i) - // A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ... - val instance = Ident(instanceSym) - - val isWrapper: (String, Type, Tree) => Boolean = convert.asPredicate(c) - - // Local definitions `defs` in the macro. This is used to ensure references are to M instances defined outside of the macro call. - // Also `refCount` is the number of references, which is used to create the private, synthetic method containing the body - val defs = util.collectDefs(tree, isWrapper) - val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper) - - type In = Input[c.universe.type] - var inputs = List[In]() - - // transforms the original tree into calls to the Instance functions pure, map, ..., - // resulting in a value of type M[T] - def makeApp(body: Tree): Tree = - inputs match { - case Nil => pure(body) - case x :: Nil => single(body, x) - case xs => arbArity(body, xs) - } - - // no inputs, so construct M[T] via Instance.pure or pure+flatten - def pure(body: Tree): Tree = - { - val typeApplied = TypeApply(util.select(instance, PureName), TypeTree(treeType) :: Nil) - val f = util.createFunction(Nil, body, functionSym) - val p = ApplyTree(typeApplied, f :: Nil) - if (t.isLeft) p else flatten(p) - } - // m should have type M[M[T]] - // the returned Tree will have type M[T] - def flatten(m: Tree): Tree = - { - val typedFlatten = TypeApply(util.select(instance, FlattenName), TypeTree(tt.tpe) :: Nil) - ApplyTree(typedFlatten, m :: Nil) - } - - // calls Instance.map or flatmap directly, skipping the intermediate Instance.app that is unnecessary for a single input - def single(body: Tree, input: In): Tree = - { - val variable = input.local - val param = treeCopy.ValDef(variable, util.parameterModifiers, variable.name, variable.tpt, EmptyTree) - val typeApplied = TypeApply(util.select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil) - val f = util.createFunction(param :: Nil, body, functionSym) - val mapped = ApplyTree(typeApplied, input.expr :: f :: Nil) - if (t.isLeft) mapped else flatten(mapped) - } - - // calls Instance.app to get the values for all inputs and then calls Instance.map or flatMap to evaluate the body - def arbArity(body: Tree, inputs: List[In]): Tree = - { - val result = builder.make(c)(mTC, inputs) - val param = util.freshMethodParameter(appliedType(result.representationC, util.idTC :: Nil)) - val bindings = result.extract(param) - val f = util.createFunction(param :: Nil, Block(bindings, body), functionSym) - val ttt = TypeTree(treeType) - val typedApp = TypeApply(util.select(instance, ApplyName), TypeTree(result.representationC) :: ttt :: Nil) - val app = ApplyTree(ApplyTree(typedApp, result.input :: f :: Nil), result.alistInstance :: Nil) - if (t.isLeft) app else flatten(app) - } - - // Called when transforming the tree to add an input. - // For `qual` of type M[A], and a `selection` qual.value, - // the call is addType(Type A, Tree qual) - // The result is a Tree representing a reference to - // the bound value of the input. - def addType(tpe: Type, qual: Tree, selection: Tree): Tree = - { - qual.foreach(checkQual) - val vd = util.freshValDef(tpe, qual.pos, functionSym) - inputs ::= new Input(tpe, qual, vd) - util.refVal(selection, vd) - } - def sub(name: String, tpe: Type, qual: Tree, replace: Tree): Converted[c.type] = - { - val tag = c.WeakTypeTag[T](tpe) - convert[T](c)(name, qual)(tag) transform { tree => - addType(tpe, tree, replace) - } - } - - // applies the transformation - val tx = util.transformWrappers(tree, (n, tpe, t, replace) => sub(n, tpe, t, replace)) - // resetting attributes must be: a) local b) done here and not wider or else there are obscure errors - val tr = makeApp(inner(tx)) - c.Expr[i.M[N[T]]](tr) - } - - import Types._ - - implicit def applicativeInstance[A[_]](implicit ap: Applicative[A]): Instance { type M[x] = A[x] } = new Instance { - type M[x] = A[x] - def app[K[L[x]], Z](in: K[A], f: K[Id] => Z)(implicit a: AList[K]) = a.apply[A, Z](in, f) - def map[S, T](in: A[S], f: S => T) = ap.map(f, in) - def pure[S](s: () => S): M[S] = ap.pure(s()) - } - - type AI[A[_]] = Instance { type M[x] = A[x] } - def compose[A[_], B[_]](implicit a: AI[A], b: AI[B]): Instance { type M[x] = A[B[x]] } = new Composed[A, B](a, b) - // made a public, named, unsealed class because of trouble with macros and inference when the Instance is not an object - class Composed[A[_], B[_]](a: AI[A], b: AI[B]) extends Instance { - type M[x] = A[B[x]] - def pure[S](s: () => S): A[B[S]] = a.pure(() => b.pure(s)) - def map[S, T](in: M[S], f: S => T): M[T] = a.map(in, (bv: B[S]) => b.map(bv, f)) - def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit alist: AList[K]): A[B[Z]] = - { - val g: K[B] => B[Z] = in => b.app[K, Z](in, f) - type Split[L[x]] = K[(L ∙ B)#l] - a.app[Split, B[Z]](in, g)(AList.asplit(alist)) - } - } -} diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala deleted file mode 100644 index b5c2878f3..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ /dev/null @@ -1,71 +0,0 @@ -package sbt -package appmacro - -import Types.Id -import scala.tools.nsc.Global -import scala.reflect._ -import macros._ - -/** A `TupleBuilder` that uses a KList as the tuple representation.*/ -object KListBuilder extends TupleBuilder { - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ - - def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { - val ctx: c.type = c - val util = ContextUtil[c.type](c) - import c.universe.{ Apply => ApplyTree, _ } - import compat._ - import util._ - - val knilType = c.typeOf[KNil] - val knil = Ident(knilType.typeSymbol.companionSymbol) - val kconsTpe = c.typeOf[KCons[Int, KNil, List]] - val kcons = kconsTpe.typeSymbol.companionSymbol - val mTC: Type = mt.asInstanceOf[c.universe.Type] - val kconsTC: Type = kconsTpe.typeConstructor - - /** This is the L in the type function [L[x]] ... */ - val tcVariable: TypeSymbol = newTCVariable(util.initialOwner) - - /** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */ - def kconsType(h: Type, t: Type): Type = - appliedType(kconsTC, h :: t :: refVar(tcVariable) :: Nil) - - def bindKList(prev: ValDef, revBindings: List[ValDef], params: List[ValDef]): List[ValDef] = - params match { - case (x @ ValDef(mods, name, tpt, _)) :: xs => - val rhs = select(Ident(prev.name), "head") - val head = treeCopy.ValDef(x, mods, name, tpt, rhs) - util.setSymbol(head, x.symbol) - val tail = localValDef(TypeTree(), select(Ident(prev.name), "tail")) - val base = head :: revBindings - bindKList(tail, if (xs.isEmpty) base else tail :: base, xs) - case Nil => revBindings.reverse - } - - private[this] def makeKList(revInputs: Inputs[c.universe.type], klist: Tree, klistType: Type): Tree = - revInputs match { - case in :: tail => - val next = ApplyTree(TypeApply(Ident(kcons), TypeTree(in.tpe) :: TypeTree(klistType) :: TypeTree(mTC) :: Nil), in.expr :: klist :: Nil) - makeKList(tail, next, appliedType(kconsTC, in.tpe :: klistType :: mTC :: Nil)) - case Nil => klist - } - - /** The input trees combined in a KList */ - val klist = makeKList(inputs.reverse, knil, knilType) - - /** - * The input types combined in a KList type. The main concern is tracking the heterogeneous types. - * The type constructor is tcVariable, so that it can be applied to [X] X or M later. - * When applied to `M`, this type gives the type of the `input` KList. - */ - val klistType: Type = (inputs :\ knilType)((in, klist) => kconsType(in.tpe, klist)) - - val representationC = PolyType(tcVariable :: Nil, klistType) - val resultType = appliedType(representationC, idTC :: Nil) - val input = klist - val alistInstance: ctx.universe.Tree = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) - def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) - } -} diff --git a/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala deleted file mode 100644 index 019dc8b20..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala +++ /dev/null @@ -1,17 +0,0 @@ -package sbt -package appmacro - -import scala.reflect._ -import macros._ - -/** - * A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`) - * and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs. - */ -object MixedBuilder extends TupleBuilder { - def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = - { - val delegate = if (inputs.size > TupleNBuilder.MaxInputs) KListBuilder else TupleNBuilder - delegate.make(c)(mt, inputs) - } -} \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala deleted file mode 100644 index a6ea2d84c..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala +++ /dev/null @@ -1,57 +0,0 @@ -package sbt -package appmacro - -import Types.Id -import scala.tools.nsc.Global -import scala.reflect._ -import macros._ - -/** - * A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList` - * and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument - * and ultimately obtain a list of expressions with type `M[T]` for different types `T`. - * The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`, - * the `Tree` for the expression, and a `ValDef` that will hold the value for the input. - * - * `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult. - * First, it returns the constructed tuple data structure Tree in `input`. - * Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure. - * For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]` - * would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method - * would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`. - * - * Finally, the `extract` method provides a list of vals that extract information from the applied input. - * The type of the applied input is the type constructor applied to `Id` (`[X] X`). - * The returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand sides. - */ -trait TupleBuilder { - /** A convenience alias for a list of inputs (associated with a Universe of type U). */ - type Inputs[U <: Universe with Singleton] = List[Instance.Input[U]] - - /** Constructs a one-time use Builder for Context `c` and type constructor `tcType`. */ - def make(c: Context)(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] -} - -trait BuilderResult[C <: Context with Singleton] { - val ctx: C - import ctx.universe._ - - /** - * Represents the higher-order type constructor `[L[x]] ...` where `...` is the - * type of the data structure containing the added expressions, - * except that it is abstracted over the type constructor applied to each heterogeneous part of the type . - */ - def representationC: PolyType - - /** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/ - def alistInstance: Tree - - /** Returns the completed value containing all expressions added to the builder. */ - def input: Tree - - /* The list of definitions that extract values from a value of type `$representationC[Id]`. - * The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with - * non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/ - def extract(param: ValDef): List[ValDef] -} - diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala deleted file mode 100644 index 232174c81..000000000 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ /dev/null @@ -1,56 +0,0 @@ -package sbt -package appmacro - -import Types.Id -import scala.tools.nsc.Global -import scala.reflect._ -import macros._ - -/** - * A builder that uses a TupleN as the tuple representation. - * It is limited to tuples of size 2 to `MaxInputs`. - */ -object TupleNBuilder extends TupleBuilder { - /** The largest number of inputs that this builder can handle. */ - final val MaxInputs = 11 - final val TupleMethodName = "tuple" - - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ - - def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { - val util = ContextUtil[c.type](c) - import c.universe.{ Apply => ApplyTree, _ } - import compat._ - import util._ - - val global: Global = c.universe.asInstanceOf[Global] - val mTC: Type = mt.asInstanceOf[c.universe.Type] - - val ctx: c.type = c - val representationC: PolyType = { - val tcVariable: Symbol = newTCVariable(util.initialOwner) - val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) - val tuple = global.definitions.tupleType(tupleTypeArgs) - PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) - } - val resultType = appliedType(representationC, idTC :: Nil) - - val input: Tree = mkTuple(inputs.map(_.expr)) - val alistInstance: Tree = { - val selectTree = select(Ident(alist), TupleMethodName + inputs.size.toString) - TypeApply(selectTree, inputs.map(in => TypeTree(in.tpe))) - } - def extract(param: ValDef): List[ValDef] = bindTuple(param, Nil, inputs.map(_.local), 1) - - def bindTuple(param: ValDef, revBindings: List[ValDef], params: List[ValDef], i: Int): List[ValDef] = - params match { - case (x @ ValDef(mods, name, tpt, _)) :: xs => - val rhs = select(Ident(param.name), "_" + i.toString) - val newVal = treeCopy.ValDef(x, mods, name, tpt, rhs) - util.setSymbol(newVal, x.symbol) - bindTuple(param, newVal :: revBindings, xs, i + 1) - case Nil => revBindings.reverse - } - } -} diff --git a/util/classfile/NOTICE b/util/classfile/NOTICE deleted file mode 100644 index 83dac0b03..000000000 --- a/util/classfile/NOTICE +++ /dev/null @@ -1,8 +0,0 @@ -Simple Build Tool (sbt) -Copyright 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) - - -Portions based on code by Mike Clark in JDepend -Copyright 1999-2004 Clarkware Consulting, Inc. -Licensed under BSD-style license (see licenses/LICENSE_jdepend) diff --git a/util/classfile/src/main/scala/sbt/classfile/Analyze.scala b/util/classfile/src/main/scala/sbt/classfile/Analyze.scala deleted file mode 100644 index 52119d37d..000000000 --- a/util/classfile/src/main/scala/sbt/classfile/Analyze.scala +++ /dev/null @@ -1,150 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt -package classfile - -import scala.collection.mutable -import mutable.{ ArrayBuffer, Buffer } -import scala.annotation.tailrec -import java.io.File -import java.lang.annotation.Annotation -import java.lang.reflect.Method -import java.lang.reflect.Modifier.{ STATIC, PUBLIC, ABSTRACT } -import java.net.URL -import xsbti.DependencyContext -import xsbti.DependencyContext._ - -private[sbt] object Analyze { - def apply[T](newClasses: Seq[File], sources: Seq[File], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File, Seq[Class[_]]) => Set[String]) { - val sourceMap = sources.toSet[File].groupBy(_.getName) - - def load(tpe: String, errMsg: => Option[String]): Option[Class[_]] = - try { Some(Class.forName(tpe, false, loader)) } - catch { case e: Throwable => errMsg.foreach(msg => log.warn(msg + " : " + e.toString)); None } - - val productToSource = new mutable.HashMap[File, File] - val sourceToClassFiles = new mutable.HashMap[File, Buffer[ClassFile]] - - // parse class files and assign classes to sources. This must be done before dependencies, since the information comes - // as class->class dependencies that must be mapped back to source->class dependencies using the source+class assignment - for ( - newClass <- newClasses; - classFile = Parser(newClass); - sourceFile <- classFile.sourceFile orElse guessSourceName(newClass.getName); - source <- guessSourcePath(sourceMap, classFile, log) - ) { - analysis.generatedClass(source, newClass, classFile.className) - productToSource(newClass) = source - sourceToClassFiles.getOrElseUpdate(source, new ArrayBuffer[ClassFile]) += classFile - } - - // get class to class dependencies and map back to source to class dependencies - for ((source, classFiles) <- sourceToClassFiles) { - val publicInherited = readAPI(source, classFiles.toSeq.flatMap(c => load(c.className, Some("Error reading API from class file")))) - - def processDependency(tpe: String, context: DependencyContext): Unit = { - trapAndLog(log) { - for (url <- Option(loader.getResource(tpe.replace('.', '/') + ClassExt)); file <- urlAsFile(url, log)) { - if (url.getProtocol == "jar") - analysis.binaryDependency(file, tpe, source, context) - else { - assume(url.getProtocol == "file") - productToSource.get(file) match { - case Some(dependsOn) => analysis.sourceDependency(dependsOn, source, context) - case None => analysis.binaryDependency(file, tpe, source, context) - } - } - } - } - } - def processDependencies(tpes: Iterable[String], context: DependencyContext): Unit = tpes.foreach(tpe => processDependency(tpe, context)) - - val notInherited = classFiles.flatMap(_.types).toSet -- publicInherited - processDependencies(notInherited, DependencyByMemberRef) - processDependencies(publicInherited, DependencyByInheritance) - } - - for (source <- sources filterNot sourceToClassFiles.keySet) { - analysis.api(source, new xsbti.api.SourceAPI(Array(), Array())) - } - } - private[this] def urlAsFile(url: URL, log: Logger): Option[File] = - try IO.urlAsFile(url) - catch { - case e: Exception => - log.warn("Could not convert URL '" + url.toExternalForm + "' to File: " + e.toString) - None - } - private def trapAndLog(log: Logger)(execute: => Unit): Unit = { - try { execute } - catch { case e: Throwable => log.trace(e); log.error(e.toString) } - } - private def guessSourceName(name: String) = Some(takeToDollar(trimClassExt(name))) - private def takeToDollar(name: String) = - { - val dollar = name.indexOf('$') - if (dollar < 0) name else name.substring(0, dollar) - } - private final val ClassExt = ".class" - private def trimClassExt(name: String) = if (name.endsWith(ClassExt)) name.substring(0, name.length - ClassExt.length) else name - private def guessSourcePath(sourceNameMap: Map[String, Set[File]], classFile: ClassFile, log: Logger) = - { - val classNameParts = classFile.className.split("""\.""") - val pkg = classNameParts.init - val simpleClassName = classNameParts.last - val sourceFileName = classFile.sourceFile.getOrElse(simpleClassName.takeWhile(_ != '$').mkString("", "", ".java")) - val candidates = findSource(sourceNameMap, pkg.toList, sourceFileName) - candidates match { - case Nil => log.warn("Could not determine source for class " + classFile.className) - case head :: Nil => () - case _ => log.warn("Multiple sources matched for class " + classFile.className + ": " + candidates.mkString(", ")) - } - candidates - } - private def findSource(sourceNameMap: Map[String, Iterable[File]], pkg: List[String], sourceFileName: String): List[File] = - refine((sourceNameMap get sourceFileName).toList.flatten.map { x => (x, x.getParentFile) }, pkg.reverse) - - @tailrec private def refine(sources: List[(File, File)], pkgRev: List[String]): List[File] = - { - def make = sources.map(_._1) - if (sources.isEmpty || sources.tail.isEmpty) - make - else - pkgRev match { - case Nil => shortest(make) - case x :: xs => - val retain = sources flatMap { - case (src, pre) => - if (pre != null && pre.getName == x) - (src, pre.getParentFile) :: Nil - else - Nil - } - refine(retain, xs) - } - } - private def shortest(files: List[File]): List[File] = - if (files.isEmpty) files - else { - val fs = files.groupBy(distanceToRoot(0)) - fs(fs.keys.min) - } - - private def distanceToRoot(acc: Int)(file: File): Int = - if (file == null) acc else distanceToRoot(acc + 1)(file.getParentFile) - - private def isTopLevel(classFile: ClassFile) = classFile.className.indexOf('$') < 0 - private lazy val unit = classOf[Unit] - private lazy val strArray = List(classOf[Array[String]]) - - private def isMain(method: Method): Boolean = - method.getName == "main" && - isMain(method.getModifiers) && - method.getReturnType == unit && - method.getParameterTypes.toList == strArray - private def isMain(modifiers: Int): Boolean = (modifiers & mainModifiers) == mainModifiers && (modifiers & notMainModifiers) == 0 - - private val mainModifiers = STATIC | PUBLIC - private val notMainModifiers = ABSTRACT -} diff --git a/util/classfile/src/main/scala/sbt/classfile/ClassFile.scala b/util/classfile/src/main/scala/sbt/classfile/ClassFile.scala deleted file mode 100644 index 3f1a0d915..000000000 --- a/util/classfile/src/main/scala/sbt/classfile/ClassFile.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt -package classfile - -import Constants._ -import java.io.File - -private[sbt] trait ClassFile { - val majorVersion: Int - val minorVersion: Int - val fileName: String - val className: String - val superClassName: String - val interfaceNames: Array[String] - val accessFlags: Int - val constantPool: Array[Constant] - val fields: Array[FieldOrMethodInfo] - val methods: Array[FieldOrMethodInfo] - val attributes: Array[AttributeInfo] - val sourceFile: Option[String] - def types: Set[String] - def stringValue(a: AttributeInfo): String - - /** - * If the given fieldName represents a ConstantValue field, parses its representation from - * the constant pool and returns it. - */ - def constantValue(fieldName: String): Option[AnyRef] = - this.fields.find(_.name.exists(_ == fieldName)).toSeq.flatMap(_.attributes).collectFirst { - case ai @ classfile.AttributeInfo(Some("ConstantValue"), _) => - constantPool(Parser.entryIndex(ai)) - }.map { - case Constant(ConstantString, nextOffset, _, _) => - // follow the indirection from ConstantString to ConstantUTF8 - val nextConstant = constantPool(nextOffset) - nextConstant.value.getOrElse { - throw new IllegalStateException(s"Empty UTF8 value in constant pool: $nextConstant") - } - case constant @ Constant((ConstantFloat | ConstantLong | ConstantDouble | ConstantInteger), _, _, ref) => - ref.getOrElse { - throw new IllegalStateException(s"Empty primitive value in constant pool: $constant") - } - case constant => - throw new IllegalStateException(s"Unsupported ConstantValue type: $constant") - } -} - -private[sbt] final case class Constant(tag: Byte, nameIndex: Int, typeIndex: Int, value: Option[AnyRef]) extends NotNull { - def this(tag: Byte, nameIndex: Int, typeIndex: Int) = this(tag, nameIndex, typeIndex, None) - def this(tag: Byte, nameIndex: Int) = this(tag, nameIndex, -1) - def this(tag: Byte, value: AnyRef) = this(tag, -1, -1, Some(value)) - def wide = tag == ConstantLong || tag == ConstantDouble -} -private[sbt] final case class FieldOrMethodInfo(accessFlags: Int, name: Option[String], descriptor: Option[String], attributes: IndexedSeq[AttributeInfo]) extends NotNull { - def isStatic = (accessFlags & ACC_STATIC) == ACC_STATIC - def isPublic = (accessFlags & ACC_PUBLIC) == ACC_PUBLIC - def isMain = isPublic && isStatic && descriptor.exists(_ == "([Ljava/lang/String;)V") -} -private[sbt] final case class AttributeInfo(name: Option[String], value: Array[Byte]) extends NotNull { - def isNamed(s: String) = name.exists(s == _) - def isSignature = isNamed("Signature") - def isSourceFile = isNamed("SourceFile") -} -private[sbt] object Constants { - final val ACC_STATIC = 0x0008 - final val ACC_PUBLIC = 0x0001 - - final val JavaMagic = 0xCAFEBABE - final val ConstantUTF8 = 1 - final val ConstantUnicode = 2 - final val ConstantInteger = 3 - final val ConstantFloat = 4 - final val ConstantLong = 5 - final val ConstantDouble = 6 - final val ConstantClass = 7 - final val ConstantString = 8 - final val ConstantField = 9 - final val ConstantMethod = 10 - final val ConstantInterfaceMethod = 11 - final val ConstantNameAndType = 12 - final val ConstantMethodHandle = 15 - final val ConstantMethodType = 16 - final val ConstantInvokeDynamic = 18 - final val ClassDescriptor = 'L' -} diff --git a/util/classfile/src/main/scala/sbt/classfile/Parser.scala b/util/classfile/src/main/scala/sbt/classfile/Parser.scala deleted file mode 100644 index 16c5bf656..000000000 --- a/util/classfile/src/main/scala/sbt/classfile/Parser.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt -package classfile - -import java.io.{ DataInputStream, File, InputStream } -import scala.annotation.switch - -// Translation of jdepend.framework.ClassFileParser by Mike Clark, Clarkware Consulting, Inc. -// BSD Licensed -// -// Note that unlike the rest of sbt, some things might be null. - -import Constants._ - -private[sbt] object Parser { - def apply(file: File): ClassFile = Using.fileInputStream(file)(parse(file.getAbsolutePath)).right.get - private def parse(fileName: String)(is: InputStream): Either[String, ClassFile] = Right(parseImpl(fileName, is)) - private def parseImpl(filename: String, is: InputStream): ClassFile = - { - val in = new DataInputStream(is) - new ClassFile { - assume(in.readInt() == JavaMagic, "Invalid class file: " + fileName) - - val fileName = filename - val minorVersion: Int = in.readUnsignedShort() - val majorVersion: Int = in.readUnsignedShort() - - val constantPool = parseConstantPool(in) - val accessFlags: Int = in.readUnsignedShort() - - val className = getClassConstantName(in.readUnsignedShort()) - val superClassName = getClassConstantName(in.readUnsignedShort()) - val interfaceNames = array(in.readUnsignedShort())(getClassConstantName(in.readUnsignedShort())) - - val fields = readFieldsOrMethods() - val methods = readFieldsOrMethods() - - val attributes = array(in.readUnsignedShort())(parseAttribute()) - - lazy val sourceFile = - for (sourceFileAttribute <- attributes.find(_.isSourceFile)) yield toUTF8(entryIndex(sourceFileAttribute)) - - def stringValue(a: AttributeInfo) = toUTF8(entryIndex(a)) - - private def readFieldsOrMethods() = array(in.readUnsignedShort())(parseFieldOrMethodInfo()) - private def toUTF8(entryIndex: Int) = - { - val entry = constantPool(entryIndex) - assume(entry.tag == ConstantUTF8, "Constant pool entry is not a UTF8 type: " + entryIndex) - entry.value.get.asInstanceOf[String] - } - private def getClassConstantName(entryIndex: Int) = - { - val entry = constantPool(entryIndex) - if (entry == null) "" - else slashesToDots(toUTF8(entry.nameIndex)) - } - private def toString(index: Int) = - { - if (index <= 0) None - else Some(toUTF8(index)) - } - private def parseFieldOrMethodInfo() = - new FieldOrMethodInfo(in.readUnsignedShort(), toString(in.readUnsignedShort()), toString(in.readUnsignedShort()), - array(in.readUnsignedShort())(parseAttribute())) - private def parseAttribute() = - { - val nameIndex = in.readUnsignedShort() - val name = if (nameIndex == -1) None else Some(toUTF8(nameIndex)) - val value = array(in.readInt())(in.readByte()) - new AttributeInfo(name, value) - } - - def types = (classConstantReferences ++ fieldTypes ++ methodTypes).toSet - - private def getTypes(fieldsOrMethods: Array[FieldOrMethodInfo]) = - fieldsOrMethods.flatMap { fieldOrMethod => - descriptorToTypes(fieldOrMethod.descriptor) - } - - private def fieldTypes = getTypes(fields) - private def methodTypes = getTypes(methods) - - private def classConstantReferences = - constants.flatMap { constant => - constant.tag match { - case ConstantClass => - val name = toUTF8(constant.nameIndex) - if (name.startsWith("[")) - descriptorToTypes(Some(name)) - else - slashesToDots(name) :: Nil - case _ => Nil - } - } - private def constants = - { - def next(i: Int, list: List[Constant]): List[Constant] = - { - if (i < constantPool.length) { - val constant = constantPool(i) - next(if (constant.wide) i + 2 else i + 1, constant :: list) - } else - list - } - next(1, Nil) - } - } - } - private def array[T: scala.reflect.Manifest](size: Int)(f: => T) = Array.tabulate(size)(_ => f) - private def parseConstantPool(in: DataInputStream) = - { - val constantPoolSize = in.readUnsignedShort() - val pool = new Array[Constant](constantPoolSize) - - def parse(i: Int): Unit = - if (i < constantPoolSize) { - val constant = getConstant(in) - pool(i) = constant - parse(if (constant.wide) i + 2 else i + 1) - } - - parse(1) - pool - } - - private def getConstant(in: DataInputStream): Constant = - { - val tag = in.readByte() - - // No switch for byte scrutinees! Stupid compiler. - ((tag: Int): @switch) match { - case ConstantClass | ConstantString => new Constant(tag, in.readUnsignedShort()) - case ConstantField | ConstantMethod | ConstantInterfaceMethod | ConstantNameAndType => - new Constant(tag, in.readUnsignedShort(), in.readUnsignedShort()) - case ConstantInteger => new Constant(tag, new java.lang.Integer(in.readInt())) - case ConstantFloat => new Constant(tag, new java.lang.Float(in.readFloat())) - case ConstantLong => new Constant(tag, new java.lang.Long(in.readLong())) - case ConstantDouble => new Constant(tag, new java.lang.Double(in.readDouble())) - case ConstantUTF8 => new Constant(tag, in.readUTF()) - // TODO: proper support - case ConstantMethodHandle => - val kind = in.readByte() - val ref = in.readUnsignedShort() - new Constant(tag, -1, -1, None) - case ConstantMethodType => - val descriptorIndex = in.readUnsignedShort() - new Constant(tag, -1, -1, None) - case ConstantInvokeDynamic => - val bootstrapIndex = in.readUnsignedShort() - val nameAndTypeIndex = in.readUnsignedShort() - new Constant(tag, -1, -1, None) - case _ => sys.error("Unknown constant: " + tag) - } - } - - private def toInt(v: Byte) = if (v < 0) v + 256 else v.toInt - def entryIndex(a: AttributeInfo) = - { - require(a.value.length == 2, s"Expected two bytes for unsigned value; got: ${a.value.length}") - val Array(v0, v1) = a.value - toInt(v0) * 256 + toInt(v1) - } - - private def slashesToDots(s: String) = s.replace('/', '.') - - private def descriptorToTypes(descriptor: Option[String]) = - { - def toTypes(descriptor: String, types: List[String]): List[String] = - { - val startIndex = descriptor.indexOf(ClassDescriptor) - if (startIndex < 0) - types - else { - val endIndex = descriptor.indexOf(';', startIndex + 1) - val tpe = slashesToDots(descriptor.substring(startIndex + 1, endIndex)) - toTypes(descriptor.substring(endIndex), tpe :: types) - } - } - toTypes(descriptor.getOrElse(""), Nil) - } -} diff --git a/util/classpath/NOTICE b/util/classpath/NOTICE deleted file mode 100644 index dd4fe824e..000000000 --- a/util/classpath/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Classpath Component -Copyright 2008, 2009, 2010 Mark Harrah, Stuart Roebuck -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/classpath/src/main/scala/sbt/ModuleUtilities.scala b/util/classpath/src/main/scala/sbt/ModuleUtilities.scala deleted file mode 100644 index 9b3c8cbe2..000000000 --- a/util/classpath/src/main/scala/sbt/ModuleUtilities.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008 Mark Harrah - */ -package sbt - -object ModuleUtilities { - /** - * Reflectively loads and returns the companion object for top-level class `className` from `loader`. - * The class name should not include the `$` that scalac appends to the underlying jvm class for - * a companion object. - */ - def getObject(className: String, loader: ClassLoader): AnyRef = - { - val obj = Class.forName(className + "$", true, loader) - val singletonField = obj.getField("MODULE$") - singletonField.get(null) - } - - def getCheckedObject[T](className: String, loader: ClassLoader)(implicit mf: reflect.ClassManifest[T]): T = - mf.runtimeClass.cast(getObject(className, loader)).asInstanceOf[T] - - def getCheckedObjects[T](classNames: Seq[String], loader: ClassLoader)(implicit mf: reflect.ClassManifest[T]): Seq[(String, T)] = - classNames.map(name => (name, getCheckedObject(name, loader))) -} diff --git a/util/classpath/src/main/scala/sbt/ReflectUtilities.scala b/util/classpath/src/main/scala/sbt/ReflectUtilities.scala deleted file mode 100644 index c60772ad4..000000000 --- a/util/classpath/src/main/scala/sbt/ReflectUtilities.scala +++ /dev/null @@ -1,65 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008 David MacIver, Mark Harrah - */ -package sbt - -import scala.collection._ - -object ReflectUtilities { - /** Converts the camelCase String `name` to lowercase separated by `separator`. */ - def transformCamelCase(name: String, separator: Char): String = - { - val buffer = new StringBuilder - for (char <- name) { - import java.lang.Character._ - if (isUpperCase(char)) { - buffer += separator - buffer += toLowerCase(char) - } else - buffer += char - } - buffer.toString - } - - def ancestry(clazz: Class[_]): List[Class[_]] = - if (clazz == classOf[AnyRef] || !classOf[AnyRef].isAssignableFrom(clazz)) List(clazz) - else clazz :: ancestry(clazz.getSuperclass); - - def fields(clazz: Class[_]) = - mutable.OpenHashMap(ancestry(clazz). - flatMap(_.getDeclaredFields). - map(f => (f.getName, f)): _*) - - /** - * Collects all `val`s of type `T` defined on value `self`. - * The returned Map maps the name of each `val` to its value. - * This depends on scalac implementation details to determine what is a `val` using only Java reflection. - */ - def allValsC[T](self: AnyRef, clazz: Class[T]): immutable.SortedMap[String, T] = - { - var mappings = new immutable.TreeMap[String, T] - val correspondingFields = fields(self.getClass) - for (method <- self.getClass.getMethods) { - if (method.getParameterTypes.isEmpty && clazz.isAssignableFrom(method.getReturnType)) { - for (field <- correspondingFields.get(method.getName) if field.getType == method.getReturnType) { - val value = method.invoke(self).asInstanceOf[T] - if (value == null) throw new UninitializedVal(method.getName, method.getDeclaringClass.getName) - mappings += ((method.getName, value)) - } - } - } - mappings - } - - /** - * Collects all `val`s of type `T` defined on value `self`. - * The returned Map maps the name of each `val` to its value. - * This requires an available `Manifest` for `T` and depends on scalac implementation details to determine - * what is a `val` using only Java reflection. - */ - def allVals[T](self: AnyRef)(implicit mt: scala.reflect.Manifest[T]): immutable.SortedMap[String, T] = - allValsC(self, mt.runtimeClass).asInstanceOf[immutable.SortedMap[String, T]] -} - -/** An exception to indicate that while traversing the `val`s for an instance of `className`, the `val` named `valName` was `null`. */ -final class UninitializedVal(val valName: String, val className: String) extends RuntimeException("val " + valName + " in class " + className + " was null.\nThis is probably an initialization problem and a 'lazy val' should be used.") \ No newline at end of file diff --git a/util/classpath/src/main/scala/sbt/ScalaInstance.scala b/util/classpath/src/main/scala/sbt/ScalaInstance.scala deleted file mode 100644 index b2bf5bc3d..000000000 --- a/util/classpath/src/main/scala/sbt/ScalaInstance.scala +++ /dev/null @@ -1,142 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt - -import java.io.File -import xsbti.ArtifactInfo.{ ScalaCompilerID, ScalaLibraryID, ScalaOrganization } - -/** - * Represents the source for Scala classes for a given version. The reason both a ClassLoader and the jars are required - * is that the compiler requires the location of the library jar on the (boot)classpath and the loader is used - * for the compiler itself. - * The 'version' field is the version used to obtain the Scala classes. This is typically the version for the maven repository. - * The 'actualVersion' field should be used to uniquely identify the compiler. It is obtained from the compiler.properties file. - * - * This should be constructed via the ScalaInstance.apply methods. The primary constructor is deprecated. - */ -final class ScalaInstance(val version: String, val loader: ClassLoader, val libraryJar: File, - @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") val compilerJar: File, - @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") val extraJars: Seq[File], - val explicitActual: Option[String]) extends xsbti.compile.ScalaInstance { - /** - * This tells us if the scalaInstance is from a managed (i.e. ivy-resolved) scala *or* - * if it's a free-floating ScalaInstance, in which case we need to do tricks to the classpaths we find - * because it won't be on them. - */ - final def isManagedVersion = explicitActual.isDefined - // These are to implement xsbti.ScalaInstance - @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") - def otherJars: Array[File] = extraJars.toArray - def allJars: Array[File] = jars.toArray - - require(version.indexOf(' ') < 0, "Version cannot contain spaces (was '" + version + "')") - def jars = libraryJar :: compilerJar :: extraJars.toList - /** Gets the version of Scala in the compiler.properties file from the loader. This version may be different than that given by 'version'*/ - lazy val actualVersion = explicitActual getOrElse ScalaInstance.actualVersion(loader)("\n version " + version + ", " + jarStrings) - def jarStrings = "library jar: " + libraryJar + ", compiler jar: " + compilerJar - override def toString = "Scala instance{version label " + version + ", actual version " + actualVersion + ", " + jarStrings + "}" -} -object ScalaInstance { - val ScalaOrg = ScalaOrganization - val VersionPrefix = "version " - - def apply(org: String, version: String, launcher: xsbti.Launcher): ScalaInstance = - // Due to incompatibility with previous launchers if scalaOrg has default value revert to an existing method - if (org == ScalaOrg) - apply(version, launcher) - else try { - apply(version, launcher.getScala(version, "", org)) - } catch { - case x: NoSuchMethodError => sys.error("Incompatible version of the xsbti.Launcher interface. Use an sbt 0.12+ launcher instead.") - } - - /** Creates a ScalaInstance using the given provider to obtain the jars and loader.*/ - def apply(version: String, launcher: xsbti.Launcher): ScalaInstance = - apply(version, launcher.getScala(version)) - def apply(version: String, provider: xsbti.ScalaProvider): ScalaInstance = - new ScalaInstance(version, provider.loader, provider.libraryJar, provider.compilerJar, (provider.jars.toSet - provider.libraryJar - provider.compilerJar).toSeq, None) - - def apply(scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = - apply(libraryJar(scalaHome), compilerJar(scalaHome), launcher, allJars(scalaHome): _*) - - def apply(scalaHome: File)(classLoader: List[File] => ClassLoader): ScalaInstance = - apply(libraryJar(scalaHome), compilerJar(scalaHome), allJars(scalaHome): _*)(classLoader) - - def apply(version: String, scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = - apply(version, libraryJar(scalaHome), compilerJar(scalaHome), launcher, allJars(scalaHome): _*) - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = - apply(libraryJar, compilerJar, extraJars: _*)(scalaLoader(launcher)) - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = - { - val loader = classLoader(libraryJar :: compilerJar :: extraJars.toList) - val version = actualVersion(loader)(" (library jar " + libraryJar.getAbsolutePath + ")") - new ScalaInstance(version, loader, libraryJar, compilerJar, extraJars, None) - } - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(version: String, libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = - apply(version, None, libraryJar, compilerJar, launcher, extraJars: _*) - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(version: String, libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = - apply(version, None, libraryJar, compilerJar, extraJars: _*)(classLoader) - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = - apply(version, explicitActual, libraryJar, compilerJar, extraJars: _*)(scalaLoader(launcher)) - - @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") - def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = - new ScalaInstance(version, classLoader(libraryJar :: compilerJar :: extraJars.toList), libraryJar, compilerJar, extraJars, explicitActual) - - @deprecated("Cannot be reliably provided for modularized Scala.", "0.13.0") - def extraJars(scalaHome: File): Seq[File] = - optScalaJar(scalaHome, "jline.jar") ++ - optScalaJar(scalaHome, "fjbg.jar") ++ - optScalaJar(scalaHome, "scala-reflect.jar") - - def allJars(scalaHome: File): Seq[File] = IO.listFiles(scalaLib(scalaHome)).filter(f => !blacklist(f.getName)) - private[this] def scalaLib(scalaHome: File): File = new File(scalaHome, "lib") - private[this] val blacklist: Set[String] = Set("scala-actors.jar", "scalacheck.jar", "scala-partest.jar", "scala-partest-javaagent.jar", "scalap.jar", "scala-swing.jar") - - private def compilerJar(scalaHome: File) = scalaJar(scalaHome, "scala-compiler.jar") - private def libraryJar(scalaHome: File) = scalaJar(scalaHome, "scala-library.jar") - - def scalaJar(scalaHome: File, name: String) = new File(scalaLib(scalaHome), name) - - @deprecated("No longer used.", "0.13.0") - def optScalaJar(scalaHome: File, name: String): List[File] = - { - val jar = scalaJar(scalaHome, name) - if (jar.isFile) jar :: Nil else Nil - } - - /** Gets the version of Scala in the compiler.properties file from the loader.*/ - private def actualVersion(scalaLoader: ClassLoader)(label: String) = - try fastActualVersion(scalaLoader) - catch { case e: Exception => slowActualVersion(scalaLoader)(label) } - private def slowActualVersion(scalaLoader: ClassLoader)(label: String) = - { - val v = try { Class.forName("scala.tools.nsc.Properties", true, scalaLoader).getMethod("versionString").invoke(null).toString } - catch { case cause: Exception => throw new InvalidScalaInstance("Scala instance doesn't exist or is invalid: " + label, cause) } - if (v.startsWith(VersionPrefix)) v.substring(VersionPrefix.length) else v - } - private def fastActualVersion(scalaLoader: ClassLoader): String = - { - val stream = scalaLoader.getResourceAsStream("compiler.properties") - try { - val props = new java.util.Properties - props.load(stream) - props.getProperty("version.number") - } finally stream.close() - } - import java.net.{ URL, URLClassLoader } - private def scalaLoader(launcher: xsbti.Launcher): Seq[File] => ClassLoader = jars => - new URLClassLoader(jars.map(_.toURI.toURL).toArray[URL], launcher.topLoader) -} -class InvalidScalaInstance(message: String, cause: Throwable) extends RuntimeException(message, cause) \ No newline at end of file diff --git a/util/classpath/src/main/scala/sbt/classpath/ClassLoaderCache.scala b/util/classpath/src/main/scala/sbt/classpath/ClassLoaderCache.scala deleted file mode 100644 index 2e489eb6f..000000000 --- a/util/classpath/src/main/scala/sbt/classpath/ClassLoaderCache.scala +++ /dev/null @@ -1,41 +0,0 @@ -package sbt.classpath - -import java.lang.ref.{ Reference, SoftReference } -import java.io.File -import java.net.URLClassLoader -import java.util.HashMap - -// Hack for testing only -private[sbt] final class ClassLoaderCache(val commonParent: ClassLoader) { - private[this] val delegate = new HashMap[List[File], Reference[CachedClassLoader]] - - /** - * Returns a ClassLoader with `commonParent` as a parent and that will load classes from classpath `files`. - * The returned ClassLoader may be cached from a previous call if the last modified time of all `files` is unchanged. - * This method is thread-safe. - */ - def apply(files: List[File]): ClassLoader = synchronized { - val tstamps = files.map(_.lastModified) - getFromReference(files, tstamps, delegate.get(files)) - } - - private[this] def getFromReference(files: List[File], stamps: List[Long], existingRef: Reference[CachedClassLoader]) = - if (existingRef eq null) - newEntry(files, stamps) - else - get(files, stamps, existingRef.get) - - private[this] def get(files: List[File], stamps: List[Long], existing: CachedClassLoader): ClassLoader = - if (existing == null || stamps != existing.timestamps) { - newEntry(files, stamps) - } else - existing.loader - - private[this] def newEntry(files: List[File], stamps: List[Long]): ClassLoader = - { - val loader = new URLClassLoader(files.map(_.toURI.toURL).toArray, commonParent) - delegate.put(files, new SoftReference(new CachedClassLoader(loader, files, stamps))) - loader - } -} -private[sbt] final class CachedClassLoader(val loader: ClassLoader, val files: List[File], val timestamps: List[Long]) diff --git a/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala b/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala deleted file mode 100644 index 2a1362f7e..000000000 --- a/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala +++ /dev/null @@ -1,180 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt -package classpath - -import java.io.File -import java.net.{ URL, URLClassLoader } -import annotation.tailrec - -/** - * This is a starting point for defining a custom ClassLoader. Override 'doLoadClass' to define - * loading a class that has not yet been loaded. - */ -abstract class LoaderBase(urls: Seq[URL], parent: ClassLoader) extends URLClassLoader(urls.toArray, parent) { - require(parent != null) // included because a null parent is legitimate in Java - @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - val loaded = findLoadedClass(className) - val found = - if (loaded == null) - doLoadClass(className) - else - loaded - - if (resolve) - resolveClass(found) - found - } - /** Provides the implementation of finding a class that has not yet been loaded.*/ - protected def doLoadClass(className: String): Class[_] - /** Provides access to the default implementation of 'loadClass'.*/ - protected final def defaultLoadClass(className: String): Class[_] = super.loadClass(className, false) -} - -/** Searches self first before delegating to the parent.*/ -final class SelfFirstLoader(classpath: Seq[URL], parent: ClassLoader) extends LoaderBase(classpath, parent) { - @throws(classOf[ClassNotFoundException]) - override final def doLoadClass(className: String): Class[_] = - { - try { findClass(className) } - catch { case _: ClassNotFoundException => defaultLoadClass(className) } - } -} - -/** Doesn't load any classes itself, but instead verifies that all classes loaded through `parent` either come from `root` or `classpath`.*/ -final class ClasspathFilter(parent: ClassLoader, root: ClassLoader, classpath: Set[File]) extends ClassLoader(parent) { - override def toString = - s"""|ClasspathFilter( - | parent = $parent - | root = $root - | cp = $classpath - |)""".stripMargin - - private[this] val directories: Seq[File] = classpath.toSeq.filter(_.isDirectory) - override def loadClass(className: String, resolve: Boolean): Class[_] = - { - val c = super.loadClass(className, resolve) - if (includeLoader(c.getClassLoader, root) || fromClasspath(c)) - c - else - throw new ClassNotFoundException(className) - } - private[this] def fromClasspath(c: Class[_]): Boolean = - { - val codeSource = c.getProtectionDomain.getCodeSource - (codeSource eq null) || - onClasspath(codeSource.getLocation) - } - private[this] def onClasspath(src: URL): Boolean = - (src eq null) || ( - IO.urlAsFile(src) match { - case Some(f) => classpath(f) || directories.exists(dir => IO.relativize(dir, f).isDefined) - case None => false - } - ) - - override def getResource(name: String): URL = { - val u = super.getResource(name) - if (onClasspath(u)) u else null - } - - override def getResources(name: String): java.util.Enumeration[URL] = - { - import collection.convert.WrapAsScala.{ enumerationAsScalaIterator => asIt } - import collection.convert.WrapAsJava.{ asJavaEnumeration => asEn } - val us = super.getResources(name) - if (us ne null) asEn(asIt(us).filter(onClasspath)) else null - } - - @tailrec private[this] def includeLoader(c: ClassLoader, base: ClassLoader): Boolean = - (base ne null) && ( - (c eq base) || includeLoader(c, base.getParent) - ) -} - -/** - * Delegates class loading to `parent` for all classes included by `filter`. An attempt to load classes excluded by `filter` - * results in a `ClassNotFoundException`. - */ -final class FilteredLoader(parent: ClassLoader, filter: ClassFilter) extends ClassLoader(parent) { - require(parent != null) // included because a null parent is legitimate in Java - def this(parent: ClassLoader, excludePackages: Iterable[String]) = this(parent, new ExcludePackagesFilter(excludePackages)) - - @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - if (filter.include(className)) - super.loadClass(className, resolve) - else - throw new ClassNotFoundException(className) - } -} - -/** Defines a filter on class names. */ -trait ClassFilter { - def include(className: String): Boolean -} -abstract class PackageFilter(packages: Iterable[String]) extends ClassFilter { - require(packages.forall(_.endsWith("."))) - protected final def matches(className: String): Boolean = packages.exists(className.startsWith) -} -/** - * Excludes class names that begin with one of the packages in `exclude`. - * Each package name in `packages` must end with a `.` - */ -final class ExcludePackagesFilter(exclude: Iterable[String]) extends PackageFilter(exclude) { - def include(className: String): Boolean = !matches(className) -} - -/** - * Includes class names that begin with one of the packages in `include`. - * Each package name in `include` must end with a `.` - */ -final class IncludePackagesFilter(include: Iterable[String]) extends PackageFilter(include) { - def include(className: String): Boolean = matches(className) -} - -/** - * Configures a [[NativeCopyLoader]]. - * The loader will provide native libraries listed in `explicitLibraries` and on `searchPaths` by copying them to `tempDirectory`. - * If `tempDirectory` is unique to the class loader, this ensures that the class loader gets a unique path for - * the native library and avoids the restriction on a native library being loaded by a single class loader. - */ -final class NativeCopyConfig(val tempDirectory: File, val explicitLibraries: Seq[File], val searchPaths: Seq[File]) - -/** - * Loads native libraries from a temporary location in order to work around the jvm native library uniqueness restriction. - * See [[NativeCopyConfig]] for configuration details. - */ -trait NativeCopyLoader extends ClassLoader { - /** Configures this loader. See [[NativeCopyConfig]] for details. */ - protected val config: NativeCopyConfig - import config._ - - private[this] val mapped = new collection.mutable.HashMap[String, String] - - override protected def findLibrary(name: String): String = - synchronized { mapped.getOrElseUpdate(name, findLibrary0(name)) } - - private[this] def findLibrary0(name: String): String = - { - val mappedName = System.mapLibraryName(name) - val explicit = explicitLibraries.filter(_.getName == mappedName).toStream - val search = searchPaths.toStream flatMap relativeLibrary(mappedName) - (explicit ++ search).headOption.map(copy).orNull - } - private[this] def relativeLibrary(mappedName: String)(base: File): Seq[File] = - { - val f = new File(base, mappedName) - if (f.isFile) f :: Nil else Nil - } - private[this] def copy(f: File): String = - { - val target = new File(tempDirectory, f.getName) - IO.copyFile(f, target) - target.getAbsolutePath - } -} \ No newline at end of file diff --git a/util/classpath/src/main/scala/sbt/classpath/ClasspathUtilities.scala b/util/classpath/src/main/scala/sbt/classpath/ClasspathUtilities.scala deleted file mode 100644 index dd29d7f77..000000000 --- a/util/classpath/src/main/scala/sbt/classpath/ClasspathUtilities.scala +++ /dev/null @@ -1,121 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt -package classpath - -import java.lang.ref.{ Reference, SoftReference, WeakReference } -import java.io.File -import java.net.{ URI, URL, URLClassLoader } -import java.util.Collections -import scala.collection.{ mutable, JavaConversions, Set } -import mutable.{ HashSet, ListBuffer } -import IO.{ createTemporaryDirectory, write } - -object ClasspathUtilities { - def toLoader(finder: PathFinder): ClassLoader = toLoader(finder, rootLoader) - def toLoader(finder: PathFinder, parent: ClassLoader): ClassLoader = new URLClassLoader(finder.getURLs, parent) - - def toLoader(paths: Seq[File]): ClassLoader = toLoader(paths, rootLoader) - def toLoader(paths: Seq[File], parent: ClassLoader): ClassLoader = new URLClassLoader(Path.toURLs(paths), parent) - - def toLoader(paths: Seq[File], parent: ClassLoader, resourceMap: Map[String, String]): ClassLoader = - new URLClassLoader(Path.toURLs(paths), parent) with RawResources { override def resources = resourceMap } - - def toLoader(paths: Seq[File], parent: ClassLoader, resourceMap: Map[String, String], nativeTemp: File): ClassLoader = - new URLClassLoader(Path.toURLs(paths), parent) with RawResources with NativeCopyLoader { - override def resources = resourceMap - override val config = new NativeCopyConfig(nativeTemp, paths, javaLibraryPaths) - override def toString = - s"""|URLClassLoader with NativeCopyLoader with RawResources( - | urls = $paths, - | parent = $parent, - | resourceMap = ${resourceMap.keySet}, - | nativeTemp = $nativeTemp - |)""".stripMargin - } - - def javaLibraryPaths: Seq[File] = IO.parseClasspath(System.getProperty("java.library.path")) - - lazy val rootLoader = - { - def parent(loader: ClassLoader): ClassLoader = - { - val p = loader.getParent - if (p eq null) loader else parent(p) - } - val systemLoader = ClassLoader.getSystemClassLoader - if (systemLoader ne null) parent(systemLoader) - else parent(getClass.getClassLoader) - } - lazy val xsbtiLoader = classOf[xsbti.Launcher].getClassLoader - - final val AppClassPath = "app.class.path" - final val BootClassPath = "boot.class.path" - - def createClasspathResources(classpath: Seq[File], instance: ScalaInstance): Map[String, String] = - createClasspathResources(classpath, instance.jars) - - def createClasspathResources(appPaths: Seq[File], bootPaths: Seq[File]): Map[String, String] = - { - def make(name: String, paths: Seq[File]) = name -> Path.makeString(paths) - Map(make(AppClassPath, appPaths), make(BootClassPath, bootPaths)) - } - - private[sbt] def filterByClasspath(classpath: Seq[File], loader: ClassLoader): ClassLoader = - new ClasspathFilter(loader, xsbtiLoader, classpath.toSet) - - def makeLoader(classpath: Seq[File], instance: ScalaInstance): ClassLoader = - filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance)) - - def makeLoader(classpath: Seq[File], instance: ScalaInstance, nativeTemp: File): ClassLoader = - filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance, nativeTemp)) - - def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance): ClassLoader = - toLoader(classpath, parent, createClasspathResources(classpath, instance)) - - def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance, nativeTemp: File): ClassLoader = - toLoader(classpath, parent, createClasspathResources(classpath, instance), nativeTemp) - - private[sbt] def printSource(c: Class[_]) = - println(c.getName + " loader=" + c.getClassLoader + " location=" + IO.classLocationFile(c)) - - def isArchive(file: File): Boolean = isArchive(file, contentFallback = false) - - def isArchive(file: File, contentFallback: Boolean): Boolean = - file.isFile && (isArchiveName(file.getName) || (contentFallback && hasZipContent(file))) - - def isArchiveName(fileName: String) = fileName.endsWith(".jar") || fileName.endsWith(".zip") - - def hasZipContent(file: File): Boolean = try { - Using.fileInputStream(file) { in => - (in.read() == 0x50) && - (in.read() == 0x4b) && - (in.read() == 0x03) && - (in.read() == 0x04) - } - } catch { case e: Exception => false } - - /** Returns all entries in 'classpath' that correspond to a compiler plugin.*/ - private[sbt] def compilerPlugins(classpath: Seq[File]): Iterable[File] = - { - import collection.JavaConversions._ - val loader = new URLClassLoader(Path.toURLs(classpath)) - loader.getResources("scalac-plugin.xml").toList.flatMap(asFile(true)) - } - /** Converts the given URL to a File. If the URL is for an entry in a jar, the File for the jar is returned. */ - private[sbt] def asFile(url: URL): List[File] = asFile(false)(url) - private[sbt] def asFile(jarOnly: Boolean)(url: URL): List[File] = - { - try { - url.getProtocol match { - case "file" if !jarOnly => IO.toFile(url) :: Nil - case "jar" => - val path = url.getPath - val end = path.indexOf('!') - new File(new URI(if (end == -1) path else path.substring(0, end))) :: Nil - case _ => Nil - } - } catch { case e: Exception => Nil } - } -} diff --git a/util/classpath/src/main/scala/sbt/classpath/DualLoader.scala b/util/classpath/src/main/scala/sbt/classpath/DualLoader.scala deleted file mode 100644 index 2b568cef0..000000000 --- a/util/classpath/src/main/scala/sbt/classpath/DualLoader.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt -package classpath - -import java.net.URL -import java.util.Enumeration -import java.util.Collections - -/** A class loader that always fails to load classes and resources. */ -final class NullLoader extends ClassLoader { - override final def loadClass(className: String, resolve: Boolean): Class[_] = throw new ClassNotFoundException("No classes can be loaded from the null loader") - override def getResource(name: String): URL = null - override def getResources(name: String): Enumeration[URL] = Collections.enumeration(Collections.emptyList()) - override def toString = "NullLoader" -} - -/** Exception thrown when `loaderA` and `loaderB` load a different Class for the same name. */ -class DifferentLoaders(message: String, val loaderA: ClassLoader, val loaderB: ClassLoader) extends ClassNotFoundException(message) - -/** - * A ClassLoader with two parents `parentA` and `parentB`. The predicates direct lookups towards one parent or the other. - * - * If `aOnlyClasses` returns `true` for a class name, class lookup delegates to `parentA` only. - * Otherwise, if `bOnlyClasses` returns `true` for a class name, class lookup delegates to `parentB` only. - * If both `aOnlyClasses` and `bOnlyClasses` are `false` for a given class name, both class loaders must load the same Class or - * a [[DifferentLoaders]] exception is thrown. - * - * If `aOnlyResources` is `true` for a resource path, lookup delegates to `parentA` only. - * Otherwise, if `bOnlyResources` is `true` for a resource path, lookup delegates to `parentB` only. - * If neither are `true` for a resource path and either `parentA` or `parentB` return a valid URL, that valid URL is returned. - */ -class DualLoader(parentA: ClassLoader, aOnlyClasses: String => Boolean, aOnlyResources: String => Boolean, - parentB: ClassLoader, bOnlyClasses: String => Boolean, bOnlyResources: String => Boolean) extends ClassLoader(new NullLoader) { - def this(parentA: ClassLoader, aOnly: String => Boolean, parentB: ClassLoader, bOnly: String => Boolean) = - this(parentA, aOnly, aOnly, parentB, bOnly, bOnly) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - val c = - if (aOnlyClasses(className)) - parentA.loadClass(className) - else if (bOnlyClasses(className)) - parentB.loadClass(className) - else { - val classA = parentA.loadClass(className) - val classB = parentB.loadClass(className) - if (classA.getClassLoader eq classB.getClassLoader) - classA - else - throw new DifferentLoaders("Parent class loaders returned different classes for '" + className + "'", classA.getClassLoader, classB.getClassLoader) - } - if (resolve) - resolveClass(c) - c - } - override def getResource(name: String): URL = - { - if (aOnlyResources(name)) - parentA.getResource(name) - else if (bOnlyResources(name)) - parentB.getResource(name) - else { - val urlA = parentA.getResource(name) - val urlB = parentB.getResource(name) - if (urlA eq null) - urlB - else - urlA - } - } - override def getResources(name: String): Enumeration[URL] = - { - if (aOnlyResources(name)) - parentA.getResources(name) - else if (bOnlyResources(name)) - parentB.getResources(name) - else { - val urlsA = parentA.getResources(name) - val urlsB = parentB.getResources(name) - if (!urlsA.hasMoreElements) - urlsB - else if (!urlsB.hasMoreElements) - urlsA - else - new DualEnumeration(urlsA, urlsB) - } - } - - override def toString = s"DualLoader(a = $parentA, b = $parentB)" -} - -/** Concatenates `a` and `b` into a single `Enumeration`.*/ -final class DualEnumeration[T](a: Enumeration[T], b: Enumeration[T]) extends Enumeration[T] { - // invariant: current.hasMoreElements or current eq b - private[this] var current = if (a.hasMoreElements) a else b - def hasMoreElements = current.hasMoreElements - def nextElement = - { - val element = current.nextElement - if (!current.hasMoreElements) - current = b - element - } -} diff --git a/util/classpath/src/main/scala/sbt/classpath/RawURL.scala b/util/classpath/src/main/scala/sbt/classpath/RawURL.scala deleted file mode 100644 index b4094553f..000000000 --- a/util/classpath/src/main/scala/sbt/classpath/RawURL.scala +++ /dev/null @@ -1,65 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package classpath - -import java.io.{ ByteArrayInputStream, InputStream } -import java.net.{ Proxy, URL, URLConnection, URLStreamHandler } -import java.util.Enumeration - -object RawURL { - /** - * Constructs a URL with scheme `raw` and path `file` that will return the bytes for `value` in the platform default encoding - * when a connection to the URL is opened. - */ - def apply(file: String, value: String): URL = - apply(file, value.getBytes) - - /** Constructs a URL with scheme `raw` and path `file` that will return the bytes `value` when a connection to the URL is opened. */ - def apply(file: String, value: Array[Byte]): URL = - apply(file)(new ByteArrayInputStream(value)) - - /** - * Constructs a URL with scheme `raw` and path `file` that will use `value` to construct the `InputStream` used when a connection - * to the URL is opened. - */ - def apply(file: String)(value: => InputStream): URL = - new URL("raw", null, -1, file, new RawStreamHandler(value)) - - private[this] final class RawStreamHandler(value: => InputStream) extends URLStreamHandler { - override protected def openConnection(url: URL, p: Proxy): URLConnection = - openConnection(url) - override protected def openConnection(url: URL): URLConnection = - new URLConnection(url) { - private lazy val in = value - def connect(): Unit = in - override def getInputStream = in - } - } -} - -/** A ClassLoader that looks up resource requests in a `Map` prior to the base ClassLoader's resource lookups. */ -trait RawResources extends FixedResources { - /** The map from resource paths to the raw String content to provide via the URL returned by [[findResource]] or [[findResources]]. */ - protected def resources: Map[String, String] - override protected final val resourceURL = resources.transform(RawURL.apply) -} - -/** A ClassLoader that looks up resource requests in a `Map` prior to the base ClassLoader's resource lookups. */ -trait FixedResources extends ClassLoader { - /** The map from resource paths to URL to provide in [[findResource]] and [[findResources]]. */ - protected def resourceURL: Map[String, URL] - - override def findResource(s: String): URL = resourceURL.getOrElse(s, super.findResource(s)) - - import java.util.Collections.{ enumeration, singletonList } - override def findResources(s: String): Enumeration[URL] = - { - val sup = super.findResources(s) - resourceURL.get(s) match { - case Some(url) => new DualEnumeration(enumeration(singletonList(url)), sup) - case None => sup - } - } -} diff --git a/util/collection/NOTICE b/util/collection/NOTICE deleted file mode 100644 index 428020987..000000000 --- a/util/collection/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Collection Component -Copyright 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/AList.scala b/util/collection/src/main/scala/sbt/AList.scala deleted file mode 100644 index 24368219b..000000000 --- a/util/collection/src/main/scala/sbt/AList.scala +++ /dev/null @@ -1,212 +0,0 @@ -package sbt - -import Classes.Applicative -import Types._ - -/** - * An abstraction over a higher-order type constructor `K[x[y]]` with the purpose of abstracting - * over heterogeneous sequences like `KList` and `TupleN` with elements with a common type - * constructor as well as homogeneous sequences `Seq[M[T]]`. - */ -trait AList[K[L[x]]] { - def transform[M[_], N[_]](value: K[M], f: M ~> N): K[N] - def traverse[M[_], N[_], P[_]](value: K[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[K[P]] - def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A - - def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil) - def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] = - a.map(f, traverse[M, M, Id](value, idK[M])(a)) -} -object AList { - type Empty = AList[({ type l[L[x]] = Unit })#l] - /** AList for Unit, which represents a sequence that is always empty.*/ - val empty: Empty = new Empty { - def transform[M[_], N[_]](in: Unit, f: M ~> N) = () - def foldr[M[_], T](in: Unit, f: (M[_], T) => T, init: T) = init - override def apply[M[_], C](in: Unit, f: Unit => C)(implicit app: Applicative[M]): M[C] = app.pure(f(())) - def traverse[M[_], N[_], P[_]](in: Unit, f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Unit] = np.pure(()) - } - - type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l] - /** AList for a homogeneous sequence. */ - def seq[T]: SeqList[T] = new SeqList[T] { - def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T]) - def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t)) - override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = - { - def loop[V](in: List[M[T]], g: List[T] => V): M[V] = - in match { - case Nil => ap.pure(g(Nil)) - case x :: xs => - val h = (ts: List[T]) => (t: T) => g(t :: ts) - ap.apply(loop(xs, h), x) - } - loop(s, f) - } - def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ??? - } - - /** AList for the arbitrary arity data structure KList. */ - def klist[KL[M[_]] <: KList[M] { type Transform[N[_]] = KL[N] }]: AList[KL] = new AList[KL] { - def transform[M[_], N[_]](k: KL[M], f: M ~> N) = k.transform(f) - def foldr[M[_], T](k: KL[M], f: (M[_], T) => T, init: T): T = k.foldr(f, init) - override def apply[M[_], C](k: KL[M], f: KL[Id] => C)(implicit app: Applicative[M]): M[C] = k.apply(f)(app) - def traverse[M[_], N[_], P[_]](k: KL[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KL[P]] = k.traverse[N, P](f)(np) - override def toList[M[_]](k: KL[M]) = k.toList - } - - /** AList for a single value. */ - type Single[A] = AList[({ type l[L[x]] = L[A] })#l] - def single[A]: Single[A] = new Single[A] { - def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a) - def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init) - def traverse[M[_], N[_], P[_]](a: M[A], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[P[A]] = f(a) - } - - type ASplit[K[L[x]], B[x]] = AList[({ type l[L[x]] = K[(L ∙ B)#l] })#l] - /** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/ - def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] { - type Split[L[x]] = K[(L ∙ B)#l] - def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] = - base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f)) - - def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = - { - val g = nestCon[M, (N ∙ P)#l, B](f) - base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) - } - - def foldr[M[_], A](value: Split[M], f: (M[_], A) => A, init: A): A = - base.foldr[(M ∙ B)#l, A](value, f, init) - } - - // TODO: auto-generate - sealed trait T2K[A, B] { type l[L[x]] = (L[A], L[B]) } - type T2List[A, B] = AList[T2K[A, B]#l] - def tuple2[A, B]: T2List[A, B] = new T2List[A, B] { - type T2[M[_]] = (M[A], M[B]) - def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2)) - def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init)) - def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = - { - val g = (Tuple2.apply[P[A], P[B]] _).curried - np.apply(np.map(g, f(t._1)), f(t._2)) - } - } - - sealed trait T3K[A, B, C] { type l[L[x]] = (L[A], L[B], L[C]) } - type T3List[A, B, C] = AList[T3K[A, B, C]#l] - def tuple3[A, B, C]: T3List[A, B, C] = new T3List[A, B, C] { - type T3[M[_]] = (M[A], M[B], M[C]) - def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3)) - def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init))) - def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = - { - val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried - np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)) - } - } - - sealed trait T4K[A, B, C, D] { type l[L[x]] = (L[A], L[B], L[C], L[D]) } - type T4List[A, B, C, D] = AList[T4K[A, B, C, D]#l] - def tuple4[A, B, C, D]: T4List[A, B, C, D] = new T4List[A, B, C, D] { - type T4[M[_]] = (M[A], M[B], M[C], M[D]) - def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4)) - def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init)))) - def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = - { - val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried - np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) - } - } - - sealed trait T5K[A, B, C, D, E] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E]) } - type T5List[A, B, C, D, E] = AList[T5K[A, B, C, D, E]#l] - def tuple5[A, B, C, D, E]: T5List[A, B, C, D, E] = new T5List[A, B, C, D, E] { - type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E]) - def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5)) - def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) - def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = - { - val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried - np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)) - } - } - - sealed trait T6K[A, B, C, D, E, F] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F]) } - type T6List[A, B, C, D, E, F] = AList[T6K[A, B, C, D, E, F]#l] - def tuple6[A, B, C, D, E, F]: T6List[A, B, C, D, E, F] = new T6List[A, B, C, D, E, F] { - type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F]) - def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) - def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) - def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = - { - val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) - } - } - - sealed trait T7K[A, B, C, D, E, F, G] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) } - type T7List[A, B, C, D, E, F, G] = AList[T7K[A, B, C, D, E, F, G]#l] - def tuple7[A, B, C, D, E, F, G]: T7List[A, B, C, D, E, F, G] = new T7List[A, B, C, D, E, F, G] { - type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G]) - def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) - def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) - def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = - { - val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) - } - } - sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) } - type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l] - def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] { - type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) - def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) - def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) - def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = - { - val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) - } - } - - sealed trait T9K[A, B, C, D, E, F, G, H, I] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) } - type T9List[A, B, C, D, E, F, G, H, I] = AList[T9K[A, B, C, D, E, F, G, H, I]#l] - def tuple9[A, B, C, D, E, F, G, H, I]: T9List[A, B, C, D, E, F, G, H, I] = new T9List[A, B, C, D, E, F, G, H, I] { - type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) - def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) - def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) - def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = - { - val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) - } - } - - sealed trait T10K[A, B, C, D, E, F, G, H, I, J] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) } - type T10List[A, B, C, D, E, F, G, H, I, J] = AList[T10K[A, B, C, D, E, F, G, H, I, J]#l] - def tuple10[A, B, C, D, E, F, G, H, I, J]: T10List[A, B, C, D, E, F, G, H, I, J] = new T10List[A, B, C, D, E, F, G, H, I, J] { - type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) - def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) - def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init)))))))))) - def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = - { - val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) - } - } - - sealed trait T11K[A, B, C, D, E, F, G, H, I, J, K] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } - type T11List[A, B, C, D, E, F, G, H, I, J, K] = AList[T11K[A, B, C, D, E, F, G, H, I, J, K]#l] - def tuple11[A, B, C, D, E, F, G, H, I, J, K]: T11List[A, B, C, D, E, F, G, H, I, J, K] = new T11List[A, B, C, D, E, F, G, H, I, J, K] { - type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) - def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11)) - def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init))))))))))) - def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = - { - val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) - } - } -} diff --git a/util/collection/src/main/scala/sbt/Attributes.scala b/util/collection/src/main/scala/sbt/Attributes.scala deleted file mode 100644 index 64f379012..000000000 --- a/util/collection/src/main/scala/sbt/Attributes.scala +++ /dev/null @@ -1,210 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ -import scala.reflect.Manifest - -// T must be invariant to work properly. -// Because it is sealed and the only instances go through AttributeKey.apply, -// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts - -/** - * A key in an [[AttributeMap]] that constrains its associated value to be of type `T`. - * The key is uniquely defined by its [[label]] and type `T`, represented at runtime by [[manifest]]. - */ -sealed trait AttributeKey[T] { - - /** The runtime evidence for `T` */ - def manifest: Manifest[T] - - @deprecated("Should only be used for compatibility during the transition from hyphenated labels to camelCase labels.", "0.13.0") - def rawLabel: String - - /** The label is the identifier for the key and is camelCase by convention. */ - def label: String - - /** An optional, brief description of the key. */ - def description: Option[String] - - /** - * In environments that support delegation, looking up this key when it has no associated value will delegate to the values associated with these keys. - * The delegation proceeds in order the keys are returned here. - */ - def extend: Seq[AttributeKey[_]] - - /** - * Specifies whether this key is a local, anonymous key (`true`) or not (`false`). - * This is typically only used for programmatic, intermediate keys that should not be referenced outside of a specific scope. - */ - def isLocal: Boolean - - /** Identifies the relative importance of a key among other keys.*/ - def rank: Int -} -private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { - override final def toString = label - override final def hashCode = label.hashCode - override final def equals(o: Any) = (this eq o.asInstanceOf[AnyRef]) || (o match { - case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest - case _ => false - }) - final def isLocal: Boolean = false -} -object AttributeKey { - def apply[T](name: String)(implicit mf: Manifest[T]): AttributeKey[T] = - make(name, None, Nil, Int.MaxValue) - - def apply[T](name: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = - make(name, None, Nil, rank) - - def apply[T](name: String, description: String)(implicit mf: Manifest[T]): AttributeKey[T] = - apply(name, description, Nil) - - def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = - apply(name, description, Nil, rank) - - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T]): AttributeKey[T] = - apply(name, description, extend, Int.MaxValue) - - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = - make(name, Some(description), extend, rank) - - private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T]): AttributeKey[T] = new SharedAttributeKey[T] { - def manifest = mf - def rawLabel = name - val label = Util.hyphenToCamel(name) - def description = description0 - def extend = extend0 - def rank = rank0 - } - private[sbt] def local[T](implicit mf: Manifest[T]): AttributeKey[T] = new AttributeKey[T] { - def manifest = mf - def rawLabel = LocalLabel - def label = LocalLabel - def description = None - def extend = Nil - override def toString = label - def isLocal: Boolean = true - def rank = Int.MaxValue - } - private[sbt] final val LocalLabel = "$local" -} - -/** - * An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be associated with values of type `T`. - * It is therefore possible for this map to contain mappings for keys with the same label but different types. - * Excluding this possibility is the responsibility of the client if desired. - */ -trait AttributeMap { - /** - * Gets the value of type `T` associated with the key `k`. - * If a key with the same label but different type is defined, this method will fail. - */ - def apply[T](k: AttributeKey[T]): T - - /** - * Gets the value of type `T` associated with the key `k` or `None` if no value is associated. - * If a key with the same label but a different type is defined, this method will return `None`. - */ - def get[T](k: AttributeKey[T]): Option[T] - - /** - * Returns this map without the mapping for `k`. - * This method will not remove a mapping for a key with the same label but a different type. - */ - def remove[T](k: AttributeKey[T]): AttributeMap - - /** - * Returns true if this map contains a mapping for `k`. - * If a key with the same label but a different type is defined in this map, this method will return `false`. - */ - def contains[T](k: AttributeKey[T]): Boolean - - /** - * Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`. - * Any mappings for keys with the same label but different types are unaffected. - */ - def put[T](k: AttributeKey[T], value: T): AttributeMap - - /** All keys with defined mappings. There may be multiple keys with the same `label`, but different types. */ - def keys: Iterable[AttributeKey[_]] - - /** Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing mappings.*/ - def ++(o: Iterable[AttributeEntry[_]]): AttributeMap - - /** Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking precedence over existing mappings.*/ - def ++(o: AttributeMap): AttributeMap - - /** All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings, although the specific types are unknown.*/ - def entries: Iterable[AttributeEntry[_]] - - /** `true` if there are no mappings in this map, `false` if there are. */ - def isEmpty: Boolean -} -object AttributeMap { - /** An [[AttributeMap]] without any mappings. */ - val empty: AttributeMap = new BasicAttributeMap(Map.empty) - - /** Constructs an [[AttributeMap]] containing the given `entries`. */ - def apply(entries: Iterable[AttributeEntry[_]]): AttributeMap = empty ++ entries - - /** Constructs an [[AttributeMap]] containing the given `entries`.*/ - def apply(entries: AttributeEntry[_]*): AttributeMap = empty ++ entries - - /** Presents an `AttributeMap` as a natural transformation. */ - implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) { - def apply[T](key: AttributeKey[T]): T = map(key) - } -} -private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap { - def isEmpty: Boolean = backing.isEmpty - def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T] - def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]] - def remove[T](k: AttributeKey[T]): AttributeMap = new BasicAttributeMap(backing - k) - def contains[T](k: AttributeKey[T]) = backing.contains(k) - def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap(backing.updated(k, value)) - def keys: Iterable[AttributeKey[_]] = backing.keys - def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = - { - val newBacking = (backing /: o) { case (b, AttributeEntry(key, value)) => b.updated(key, value) } - new BasicAttributeMap(newBacking) - } - def ++(o: AttributeMap): AttributeMap = - o match { - case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing) - case _ => o ++ this - } - def entries: Iterable[AttributeEntry[_]] = - for ((k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt]) - override def toString = entries.mkString("(", ", ", ")") -} - -// type inference required less generality -/** A map entry where `key` is constrained to only be associated with a fixed value of type `T`. */ -final case class AttributeEntry[T](key: AttributeKey[T], value: T) { - override def toString = key.label + ": " + value -} - -/** Associates a `metadata` map with `data`. */ -final case class Attributed[D](data: D)(val metadata: AttributeMap) { - /** Retrieves the associated value of `key` from the metadata. */ - def get[T](key: AttributeKey[T]): Option[T] = metadata.get(key) - - /** Defines a mapping `key -> value` in the metadata. */ - def put[T](key: AttributeKey[T], value: T): Attributed[D] = Attributed(data)(metadata.put(key, value)) - - /** Transforms the data by applying `f`. */ - def map[T](f: D => T): Attributed[T] = Attributed(f(data))(metadata) -} -object Attributed { - /** Extracts the underlying data from the sequence `in`. */ - def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data) - - /** Associates empty metadata maps with each entry of `in`.*/ - def blankSeq[T](in: Seq[T]): Seq[Attributed[T]] = in map blank - - /** Associates an empty metadata map with `data`. */ - def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty) -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Classes.scala b/util/collection/src/main/scala/sbt/Classes.scala deleted file mode 100644 index 1db644f96..000000000 --- a/util/collection/src/main/scala/sbt/Classes.scala +++ /dev/null @@ -1,24 +0,0 @@ -package sbt - -object Classes { - trait Applicative[M[_]] { - def apply[S, T](f: M[S => T], v: M[S]): M[T] - def pure[S](s: => S): M[S] - def map[S, T](f: S => T, v: M[S]): M[T] - } - trait Monad[M[_]] extends Applicative[M] { - def flatten[T](m: M[M[T]]): M[T] - } - implicit val optionMonad: Monad[Option] = new Monad[Option] { - def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None } - def pure[S](s: => S) = Some(s) - def map[S, T](f: S => T, v: Option[S]) = v map f - def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten - } - implicit val listMonad: Monad[List] = new Monad[List] { - def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv) - def pure[S](s: => S) = s :: Nil - def map[S, T](f: S => T, v: List[S]) = v map f - def flatten[T](m: List[List[T]]): List[T] = m.flatten - } -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Dag.scala b/util/collection/src/main/scala/sbt/Dag.scala deleted file mode 100644 index 118cd0dff..000000000 --- a/util/collection/src/main/scala/sbt/Dag.scala +++ /dev/null @@ -1,128 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 David MacIver, Mark Harrah - */ -package sbt; - -trait Dag[Node <: Dag[Node]] { - self: Node => - - def dependencies: Iterable[Node] - def topologicalSort = Dag.topologicalSort(self)(_.dependencies) -} -object Dag { - import scala.collection.{ mutable, JavaConverters } - import JavaConverters.asScalaSetConverter - - def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = topologicalSort(root :: Nil)(dependencies) - - def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = - { - val discovered = new mutable.HashSet[T] - val finished = (new java.util.LinkedHashSet[T]).asScala - - def visitAll(nodes: Iterable[T]) = nodes foreach visit - def visit(node: T): Unit = { - if (!discovered(node)) { - discovered(node) = true; - try { visitAll(dependencies(node)); } catch { case c: Cyclic => throw node :: c } - finished += node - } else if (!finished(node)) - throw new Cyclic(node) - } - - visitAll(nodes) - - finished.toList - } - // doesn't check for cycles - def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] = topologicalSortUnchecked(node :: Nil)(dependencies) - - def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = - { - val discovered = new mutable.HashSet[T] - var finished: List[T] = Nil - - def visitAll(nodes: Iterable[T]) = nodes foreach visit - def visit(node: T): Unit = { - if (!discovered(node)) { - discovered(node) = true - visitAll(dependencies(node)) - finished ::= node - } - } - - visitAll(nodes); - finished; - } - final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean) - extends Exception("Cyclic reference involving " + - (if (complete) all.mkString("\n ", "\n ", "") else value) - ) { - def this(value: Any) = this(value, value :: Nil, false) - override def toString = getMessage - def ::(a: Any): Cyclic = - if (complete) - this - else if (a == value) - new Cyclic(value, all, true) - else - new Cyclic(value, a :: all, false) - } - - /** A directed graph with edges labeled positive or negative. */ - private[sbt] trait DirectedSignedGraph[Node] { - /** - * Directed edge type that tracks the sign and target (head) vertex. - * The sign can be obtained via [[isNegative]] and the target vertex via [[head]]. - */ - type Arrow - /** List of initial nodes. */ - def nodes: List[Arrow] - /** Outgoing edges for `n`. */ - def dependencies(n: Node): List[Arrow] - /** `true` if the edge `a` is "negative", false if it is "positive". */ - def isNegative(a: Arrow): Boolean - /** The target of the directed edge `a`. */ - def head(a: Arrow): Node - } - - /** - * Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative" edge. - * The directed edges are weighted by the caller as "positive" or "negative". - * If a cycle containing a "negative" edge is detected, its member edges are returned in order. - * Otherwise, the empty list is returned. - */ - private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = - { - import scala.annotation.tailrec - import graph._ - val finished = new mutable.HashSet[Node] - val visited = new mutable.HashSet[Node] - - def visit(edges: List[Arrow], stack: List[Arrow]): List[Arrow] = edges match { - case Nil => Nil - case edge :: tail => - val node = head(edge) - if (!visited(node)) { - visited += node - visit(dependencies(node), edge :: stack) match { - case Nil => - finished += node - visit(tail, stack) - case cycle => cycle - } - } else if (!finished(node)) { - // cycle. If a negative edge is involved, it is an error. - val between = edge :: stack.takeWhile(f => head(f) != node) - if (between exists isNegative) - between - else - visit(tail, stack) - } else - visit(tail, stack) - } - - visit(graph.nodes, Nil) - } - -} diff --git a/util/collection/src/main/scala/sbt/HList.scala b/util/collection/src/main/scala/sbt/HList.scala deleted file mode 100644 index 23f5488c6..000000000 --- a/util/collection/src/main/scala/sbt/HList.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ - -/** - * A minimal heterogeneous list type. For background, see - * http://apocalisp.wordpress.com/2010/07/06/type-level-programming-in-scala-part-6a-heterogeneous-list basics/ - */ -sealed trait HList { - type Wrap[M[_]] <: HList -} -sealed trait HNil extends HList { - type Wrap[M[_]] = HNil - def :+:[G](g: G): G :+: HNil = HCons(g, this) - - override def toString = "HNil" -} -object HNil extends HNil -final case class HCons[H, T <: HList](head: H, tail: T) extends HList { - type Wrap[M[_]] = M[H] :+: T#Wrap[M] - def :+:[G](g: G): G :+: H :+: T = HCons(g, this) - - override def toString = head + " :+: " + tail.toString -} - -object HList { - // contains no type information: not even A - implicit def fromList[A](list: Traversable[A]): HList = ((HNil: HList) /: list)((hl, v) => HCons(v, hl)) -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/IDSet.scala b/util/collection/src/main/scala/sbt/IDSet.scala deleted file mode 100644 index 4f5245a26..000000000 --- a/util/collection/src/main/scala/sbt/IDSet.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -/** A mutable set interface that uses object identity to test for set membership.*/ -trait IDSet[T] { - def apply(t: T): Boolean - def contains(t: T): Boolean - def +=(t: T): Unit - def ++=(t: Iterable[T]): Unit - def -=(t: T): Boolean - def all: collection.Iterable[T] - def toList: List[T] - def isEmpty: Boolean - def foreach(f: T => Unit): Unit - def process[S](t: T)(ifSeen: S)(ifNew: => S): S -} - -object IDSet { - implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all - def apply[T](values: T*): IDSet[T] = apply(values) - def apply[T](values: Iterable[T]): IDSet[T] = - { - val s = create[T] - s ++= values - s - } - def create[T]: IDSet[T] = new IDSet[T] { - private[this] val backing = new java.util.IdentityHashMap[T, AnyRef] - private[this] val Dummy: AnyRef = "" - - def apply(t: T) = contains(t) - def contains(t: T) = backing.containsKey(t) - def foreach(f: T => Unit) = all foreach f - def +=(t: T) = backing.put(t, Dummy) - def ++=(t: Iterable[T]) = t foreach += - def -=(t: T) = if (backing.remove(t) eq null) false else true - def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet) - def toList = all.toList - def isEmpty = backing.isEmpty - def process[S](t: T)(ifSeen: S)(ifNew: => S) = if (contains(t)) ifSeen else { this += t; ifNew } - override def toString = backing.toString - } -} diff --git a/util/collection/src/main/scala/sbt/INode.scala b/util/collection/src/main/scala/sbt/INode.scala deleted file mode 100644 index ce39fadad..000000000 --- a/util/collection/src/main/scala/sbt/INode.scala +++ /dev/null @@ -1,177 +0,0 @@ -package sbt - -import java.lang.Runnable -import java.util.concurrent.{ atomic, Executor, LinkedBlockingQueue } -import atomic.{ AtomicBoolean, AtomicInteger } -import Types.{ :+:, ConstK, Id } - -object EvaluationState extends Enumeration { - val New, Blocked, Ready, Calling, Evaluated = Value -} - -abstract class EvaluateSettings[Scope] { - protected val init: Init[Scope] - import init._ - protected def executor: Executor - protected def compiledSettings: Seq[Compiled[_]] - - import EvaluationState.{ Value => EvaluationState, _ } - - private[this] val complete = new LinkedBlockingQueue[Option[Throwable]] - private[this] val static = PMap.empty[ScopedKey, INode] - private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet - private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key) - - private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) { - def apply[T](i: Initialize[T]): INode[T] = i match { - case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform) - case a: Apply[k, T] @unchecked => new MixedNode[k, T](a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist) - case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x))) - case init.StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] - case v: Value[T] @unchecked => constant(v.value) - case v: ValidationCapture[T] @unchecked => strictConstant(v.key) - case t: TransformCapture => strictConstant(t.f) - case o: Optional[s, T] @unchecked => o.a match { - case None => constant(() => o.f(None)) - case Some(i) => single[s, T](transform(i), x => o.f(Some(x))) - } - } - } - private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs => - (cs.settings map { s => - val t = transform(s.init) - static(s.key) = t - t - }): Seq[INode[_]] - } - private[this] var running = new AtomicInteger - private[this] var cancel = new AtomicBoolean(false) - - def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = - { - assert(running.get() == 0, "Already running") - startWork() - roots.foreach(_.registerIfNew()) - workComplete() - complete.take() foreach { ex => - cancel.set(true) - throw ex - } - getResults(delegates) - } - private[this] def getResults(implicit delegates: Scope => Seq[Scope]) = - (empty /: static.toTypedSeq) { - case (ss, static.TPair(key, node)) => - if (key.key.isLocal) ss else ss.set(key.scope, key.key, node.get) - } - private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get } - - private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate()) - private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = submit(node.callComplete(value)) - private[this] def submit(work: => Unit): Unit = - { - startWork() - executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) }) - } - private[this] def run0(work: => Unit): Unit = - { - try { work } catch { case e: Throwable => complete.put(Some(e)) } - workComplete() - } - - private[this] def startWork(): Unit = running.incrementAndGet() - private[this] def workComplete(): Unit = - if (running.decrementAndGet() == 0) - complete.put(None) - - private[this] sealed abstract class INode[T] { - private[this] var state: EvaluationState = New - private[this] var value: T = _ - private[this] val blocking = new collection.mutable.ListBuffer[INode[_]] - private[this] var blockedOn: Int = 0 - private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]] - - override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " + - keyString - - private[this] def keyString = - (static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static" - - final def get: T = synchronized { - assert(value != null, toString + " not evaluated") - value - } - final def doneOrBlock(from: INode[_]): Boolean = synchronized { - val ready = state == Evaluated - if (!ready) blocking += from - registerIfNew() - ready - } - final def isDone: Boolean = synchronized { state == Evaluated } - final def isNew: Boolean = synchronized { state == New } - final def isCalling: Boolean = synchronized { state == Calling } - final def registerIfNew(): Unit = synchronized { if (state == New) register() } - private[this] def register(): Unit = { - assert(state == New, "Already registered and: " + toString) - val deps = dependsOn - blockedOn = deps.size - deps.count(_.doneOrBlock(this)) - if (blockedOn == 0) - schedule() - else - state = Blocked - } - - final def schedule(): Unit = synchronized { - assert(state == New || state == Blocked, "Invalid state for schedule() call: " + toString) - state = Ready - submitEvaluate(this) - } - final def unblocked(): Unit = synchronized { - assert(state == Blocked, "Invalid state for unblocked() call: " + toString) - blockedOn -= 1 - assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString) - if (blockedOn == 0) schedule() - } - final def evaluate(): Unit = synchronized { evaluate0() } - protected final def makeCall(source: BindNode[_, T], target: INode[T]): Unit = { - assert(state == Ready, "Invalid state for call to makeCall: " + toString) - state = Calling - target.call(source) - } - protected final def setValue(v: T): Unit = { - assert(state != Evaluated, "Already evaluated (trying to set value to " + v + "): " + toString) - if (v == null) sys.error("Setting value cannot be null: " + keyString) - value = v - state = Evaluated - blocking foreach { _.unblocked() } - blocking.clear() - calledBy foreach { node => submitCallComplete(node, value) } - calledBy.clear() - } - final def call(by: BindNode[_, T]): Unit = synchronized { - registerIfNew() - state match { - case Evaluated => submitCallComplete(by, value) - case _ => calledBy += by - } - } - protected def dependsOn: Seq[INode[_]] - protected def evaluate0(): Unit - } - - private[this] def strictConstant[T](v: T): INode[T] = constant(() => v) - private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) - private[this] def single[S, T](in: INode[S], f: S => T): INode[T] = new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) - private[this] final class BindNode[S, T](in: INode[S], f: S => INode[T]) extends INode[T] { - protected def dependsOn = in :: Nil - protected def evaluate0(): Unit = makeCall(this, f(in.get)) - def callComplete(value: T): Unit = synchronized { - assert(isCalling, "Invalid state for callComplete(" + value + "): " + toString) - setValue(value) - } - } - private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] { - protected def dependsOn = alist.toList(in) - protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue))) - } -} diff --git a/util/collection/src/main/scala/sbt/KList.scala b/util/collection/src/main/scala/sbt/KList.scala deleted file mode 100644 index 0b09ac9b1..000000000 --- a/util/collection/src/main/scala/sbt/KList.scala +++ /dev/null @@ -1,53 +0,0 @@ -package sbt - -import Types._ -import Classes.Applicative - -/** Heterogeneous list with each element having type M[T] for some type T.*/ -sealed trait KList[+M[_]] { - type Transform[N[_]] <: KList[N] - - /** Apply the natural transformation `f` to each element. */ - def transform[N[_]](f: M ~> N): Transform[N] - - /** Folds this list using a function that operates on the homogeneous type of the elements of this list. */ - def foldr[T](f: (M[_], T) => T, init: T): T = init // had trouble defining it in KNil - - /** Applies `f` to the elements of this list in the applicative functor defined by `ap`. */ - def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] - - /** Equivalent to `transform(f) . apply(x => x)`, this is the essence of the iterator at the level of natural transformations.*/ - def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] - - /** Discards the heterogeneous type information and constructs a plain List from this KList's elements. */ - def toList: List[M[_]] -} -final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] { - final type Transform[N[_]] = KCons[H, tail.Transform[N], N] - - def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f)) - def toList: List[M[_]] = head :: tail.toList - def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = - { - val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t)) - ap.apply(tail.apply[N, H => Z](g), head) - } - def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = - { - val tt: N[tail.Transform[P]] = tail.traverse[N, P](f) - val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t) - np.apply(np.map(g, tt), f(head)) - } - def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this) - override def foldr[T](f: (M[_], T) => T, init: T): T = f(head, tail.foldr(f, init)) -} -sealed abstract class KNil extends KList[Nothing] { - final type Transform[N[_]] = KNil - final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil - final def toList = Nil - final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil)) - final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil) -} -case object KNil extends KNil { - def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this) -} diff --git a/util/collection/src/main/scala/sbt/PMap.scala b/util/collection/src/main/scala/sbt/PMap.scala deleted file mode 100644 index cf0454fd9..000000000 --- a/util/collection/src/main/scala/sbt/PMap.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import collection.mutable - -trait RMap[K[_], V[_]] { - def apply[T](k: K[T]): V[T] - def get[T](k: K[T]): Option[V[T]] - def contains[T](k: K[T]): Boolean - def toSeq: Seq[(K[_], V[_])] - def toTypedSeq: Seq[TPair[_]] = toSeq.map { case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) } - def keys: Iterable[K[_]] - def values: Iterable[V[_]] - def isEmpty: Boolean - - final case class TPair[T](key: K[T], value: V[T]) -} - -trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { - def put[T](k: K[T], v: V[T]): IMap[K, V] - def remove[T](k: K[T]): IMap[K, V] - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V] - def mapValues[V2[_]](f: V ~> V2): IMap[K, V2] - def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l): (IMap[K, VL], IMap[K, VR]) -} -trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { - def update[T](k: K[T], v: V[T]): Unit - def remove[T](k: K[T]): Option[V[T]] - def getOrUpdate[T](k: K[T], make: => V[T]): V[T] - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] -} -object PMap { - implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k) - def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap) -} -object IMap { - /** - * Only suitable for K that is invariant in its type parameter. - * Option and List keys are not suitable, for example, - * because None <:< Option[String] and None <: Option[Int]. - */ - def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty) - - private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]]) extends AbstractRMap[K, V] with IMap[K, V] { - def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]] - def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v)) - def remove[T](k: K[T]) = new IMap0[K, V](backing - k) - - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]) = - put(k, f(this get k getOrElse init)) - - def mapValues[V2[_]](f: V ~> V2) = - new IMap0[K, V2](backing.mapValues(x => f(x))) - - def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) = - { - val mapped = backing.iterator.map { - case (k, v) => f(v) match { - case Left(l) => Left((k, l)) - case Right(r) => Right((k, r)) - } - } - val (l, r) = Util.separateE[(K[_], VL[_]), (K[_], VR[_])](mapped.toList) - (new IMap0[K, VL](l.toMap), new IMap0[K, VR](r.toMap)) - } - - def toSeq = backing.toSeq - def keys = backing.keys - def values = backing.values - def isEmpty = backing.isEmpty - - override def toString = backing.toString - } -} - -abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { - def apply[T](k: K[T]): V[T] = get(k).get - def contains[T](k: K[T]): Boolean = get(k).isDefined -} - -/** - * Only suitable for K that is invariant in its type parameter. - * Option and List keys are not suitable, for example, - * because None <:< Option[String] and None <: Option[Int]. - */ -class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends AbstractRMap[K, V] with PMap[K, V] { - def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k)) - def update[T](k: K[T], v: V[T]) { backing(k) = v } - def remove[T](k: K[T]) = cast(backing.remove(k)) - def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make)) - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = - { - val v = f(this get k getOrElse init) - update(k, v) - v - } - def toSeq = backing.toSeq - def keys = backing.keys - def values = backing.values - def isEmpty = backing.isEmpty - - private[this] def cast[T](v: V[_]): V[T] = v.asInstanceOf[V[T]] - private[this] def cast[T](o: Option[V[_]]): Option[V[T]] = o map cast[T] - - override def toString = backing.toString -} diff --git a/util/collection/src/main/scala/sbt/Param.scala b/util/collection/src/main/scala/sbt/Param.scala deleted file mode 100644 index 19d12798a..000000000 --- a/util/collection/src/main/scala/sbt/Param.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ - -// Used to emulate ~> literals -trait Param[A[_], B[_]] { - type T - def in: A[T] - def ret(out: B[T]) - def ret: B[T] -} - -object Param { - implicit def pToT[A[_], B[_]](p: Param[A, B] => Unit): A ~> B = new (A ~> B) { - def apply[s](a: A[s]): B[s] = { - val v: Param[A, B] { type T = s } = new Param[A, B] { - type T = s - def in = a - private var r: B[T] = _ - def ret(b: B[T]): Unit = { r = b } - def ret: B[T] = r - } - p(v) - v.ret - } - } -} diff --git a/util/collection/src/main/scala/sbt/Positions.scala b/util/collection/src/main/scala/sbt/Positions.scala deleted file mode 100755 index 5d7e1915d..000000000 --- a/util/collection/src/main/scala/sbt/Positions.scala +++ /dev/null @@ -1,20 +0,0 @@ -package sbt - -sealed trait SourcePosition - -sealed trait FilePosition extends SourcePosition { - def path: String - def startLine: Int -} - -case object NoPosition extends SourcePosition - -final case class LinePosition(path: String, startLine: Int) extends FilePosition - -final case class LineRange(start: Int, end: Int) { - def shift(n: Int) = new LineRange(start + n, end + n) -} - -final case class RangePosition(path: String, range: LineRange) extends FilePosition { - def startLine = range.start -} diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/util/collection/src/main/scala/sbt/Settings.scala deleted file mode 100644 index eb4227d09..000000000 --- a/util/collection/src/main/scala/sbt/Settings.scala +++ /dev/null @@ -1,642 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -import Types._ - -sealed trait Settings[Scope] { - def data: Map[Scope, AttributeMap] - def keys(scope: Scope): Set[AttributeKey[_]] - def scopes: Set[Scope] - def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] - def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] - def get[T](scope: Scope, key: AttributeKey[T]): Option[T] - def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] - def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] -} - -private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] { - def scopes: Set[Scope] = data.keySet - def keys(scope: Scope) = data(scope).keys.toSet - def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) } toSeq - - def get[T](scope: Scope, key: AttributeKey[T]): Option[T] = - delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption - def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] = - delegates(scope).toStream.find(sc => getDirect(sc, key).isDefined) - - def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] = - (data get scope).flatMap(_ get key) - - def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] = - { - val map = data getOrElse (scope, AttributeMap.empty) - val newData = data.updated(scope, map.put(key, value)) - new Settings0(newData, delegates) - } -} -// delegates should contain the input Scope as the first entry -// this trait is intended to be mixed into an object -trait Init[Scope] { - /** The Show instance used when a detailed String needs to be generated. It is typically used when no context is available.*/ - def showFullKey: Show[ScopedKey[_]] - - final case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] { - def scopedKey = this - } - - type SettingSeq[T] = Seq[Setting[T]] - type ScopedMap = IMap[ScopedKey, SettingSeq] - type CompiledMap = Map[ScopedKey[_], Compiled[_]] - type MapScoped = ScopedKey ~> ScopedKey - type ValidatedRef[T] = Either[Undefined, ScopedKey[T]] - type ValidatedInit[T] = Either[Seq[Undefined], Initialize[T]] - type ValidateRef = ScopedKey ~> ValidatedRef - type ScopeLocal = ScopedKey[_] => Seq[Setting[_]] - type MapConstant = ScopedKey ~> Option - - private[sbt] abstract class ValidateKeyRef { - def apply[T](key: ScopedKey[T], selfRefOk: Boolean): ValidatedRef[T] - } - - /** - * The result of this initialization is the composition of applied transformations. - * This can be useful when dealing with dynamic Initialize values. - */ - lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = new TransformCapture(idK[Initialize]) - - def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos) - def valueStrict[T](value: T): Initialize[T] = pure(() => value) - def value[T](value: => T): Initialize[T] = pure(value _) - def pure[T](value: () => T): Initialize[T] = new Value(value) - def optional[T, U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f) - def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = setting[T](key, map(key)(f), NoPosition) - def bind[S, T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in) - def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] = new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S]) - def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(implicit alist: AList[K]): Initialize[T] = new Apply[K, T](f, inputs, alist) - def uniform[S, T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] = - new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S]) - - /** - * The result of this initialization is the validated `key`. - * No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`. - * That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`. - */ - private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) - - @deprecated("Use the version with default arguments and default parameter.", "0.13.7") - final def derive[T](s: Setting[T], allowDynamic: Boolean, filter: Scope => Boolean, trigger: AttributeKey[_] => Boolean): Setting[T] = - derive(s, allowDynamic, filter, trigger, false) - /** - * Constructs a derived setting that will be automatically defined in every scope where one of its dependencies - * is explicitly defined and the where the scope matches `filter`. - * A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true. - * Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either. - */ - final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true), trigger: AttributeKey[_] => Boolean = const(true), default: Boolean = false): Setting[T] = { - deriveAllowed(s, allowDynamic) foreach sys.error - val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger) - if (default) d.default() else d - } - def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = s.init match { - case _: Bind[_, _] if !allowDynamic => Some("Cannot derive from dynamic dependencies.") - case _ => None - } - // id is used for equality - private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default() - private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = ss.map(s => defaultSetting(s)) - private[this] final val nextID = new java.util.concurrent.atomic.AtomicLong - private[this] final def nextDefaultID(): Long = nextID.incrementAndGet() - - def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates) - def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) { - def apply[T](k: ScopedKey[T]): T = getValue(s, k) - } - def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k)) - def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k) - def mapScope(f: Scope => Scope): MapScoped = new MapScoped { - def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope)) - } - private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey(key)) - - private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = - { - val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { case u: DefaultSetting[_] => Left(u); case s => Right(s) } - defaults.distinct ++ others - } - - def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): CompiledMap = - { - val initDefaults = applyDefaults(init) - // inject derived settings into scopes where their dependencies are directly defined - // and prepend per-scope settings - val derived = deriveAndLocal(initDefaults) - // group by Scope/Key, dropping dead initializations - val sMap: ScopedMap = grouped(derived) - // delegate references to undefined values according to 'delegates' - val dMap: ScopedMap = if (actual) delegate(sMap)(delegates, display) else sMap - // merge Seq[Setting[_]] into Compiled - compile(dMap) - } - def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): Settings[Scope] = - { - val cMap = compiled(init)(delegates, scopeLocal, display) - // order the initializations. cyclic references are detected here. - val ordered: Seq[Compiled[_]] = sort(cMap) - // evaluation: apply the initializations. - try { applyInits(ordered) } - catch { case rru: RuntimeUndefined => throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true) } - } - def sort(cMap: CompiledMap): Seq[Compiled[_]] = - Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap)) - - def compile(sMap: ScopedMap): CompiledMap = - sMap.toTypedSeq.map { - case sMap.TPair(k, ss) => - val deps = ss flatMap { _.dependencies } toSet; - (k, new Compiled(k, deps, ss)) - } toMap; - - def grouped(init: Seq[Setting[_]]): ScopedMap = - ((IMap.empty: ScopedMap) /: init)((m, s) => add(m, s)) - - def add[T](m: ScopedMap, s: Setting[T]): ScopedMap = - m.mapValue[T](s.key, Nil, ss => append(ss, s)) - - def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] = - if (s.definitive) s :: Nil else ss :+ s - - def addLocal(init: Seq[Setting[_]])(implicit scopeLocal: ScopeLocal): Seq[Setting[_]] = - init.flatMap(_.dependencies flatMap scopeLocal) ++ init - - def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope], display: Show[ScopedKey[_]]): ScopedMap = - { - def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef { - def apply[T](k: ScopedKey[T], selfRefOk: Boolean) = - delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst) - } - type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]] - val f = new (SettingSeq ~> ValidatedSettings) { - def apply[T](ks: Seq[Setting[T]]) = { - val (undefs, valid) = Util.separate(ks.zipWithIndex) { case (s, i) => s validateKeyReferenced refMap(s, i == 0) } - if (undefs.isEmpty) Right(valid) else Left(undefs.flatten) - } - } - type Undefs[_] = Seq[Undefined] - val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f) - if (undefineds.isEmpty) - result - else - throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false) - } - private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], selfRefOk: Boolean): Either[Undefined, ScopedKey[T]] = - { - val skeys = scopes.iterator.map(x => ScopedKey(x, k.key)) - val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk)) - definedAt.toRight(Undefined(ref, k)) - } - - private[this] def applyInits(ordered: Seq[Compiled[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = - { - val x = java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors) - try { - val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { - override val init: Init.this.type = Init.this - def compiledSettings = ordered - def executor = x - } - eval.run - } finally { x.shutdown() } - } - - def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(implicit display: Show[ScopedKey[_]]): String = - { - val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey) - val derived = u.defining.isDerived - val refString = display(u.defining.key) - val sourceString = if (derived) "" else parenPosString(u.defining) - val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display(g) + " ?").toList.mkString - val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else "" - display(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString - } - private[this] def parenPosString(s: Setting[_]): String = - s.positionString match { case None => ""; case Some(s) => " (" + s + ")" } - - def guessIntendedScope(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], key: ScopedKey[_]): Option[ScopedKey[_]] = - { - val distances = validKeys.flatMap { validKey => refinedDistance(delegates, validKey, key).map(dist => (dist, validKey)) } - distances.sortBy(_._1).map(_._2).headOption - } - def refinedDistance(delegates: Scope => Seq[Scope], a: ScopedKey[_], b: ScopedKey[_]): Option[Int] = - if (a.key != b.key || a == b) None - else { - val dist = delegates(a.scope).indexOf(b.scope) - if (dist < 0) None else Some(dist) - } - - final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString) - final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_]) { - @deprecated("For compatibility only, use `defining` directly.", "0.13.1") - val definingKey = defining.key - @deprecated("For compatibility only, use `defining` directly.", "0.13.1") - val derived: Boolean = defining.isDerived - @deprecated("Use the non-deprecated Undefined factory method.", "0.13.1") - def this(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean) = this(fakeUndefinedSetting(definingKey, derived), referencedKey) - } - final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.") { - override def getMessage = - super.getMessage + undefined.map { u => - "\n" + u.defining + " referenced from " + u.referencedKey - }.mkString - } - - @deprecated("Use the other overload.", "0.13.1") - def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean): Undefined = - new Undefined(fakeUndefinedSetting(definingKey, derived), referencedKey) - private[this] def fakeUndefinedSetting[T](definingKey: ScopedKey[T], d: Boolean): Setting[T] = - { - val init: Initialize[T] = pure(() => sys.error("Dummy setting for compatibility only.")) - new Setting(definingKey, init, NoPosition) { override def isDerived = d } - } - - def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(defining, referencedKey) - def Uninitialized(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], keys: Seq[Undefined], runtime: Boolean)(implicit display: Show[ScopedKey[_]]): Uninitialized = - { - assert(keys.nonEmpty) - val suffix = if (keys.length > 1) "s" else "" - val prefix = if (runtime) "Runtime reference" else "Reference" - val keysString = keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "") - new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") - } - final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) { - override def toString = showFullKey(key) - } - final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]]) - - def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = - { - import collection.breakOut - val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil } - val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) - def flatten(cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], deps: Iterable[ScopedKey[_]]): Flattened = - new Flattened(key, deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)) - - val empty = Map.empty[ScopedKey[_], Flattened] - val flattenedLocals = (empty /: ordered) { (cmap, c) => cmap.updated(c.key, flatten(cmap, c.key, c.dependencies)) } - compiled flatMap { - case (key, comp) => - if (key.key.isLocal) - Nil - else - Seq[(ScopedKey[_], Flattened)]((key, flatten(flattenedLocals, key, comp.dependencies))) - } - } - - def definedAtString(settings: Seq[Setting[_]]): String = - { - val posDefined = settings.flatMap(_.positionString.toList) - if (posDefined.nonEmpty) { - val header = if (posDefined.size == settings.size) "defined at:" else - "some of the defining occurrences:" - header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n")) - } else "" - } - - /** - * Intersects two scopes, returning the more specific one if they intersect, or None otherwise. - */ - private[sbt] def intersect(s1: Scope, s2: Scope)(implicit delegates: Scope => Seq[Scope]): Option[Scope] = - if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific - else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific - else None - - private[this] def deriveAndLocal(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): Seq[Setting[_]] = - { - import collection.mutable - - final class Derived(val setting: DerivedSetting[_]) { - val dependencies = setting.dependencies.map(_.key) - def triggeredBy = dependencies.filter(setting.trigger) - val inScopes = new mutable.HashSet[Scope] - val outputs = new mutable.ListBuffer[Setting[_]] - } - final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) { - def dependencies = settings.flatMap(_.dependencies) - // This is mainly for use in the cyclic reference error message - override def toString = s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}" - } - - // separate `derived` settings from normal settings (`defs`) - val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) } - val defs = addLocal(rawDefs)(scopeLocal) - - // group derived settings by the key they define - val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds] - for (s <- derived) { - val key = s.setting.key.key - derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s - } - - // sort derived settings so that dependencies come first - // this is necessary when verifying that a derived setting's dependencies exist - val ddeps = (d: Deriveds) => d.dependencies.flatMap(derivsByDef.get) - val sortedDerivs = Dag.topologicalSort(derivsByDef.values)(ddeps) - - // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. - val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] - for (s <- derived; d <- s.triggeredBy) - derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s - - // Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with - // the `Setting`s that were actually derived from it: `Derived.outputs` - val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s => s.setting -> s }).toMap - - // set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present - val defined = new mutable.HashSet[ScopedKey[_]] - def addDefs(ss: Seq[Setting[_]]): Unit = { for (s <- ss) defined += s.key } - addDefs(defs) - - // true iff the scoped key is in `defined`, taking delegation into account - def isDefined(key: AttributeKey[_], scope: Scope) = - delegates(scope).exists(s => defined.contains(ScopedKey(s, key))) - - // true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope` - def allDepsDefined(d: Derived, scope: Scope, local: Set[AttributeKey[_]]): Boolean = - d.dependencies.forall(dep => local(dep) || isDefined(dep, scope)) - - // Returns the list of injectable derived settings and their local settings for `sk`. - // The settings are to be injected under `outputScope` = whichever scope is more specific of: - // * the dependency's (`sk`) scope - // * the DerivedSetting's scope in which it has been declared, `definingScope` - // provided that these two scopes intersect. - // A derived setting is injectable if: - // 1. it has not been previously injected into outputScope - // 2. it applies to outputScope (as determined by its `filter`) - // 3. all of its dependencies are defined for outputScope (allowing for delegation) - // This needs to handle local settings because a derived setting wouldn't be injected if it's local setting didn't exist yet. - val deriveFor = (sk: ScopedKey[_]) => { - val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten - val scope = sk.scope - def localAndDerived(d: Derived): Seq[Setting[_]] = { - def definingScope = d.setting.key.scope - val outputScope = intersect(scope, definingScope) - outputScope collect { - case s if !d.inScopes.contains(s) && d.setting.filter(s) => - val local = d.dependencies.flatMap(dep => scopeLocal(ScopedKey(s, dep))) - if (allDepsDefined(d, s, local.map(_.key.key).toSet)) { - d.inScopes.add(s) - val out = local :+ d.setting.setScope(s) - d.outputs ++= out - out - } else - Nil - } getOrElse Nil - } - derivedForKey.flatMap(localAndDerived) - } - - val processed = new mutable.HashSet[ScopedKey[_]] - - // derives settings, transitively so that a derived setting can trigger another - def process(rem: List[Setting[_]]): Unit = rem match { - case s :: ss => - val sk = s.key - val ds = if (processed.add(sk)) deriveFor(sk) else Nil - addDefs(ds) - process(ds ::: ss) - case Nil => - } - process(defs.toList) - - // Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual - // settings that were derived. - val allDefs = addLocal(init)(scopeLocal) - allDefs flatMap { case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten; case s => Stream(s) } - } - - sealed trait Initialize[T] { - def dependencies: Seq[ScopedKey[_]] - def apply[S](g: T => S): Initialize[S] - - @deprecated("Will be made private.", "0.13.2") - def mapReferenced(g: MapScoped): Initialize[T] - @deprecated("Will be made private.", "0.13.2") - def mapConstant(g: MapConstant): Initialize[T] - - @deprecated("Will be made private.", "0.13.2") - def validateReferenced(g: ValidateRef): ValidatedInit[T] = - validateKeyReferenced(new ValidateKeyRef { def apply[T](key: ScopedKey[T], selfRefOk: Boolean) = g(key) }) - - private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] - - def evaluate(map: Settings[Scope]): T - def zip[S](o: Initialize[S]): Initialize[(T, S)] = zipTupled(o)(idFun) - def zipWith[S, U](o: Initialize[S])(f: (T, S) => U): Initialize[U] = zipTupled(o)(f.tupled) - private[this] def zipTupled[S, U](o: Initialize[S])(f: ((T, S)) => U): Initialize[U] = - new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T, S]) - /** A fold on the static attributes of this and nested Initializes. */ - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S - } - object Initialize { - implicit def joinInitialize[T](s: Seq[Initialize[T]]): JoinInitSeq[T] = new JoinInitSeq(s) - final class JoinInitSeq[T](s: Seq[Initialize[T]]) { - def joinWith[S](f: Seq[T] => S): Initialize[S] = uniform(s)(f) - def join: Initialize[Seq[T]] = uniform(s)(idFun) - } - def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun) - def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] = - join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] - } - object SettingsDefinition { - implicit def unwrapSettingsDefinition(d: SettingsDefinition): Seq[Setting[_]] = d.settings - implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition = new SettingList(ss) - } - sealed trait SettingsDefinition { - def settings: Seq[Setting[_]] - } - final class SettingList(val settings: Seq[Setting[_]]) extends SettingsDefinition - sealed class Setting[T] private[Init] (val key: ScopedKey[T], val init: Initialize[T], val pos: SourcePosition) extends SettingsDefinition { - def settings = this :: Nil - def definitive: Boolean = !init.dependencies.contains(key) - def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key) - @deprecated("Will be made private.", "0.13.2") - def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos) - @deprecated("Will be made private.", "0.13.2") - def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos)) - - private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] = - (init validateKeyReferenced g).right.map(newI => make(key, newI, pos)) - - def mapKey(g: MapScoped): Setting[T] = make(g(key), init, pos) - def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos) - @deprecated("Will be made private.", "0.13.2") - def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos) - def withPos(pos: SourcePosition) = make(key, init, pos) - def positionString: Option[String] = pos match { - case pos: FilePosition => Some(pos.path + ":" + pos.startLine) - case NoPosition => None - } - private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = make(key, f(init), pos) - override def toString = "setting(" + key + ") at " + pos - - protected[this] def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new Setting[T](key, init, pos) - protected[sbt] def isDerived: Boolean = false - private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) - /** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */ - private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = DefaultSetting(key, init, pos, id) - } - private[Init] sealed class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean) extends Setting[T](sk, i, p) { - override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new DerivedSetting[T](key, init, pos, filter, trigger) - protected[sbt] override def isDerived: Boolean = true - override def default(_id: => Long): DefaultSetting[T] = new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id } - override def toString = "derived " + super.toString - } - // Only keep the first occurrence of this setting and move it to the front so that it has lower precedence than non-defaults. - // This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available. - private[Init] sealed trait DefaultSetting[T] extends Setting[T] { - val id: Long - override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = super.make(key, init, pos) default id - override final def hashCode = id.hashCode - override final def equals(o: Any): Boolean = o match { case d: DefaultSetting[_] => d.id == id; case _ => false } - override def toString = s"default($id) " + super.toString - override def default(id: => Long) = this - } - - object DefaultSetting { - def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) = new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id } - } - - private[this] def handleUndefined[T](vr: ValidatedInit[T]): Initialize[T] = vr match { - case Left(undefs) => throw new RuntimeUndefined(undefs) - case Right(x) => x - } - - private[this] lazy val getValidated = - new (ValidatedInit ~> Initialize) { def apply[T](v: ValidatedInit[T]) = handleUndefined[T](v) } - - // mainly for reducing generated class count - private[this] def validateKeyReferencedT(g: ValidateKeyRef) = - new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateKeyReferenced g } - - private[this] def mapReferencedT(g: MapScoped) = - new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g } - - private[this] def mapConstantT(g: MapConstant) = - new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapConstant g } - - private[this] def evaluateT(g: Settings[Scope]) = - new (Initialize ~> Id) { def apply[T](i: Initialize[T]) = i evaluate g } - - private[this] def deps(ls: Seq[Initialize[_]]): Seq[ScopedKey[_]] = ls.flatMap(_.dependencies) - - sealed trait Keyed[S, T] extends Initialize[T] { - def scopedKey: ScopedKey[S] - def transform: S => T - final def dependencies = scopedKey :: Nil - final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform) - final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey)) - final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue(g(scopedKey), transform) - private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = g(scopedKey, false) match { - case Left(un) => Left(un :: Nil) - case Right(nk) => Right(new GetValue(nk, transform)) - } - final def mapConstant(g: MapConstant): Initialize[T] = g(scopedKey) match { - case None => this - case Some(const) => new Value(() => transform(const)) - } - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init - } - private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T] - trait KeyedInitialize[T] extends Keyed[T, T] { - final val transform = idFun[T] - } - - private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) extends Initialize[Initialize ~> Initialize] { - def dependencies = Nil - def apply[Z](g2: (Initialize ~> Initialize) => Z): Initialize[Z] = map(this)(g2) - def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f - def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f) - def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f) - def validateKeyReferenced(g: ValidateKeyRef) = Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f)) - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init - } - private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) extends Initialize[ScopedKey[T]] { - def dependencies = Nil - def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2) - def evaluate(ss: Settings[Scope]) = key - def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk) - def mapConstant(g: MapConstant) = this - def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match { - case Left(un) => Left(un :: Nil) - case Right(k) => Right(new ValidationCapture(k, selfRefOk)) - } - - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init - } - private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S]) extends Initialize[T] { - def dependencies = in.dependencies - def apply[Z](g: T => Z): Initialize[Z] = new Bind[S, Z](s => f(s)(g), in) - def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss - def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g) - def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { validIn => - new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) - } - def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g) - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = in.processAttributes(init)(f) - } - private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T] { - def dependencies = deps(a.toList) - def apply[Z](g: T => Z): Initialize[Z] = new Optional[S, Z](a, g compose f) - def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f) - def validateKeyReferenced(g: ValidateKeyRef) = a match { - case None => Right(this) - case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f)) - } - def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f) - def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i)))) - // proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used - private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None } - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = a match { - case None => init - case Some(i) => i.processAttributes(init)(f) - } - } - private[sbt] final class Value[T](val value: () => T) extends Initialize[T] { - def dependencies = Nil - def mapReferenced(g: MapScoped) = this - def validateKeyReferenced(g: ValidateKeyRef) = Right(this) - def apply[S](g: T => S) = new Value[S](() => g(value())) - def mapConstant(g: MapConstant) = this - def evaluate(map: Settings[Scope]): T = value() - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init - } - private[sbt] final object StaticScopes extends Initialize[Set[Scope]] { - def dependencies = Nil - def mapReferenced(g: MapScoped) = this - def validateKeyReferenced(g: ValidateKeyRef) = Right(this) - def apply[S](g: Set[Scope] => S) = map(this)(g) - def mapConstant(g: MapConstant) = this - def evaluate(map: Settings[Scope]) = map.scopes - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init - } - private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T] { - def dependencies = deps(alist.toList(inputs)) - def mapReferenced(g: MapScoped) = mapInputs(mapReferencedT(g)) - def apply[S](g: T => S) = new Apply(g compose f, inputs, alist) - def mapConstant(g: MapConstant) = mapInputs(mapConstantT(g)) - def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist) - def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss))) - def validateKeyReferenced(g: ValidateKeyRef) = - { - val tx = alist.transform(inputs, validateKeyReferencedT(g)) - val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) - val get = new (ValidatedInit ~> Initialize) { def apply[T](vr: ValidatedInit[T]) = vr.right.get } - if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) - } - - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = - (init /: alist.toList(inputs)) { (v, i) => i.processAttributes(v)(f) } - } - private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v) -} diff --git a/util/collection/src/main/scala/sbt/Show.scala b/util/collection/src/main/scala/sbt/Show.scala deleted file mode 100644 index 1f8e9703b..000000000 --- a/util/collection/src/main/scala/sbt/Show.scala +++ /dev/null @@ -1,8 +0,0 @@ -package sbt - -trait Show[T] { - def apply(t: T): String -} -object Show { - def apply[T](f: T => String): Show[T] = new Show[T] { def apply(t: T): String = f(t) } -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/ShowLines.scala b/util/collection/src/main/scala/sbt/ShowLines.scala deleted file mode 100644 index 126b6360e..000000000 --- a/util/collection/src/main/scala/sbt/ShowLines.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sbt - -trait ShowLines[A] { - def showLines(a: A): Seq[String] -} -object ShowLines { - def apply[A](f: A => Seq[String]): ShowLines[A] = - new ShowLines[A] { - def showLines(a: A): Seq[String] = f(a) - } - - implicit class ShowLinesOp[A: ShowLines](a: A) { - def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a) - } -} diff --git a/util/collection/src/main/scala/sbt/Signal.scala b/util/collection/src/main/scala/sbt/Signal.scala deleted file mode 100644 index e8c9e7e6c..000000000 --- a/util/collection/src/main/scala/sbt/Signal.scala +++ /dev/null @@ -1,85 +0,0 @@ -package sbt - -object Signals { - val CONT = "CONT" - val INT = "INT" - def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T = - { - val result = - try { - val signals = new Signals0 - signals.withHandler(signal, handler, action) - } catch { case e: LinkageError => Right(action()) } - - result match { - case Left(e) => throw e - case Right(v) => v - } - } - - /** Helper interface so we can expose internals of signal-isms to others. */ - sealed trait Registration { - def remove(): Unit - } - /** - * Register a signal handler that can be removed later. - * NOTE: Does not stack with other signal handlers!!!! - */ - def register(handler: () => Unit, signal: String = INT): Registration = - // TODO - Maybe we can just ignore things if not is-supported. - if (supported(signal)) { - import sun.misc.{ Signal, SignalHandler } - val intSignal = new Signal(signal) - val newHandler = new SignalHandler { - def handle(sig: Signal) { handler() } - } - val oldHandler = Signal.handle(intSignal, newHandler) - object unregisterNewHandler extends Registration { - override def remove(): Unit = { - Signal.handle(intSignal, oldHandler) - } - } - unregisterNewHandler - } else { - // TODO - Maybe we should just throw an exception if we don't support signals... - object NullUnregisterNewHandler extends Registration { - override def remove(): Unit = () - } - NullUnregisterNewHandler - } - - def supported(signal: String): Boolean = - try { - val signals = new Signals0 - signals.supported(signal) - } catch { case e: LinkageError => false } -} - -// Must only be referenced using a -// try { } catch { case e: LinkageError => ... } -// block to -private final class Signals0 { - def supported(signal: String): Boolean = - { - import sun.misc.Signal - try { new Signal(signal); true } - catch { case e: IllegalArgumentException => false } - } - - // returns a LinkageError in `action` as Left(t) in order to avoid it being - // incorrectly swallowed as missing Signal/SignalHandler - def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] = - { - import sun.misc.{ Signal, SignalHandler } - val intSignal = new Signal(signal) - val newHandler = new SignalHandler { - def handle(sig: Signal) { handler() } - } - - val oldHandler = Signal.handle(intSignal, newHandler) - - try Right(action()) - catch { case e: LinkageError => Left(e) } - finally Signal.handle(intSignal, oldHandler) - } -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/TypeFunctions.scala b/util/collection/src/main/scala/sbt/TypeFunctions.scala deleted file mode 100644 index 74f0a7d99..000000000 --- a/util/collection/src/main/scala/sbt/TypeFunctions.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -trait TypeFunctions { - type Id[X] = X - sealed trait Const[A] { type Apply[B] = A } - sealed trait ConstK[A] { type l[L[x]] = A } - sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } - sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] } - sealed trait P1of2[M[_, _], A] { type Apply[B] = M[A, B]; type Flip[B] = M[B, A] } - - final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) } - final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) } - final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) } - final def idFun[T] = (t: T) => t - final def const[A, B](b: B): A => B = _ => b - final def idK[M[_]]: M ~> M = new (M ~> M) { def apply[T](m: M[T]): M[T] = m } - - def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l = - f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version: - /* new ( (M ∙ G)#l ~> (N ∙ G)#l ) { - def apply[T](mg: M[G[T]]): N[G[T]] = f(mg) - }*/ - - implicit def toFn1[A, B](f: A => B): Fn1[A, B] = new Fn1[A, B] { - def ∙[C](g: C => A) = f compose g - } - - type Endo[T] = T => T - type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply -} -object TypeFunctions extends TypeFunctions - -trait ~>[-A[_], +B[_]] { outer => - def apply[T](a: A[T]): B[T] - // directly on ~> because of type inference limitations - final def ∙[C[_]](g: C ~> A): C ~> B = new (C ~> B) { def apply[T](c: C[T]) = outer.apply(g(c)) } - final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i))) - final def fn[T] = (t: A[T]) => apply[T](t) -} -object ~> { - import TypeFunctions._ - val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a } - implicit def tcIdEquals: (Id ~> Id) = Id -} -trait Fn1[A, B] { - def ∙[C](g: C => A): C => B -} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Types.scala b/util/collection/src/main/scala/sbt/Types.scala deleted file mode 100644 index 29994f3d1..000000000 --- a/util/collection/src/main/scala/sbt/Types.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -object Types extends Types - -trait Types extends TypeFunctions { - val :^: = KCons - type :+:[H, T <: HList] = HCons[H, T] - val :+: = HCons -} diff --git a/util/collection/src/main/scala/sbt/Util.scala b/util/collection/src/main/scala/sbt/Util.scala deleted file mode 100644 index befc7b5a9..000000000 --- a/util/collection/src/main/scala/sbt/Util.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -import java.util.Locale - -object Util { - def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) - - def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) = - separate(ps)(Types.idFun) - - def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = - { - val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y))) - (a.reverse, b.reverse) - } - - def prependEither[A, B](acc: (Seq[A], Seq[B]), next: Either[A, B]): (Seq[A], Seq[B]) = - next match { - case Left(l) => (l +: acc._1, acc._2) - case Right(r) => (acc._1, r +: acc._2) - } - - def pairID[A, B] = (a: A, b: B) => (a, b) - - private[this] lazy val Hypen = """-(\p{javaLowerCase})""".r - def hasHyphen(s: String): Boolean = s.indexOf('-') >= 0 - @deprecated("Use the properly spelled version: hyphenToCamel", "0.13.0") - def hypenToCamel(s: String): String = hyphenToCamel(s) - def hyphenToCamel(s: String): String = - if (hasHyphen(s)) - Hypen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) - else - s - - private[this] lazy val Camel = """(\p{javaLowerCase})(\p{javaUpperCase})""".r - def camelToHypen(s: String): String = - Camel.replaceAllIn(s, m => m.group(1) + "-" + m.group(2).toLowerCase(Locale.ENGLISH)) - - def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) '`' + s + '`' else s -} diff --git a/util/collection/src/test/scala/DagSpecification.scala b/util/collection/src/test/scala/DagSpecification.scala deleted file mode 100644 index abf9ddf28..000000000 --- a/util/collection/src/test/scala/DagSpecification.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008 Mark Harrah */ - -package sbt - -import org.scalacheck._ -import Prop._ - -import scala.collection.mutable.HashSet - -object DagSpecification extends Properties("Dag") { - property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) } - property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) } - property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) } - - implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen)) - private def dagGen(nodeCount: Int): Gen[TestDag] = - { - val nodes = new HashSet[TestDag] - def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = - { - for (i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p)) - nodes += new TestDag(i, nextDeps) - for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps) - } - Gen.parameterized(nonterminalGen) - } - - private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size - private def dependenciesPrecedeNodes(sort: List[TestDag]) = - { - val seen = new HashSet[TestDag] - def iterate(remaining: List[TestDag]): Boolean = - { - remaining match { - case Nil => true - case node :: tail => - if (node.dependencies.forall(seen.contains) && !seen.contains(node)) { - seen += node - iterate(tail) - } else - false - } - } - iterate(sort) - } -} -class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] { - override def toString = id + "->" + dependencies.mkString("[", ",", "]") -} \ No newline at end of file diff --git a/util/collection/src/test/scala/KeyTest.scala b/util/collection/src/test/scala/KeyTest.scala deleted file mode 100644 index f48e3742a..000000000 --- a/util/collection/src/test/scala/KeyTest.scala +++ /dev/null @@ -1,32 +0,0 @@ -package sbt - -import org.scalacheck._ -import Prop._ - -object KeyTest extends Properties("AttributeKey") { - property("equality") = { - compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) && - compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) && - compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) && - compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) && - compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) && - compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) && - compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) && - compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false) - } - - def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = - ("a.label: " + a.label) |: - ("a.manifest: " + a.manifest) |: - ("b.label: " + b.label) |: - ("b.manifest: " + b.manifest) |: - ("expected equal? " + same) |: - compare0(a, b, same) - - def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = - if (same) { - ("equality" |: (a == b)) && - ("hash" |: (a.hashCode == b.hashCode)) - } else - ("equality" |: (a != b)) -} \ No newline at end of file diff --git a/util/collection/src/test/scala/LiteralTest.scala b/util/collection/src/test/scala/LiteralTest.scala deleted file mode 100644 index 35ef373ca..000000000 --- a/util/collection/src/test/scala/LiteralTest.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ - -// compilation test -object LiteralTest { - def x[A[_], B[_]](f: A ~> B) = f - - import Param._ - val f = x { (p: Param[Option, List]) => p.ret(p.in.toList) } - - val a: List[Int] = f(Some(3)) - val b: List[String] = f(Some("aa")) -} \ No newline at end of file diff --git a/util/collection/src/test/scala/PMapTest.scala b/util/collection/src/test/scala/PMapTest.scala deleted file mode 100644 index 6a6c558c1..000000000 --- a/util/collection/src/test/scala/PMapTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ - -// compilation test -object PMapTest { - val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap) - mp(Some("asdf")) = "a" - mp(Some(3)) = 9 - val x = Some(3) :^: Some("asdf") :^: KNil - val y = x.transform[Id](mp) - assert(y.head == 9) - assert(y.tail.head == "a") - assert(y.tail.tail == KNil) -} \ No newline at end of file diff --git a/util/collection/src/test/scala/SettingsExample.scala b/util/collection/src/test/scala/SettingsExample.scala deleted file mode 100644 index b48bb27fc..000000000 --- a/util/collection/src/test/scala/SettingsExample.scala +++ /dev/null @@ -1,87 +0,0 @@ -package sbt - -/** Define our settings system */ - -// A basic scope indexed by an integer. -final case class Scope(nestIndex: Int, idAtIndex: Int = 0) - -// Extend the Init trait. -// (It is done this way because the Scope type parameter is used everywhere in Init. -// Lots of type constructors would become binary, which as you may know requires lots of type lambdas -// when you want a type function with only one parameter. -// That would be a general pain.) -object SettingsExample extends Init[Scope] { - // Provides a way of showing a Scope+AttributeKey[_] - val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] { - def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" - } - - // A sample delegation function that delegates to a Scope with a lower index. - val delegates: Scope => Seq[Scope] = { - case s @ Scope(index, proj) => - s +: (if (index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) }) - } - - // Not using this feature in this example. - val scopeLocal: ScopeLocal = _ => Nil - - // These three functions + a scope (here, Scope) are sufficient for defining our settings system. -} - -/** Usage Example **/ - -object SettingsUsage { - import SettingsExample._ - import Types._ - - // Define some keys - val a = AttributeKey[Int]("a") - val b = AttributeKey[Int]("b") - - // Scope these keys - val a3 = ScopedKey(Scope(3), a) - val a4 = ScopedKey(Scope(4), a) - val a5 = ScopedKey(Scope(5), a) - - val b4 = ScopedKey(Scope(4), b) - - // Define some settings - val mySettings: Seq[Setting[_]] = Seq( - setting(a3, value(3)), - setting(b4, map(a4)(_ * 3)), - update(a5)(_ + 1) - ) - - // "compiles" and applies the settings. - // This can be split into multiple steps to access intermediate results if desired. - // The 'inspect' command operates on the output of 'compile', for example. - val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey) - - // Show results. - /* for(i <- 0 to 5; k <- Seq(a, b)) { - println( k.label + i + " = " + applied.get( Scope(i), k) ) - }*/ - - /** - * Output: - * For the None results, we never defined the value and there was no value to delegate to. - * For a3, we explicitly defined it to be 3. - * a4 wasn't defined, so it delegates to a3 according to our delegates function. - * b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3 - * a5 is defined as the previous value of a5 + 1 and - * since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4. - * b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well - * a0 = None - * b0 = None - * a1 = None - * b1 = None - * a2 = None - * b2 = None - * a3 = Some(3) - * b3 = None - * a4 = Some(3) - * b4 = Some(9) - * a5 = Some(4) - * b5 = Some(9) - */ -} diff --git a/util/collection/src/test/scala/SettingsTest.scala b/util/collection/src/test/scala/SettingsTest.scala deleted file mode 100644 index d97b1056a..000000000 --- a/util/collection/src/test/scala/SettingsTest.scala +++ /dev/null @@ -1,195 +0,0 @@ -package sbt - -import org.scalacheck._ -import Prop._ -import SettingsUsage._ -import SettingsExample._ - -object SettingsTest extends Properties("settings") { - - import scala.reflect.Manifest - - final val ChainMax = 5000 - lazy val chainLengthGen = Gen.choose(1, ChainMax) - - property("Basic settings test") = secure(all(tests: _*)) - - property("Basic chain") = forAll(chainLengthGen) { (i: Int) => - val abs = math.abs(i) - singleIntTest(chain(abs, value(0)), abs) - } - property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) => - val abs = math.abs(i) - singleIntTest(chainBind(value(abs)), 0) - } - - property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference } - final def allowedReference(intermediate: Int): Prop = - { - val top = value(intermediate) - def iterate(init: Initialize[Int]): Initialize[Int] = - bind(init) { t => - if (t <= 0) - top - else - iterate(value(t - 1)) - } - evaluate(setting(chk, iterate(top)) :: Nil); true - } - - property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings } - final def derivedSettings(nr: Int): Prop = - { - val genScopedKeys = { - // We wan - // t to generate lists of keys that DO NOT inclue the "ch" key we use to check things. - val attrKeys = mkAttrKeys[Int](nr).filter(_.forall(_.label != "ch")) - attrKeys map (_ map (ak => ScopedKey(Scope(0), ak))) - }.label("scopedKeys").filter(_.nonEmpty) - forAll(genScopedKeys) { scopedKeys => - try { - // Note; It's evil to grab last IF you haven't verified the set can't be empty. - val last = scopedKeys.last - val derivedSettings: Seq[Setting[Int]] = ( - for { - List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 - nextInit = if (scoped0 == chk) chk - else (scoped0 zipWith chk) { (p, _) => p + 1 } - } yield derive(setting(scoped1, nextInit)) - ).toSeq - - { - // Note: This causes a cycle refernec error, quite frequently. - checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" - } && { - checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" - } - } catch { - case t: Throwable => - // TODO - For debugging only. - t.printStackTrace(System.err) - throw t - } - } - } - - private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = - { - import Gen._ - val nonEmptyAlphaStr = - nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter)) - - (for { - list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size) - item <- list - } yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)") - } - - property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition - final def derivedKeepsPosition: Prop = - { - val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a")) - val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b")) - val prop1 = { - val settings: Seq[Setting[_]] = Seq( - setting(a, value(3)), - setting(b, value(6)), - derive(setting(b, a)), - setting(a, value(5)), - setting(b, value(8)) - ) - val ev = evaluate(settings) - checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) - } - val prop2 = { - val settings: Seq[Setting[Int]] = Seq( - setting(a, value(3)), - setting(b, value(6)), - derive(setting(b, a)), - setting(a, value(5)) - ) - val ev = evaluate(settings) - checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) - } - prop1 && prop2 - } - - property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope } - final def derivedSettingsScope(nrProjects: Int): Prop = - { - forAll(mkAttrKeys[Int](2)) { - case List(key, derivedKey) => - val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key) - val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey) - val globalKey = ScopedKey(Scope(0), key) - val globalDerivedKey = ScopedKey(Scope(0), derivedKey) - // Each project defines an initial value, but the update is defined in globalKey. - // However, the derived Settings that come from this should be scoped in each project. - val settings: Seq[Setting[_]] = - derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) - val ev = evaluate(settings) - // Also check that the key has no value at the "global" scope - val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) - checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*) - } - } - - // Circular (dynamic) references currently loop infinitely. - // This is the expected behavior (detecting dynamic cycles is expensive), - // but it may be necessary to provide an option to detect them (with a performance hit) - // This would test that cycle detection. - // property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ } - final def checkCircularReferences(intermediate: Int): Prop = - { - val ccr = new CCR(intermediate) - try { evaluate(setting(chk, ccr.top) :: Nil); false } - catch { case e: java.lang.Exception => true } - } - - def tests = - for (i <- 0 to 5; k <- Seq(a, b)) yield { - val expected = expectedValues(2 * i + (if (k == a) 0 else 1)) - checkKey[Int](ScopedKey(Scope(i), k), expected, applied) - } - - lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil - - lazy val ch = AttributeKey[Int]("ch") - lazy val chk = ScopedKey(Scope(0), ch) - def chain(i: Int, prev: Initialize[Int]): Initialize[Int] = - if (i <= 0) prev else chain(i - 1, prev(_ + 1)) - - def chainBind(prev: Initialize[Int]): Initialize[Int] = - bind(prev) { v => - if (v <= 0) prev else chainBind(value(v - 1)) - } - def singleIntTest(i: Initialize[Int], expected: Int) = - { - val eval = evaluate(setting(chk, i) :: Nil) - checkKey(chk, Some(expected), eval) - } - - def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = - { - val value = settings.get(key.scope, key.key) - ("Key: " + key) |: - ("Value: " + value) |: - ("Expected: " + expected) |: - (value == expected) - } - - def evaluate(settings: Seq[Setting[_]]): Settings[Scope] = - try { make(settings)(delegates, scopeLocal, showFullKey) } - catch { case e: Throwable => e.printStackTrace; throw e } -} -// This setup is a workaround for module synchronization issues -final class CCR(intermediate: Int) { - lazy val top = iterate(value(intermediate), intermediate) - def iterate(init: Initialize[Int], i: Int): Initialize[Int] = - bind(init) { t => - if (t <= 0) - top - else - iterate(value(t - 1), t - 1) - } -} diff --git a/util/complete/NOTICE b/util/complete/NOTICE deleted file mode 100644 index a6f2c1de4..000000000 --- a/util/complete/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Completion Component -Copyright 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/LineReader.scala b/util/complete/src/main/scala/sbt/LineReader.scala deleted file mode 100644 index b85190f92..000000000 --- a/util/complete/src/main/scala/sbt/LineReader.scala +++ /dev/null @@ -1,139 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import jline.console.ConsoleReader -import jline.console.history.{ FileHistory, MemoryHistory } -import java.io.{ File, InputStream, PrintWriter } -import complete.Parser -import java.util.concurrent.atomic.AtomicBoolean - -abstract class JLine extends LineReader { - protected[this] val handleCONT: Boolean - protected[this] val reader: ConsoleReader - - def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) } - - private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]) = - readLineWithHistory(prompt, mask) match { - case null => None - case x => Some(x.trim) - } - - private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String = - reader.getHistory match { - case fh: FileHistory => - try { readLineDirect(prompt, mask) } - finally { fh.flush() } - case _ => readLineDirect(prompt, mask) - } - - private[this] def readLineDirect(prompt: String, mask: Option[Char]): String = - if (handleCONT) - Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask)) - else - readLineDirectRaw(prompt, mask) - private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String = - { - val newprompt = handleMultilinePrompt(prompt) - mask match { - case Some(m) => reader.readLine(newprompt, m) - case None => reader.readLine(newprompt) - } - } - - private[this] def handleMultilinePrompt(prompt: String): String = { - val lines = """\r?\n""".r.split(prompt) - lines.length match { - case 0 | 1 => prompt - case _ => reader.print(lines.init.mkString("\n") + "\n"); lines.last; - } - } - - private[this] def resume() { - jline.TerminalFactory.reset - JLine.terminal.init - reader.drawLine() - reader.flush() - } -} -private object JLine { - private[this] val TerminalProperty = "jline.terminal" - - fixTerminalProperty() - - // translate explicit class names to type in order to support - // older Scala, since it shaded classes but not the system property - private[sbt] def fixTerminalProperty() { - val newValue = System.getProperty(TerminalProperty) match { - case "jline.UnixTerminal" => "unix" - case null if System.getProperty("sbt.cygwin") != null => "unix" - case "jline.WindowsTerminal" => "windows" - case "jline.AnsiWindowsTerminal" => "windows" - case "jline.UnsupportedTerminal" => "none" - case x => x - } - if (newValue != null) System.setProperty(TerminalProperty, newValue) - } - - // When calling this, ensure that enableEcho has been or will be called. - // TerminalFactory.get will initialize the terminal to disable echo. - private def terminal = jline.TerminalFactory.get - private def withTerminal[T](f: jline.Terminal => T): T = - synchronized { - val t = terminal - t.synchronized { f(t) } - } - /** - * For accessing the JLine Terminal object. - * This ensures synchronized access as well as re-enabling echo after getting the Terminal. - */ - def usingTerminal[T](f: jline.Terminal => T): T = - withTerminal { t => - t.restore - f(t) - } - def createReader(): ConsoleReader = createReader(None) - def createReader(historyPath: Option[File]): ConsoleReader = - usingTerminal { t => - val cr = new ConsoleReader - cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 - cr.setBellEnabled(false) - val h = historyPath match { - case None => new MemoryHistory - case Some(file) => new FileHistory(file) - } - h.setMaxSize(MaxHistorySize) - cr.setHistory(h) - cr - } - def withJLine[T](action: => T): T = - withTerminal { t => - t.init - try { action } - finally { t.restore } - } - - def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT) - val MaxHistorySize = 500 - val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) -} - -trait LineReader { - def readLine(prompt: String, mask: Option[Char] = None): Option[String] -} -final class FullReader(historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine { - protected[this] val reader = - { - val cr = JLine.createReader(historyPath) - sbt.complete.JLineCompletion.installCustomCompletor(cr, complete) - cr - } -} - -class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean) extends JLine { - protected[this] val reader = JLine.createReader(historyPath) -} -object SimpleReader extends SimpleReader(None, JLine.HandleCONT) - diff --git a/util/complete/src/main/scala/sbt/complete/Completions.scala b/util/complete/src/main/scala/sbt/complete/Completions.scala deleted file mode 100644 index 5237ad26d..000000000 --- a/util/complete/src/main/scala/sbt/complete/Completions.scala +++ /dev/null @@ -1,144 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt.complete - -/** - * Represents a set of completions. - * It exists instead of implicitly defined operations on top of Set[Completion] - * for laziness. - */ -sealed trait Completions { - def get: Set[Completion] - final def x(o: Completions): Completions = flatMap(_ x o) - final def ++(o: Completions): Completions = Completions(get ++ o.get) - final def +:(o: Completion): Completions = Completions(get + o) - final def filter(f: Completion => Boolean): Completions = Completions(get filter f) - final def filterS(f: String => Boolean): Completions = filter(c => f(c.append)) - override def toString = get.mkString("Completions(", ",", ")") - final def flatMap(f: Completion => Completions): Completions = Completions(get.flatMap(c => f(c).get)) - final def map(f: Completion => Completion): Completions = Completions(get map f) - override final def hashCode = get.hashCode - override final def equals(o: Any) = o match { case c: Completions => get == c.get; case _ => false } -} -object Completions { - /** Returns a lazy Completions instance using the provided Completion Set. */ - def apply(cs: => Set[Completion]): Completions = new Completions { - lazy val get = cs - } - - /** Returns a strict Completions instance using the provided Completion Set. */ - def strict(cs: Set[Completion]): Completions = apply(cs) - - /** - * No suggested completions, not even the empty Completion. - * This typically represents invalid input. - */ - val nil: Completions = strict(Set.empty) - - /** - * Only includes an empty Suggestion. - * This typically represents valid input that either has no completions or accepts no further input. - */ - val empty: Completions = strict(Set.empty + Completion.empty) - - /** Returns a strict Completions instance containing only the provided Completion.*/ - def single(c: Completion): Completions = strict(Set.empty + c) -} - -/** - * Represents a completion. - * The abstract members `display` and `append` are best explained with an example. - * - * Assuming space-delimited tokens, processing this: - * am is are w - * could produce these Completions: - * Completion { display = "was"; append = "as" } - * Completion { display = "were"; append = "ere" } - * to suggest the tokens "was" and "were". - * - * In this way, two pieces of information are preserved: - * 1) what needs to be appended to the current input if a completion is selected - * 2) the full token being completed, which is useful for presenting a user with choices to select - */ -sealed trait Completion { - /** The proposed suffix to append to the existing input to complete the last token in the input.*/ - def append: String - /** The string to present to the user to represent the full token being suggested.*/ - def display: String - /** True if this Completion is suggesting the empty string.*/ - def isEmpty: Boolean - - /** Appends the completions in `o` with the completions in this Completion.*/ - def ++(o: Completion): Completion = Completion.concat(this, o) - final def x(o: Completions): Completions = if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this) - override final lazy val hashCode = Completion.hashCode(this) - override final def equals(o: Any) = o match { case c: Completion => Completion.equal(this, c); case _ => false } -} -final class DisplayOnly(val display: String) extends Completion { - def isEmpty = display.isEmpty - def append = "" - override def toString = "{" + display + "}" -} -final class Token(val display: String, val append: String) extends Completion { - @deprecated("Retained only for compatibility. All information is now in `display` and `append`.", "0.12.1") - lazy val prepend = display.stripSuffix(append) - def isEmpty = display.isEmpty && append.isEmpty - override final def toString = "[" + display + "]++" + append -} -final class Suggestion(val append: String) extends Completion { - def isEmpty = append.isEmpty - def display = append - override def toString = append -} -object Completion { - def concat(a: Completion, b: Completion): Completion = - (a, b) match { - case (as: Suggestion, bs: Suggestion) => suggestion(as.append + bs.append) - case (at: Token, _) if at.append.isEmpty => b - case _ if a.isEmpty => b - case _ => a - } - def evaluatesRight(a: Completion): Boolean = - a match { - case _: Suggestion => true - case at: Token if at.append.isEmpty => true - case _ => a.isEmpty - } - - def equal(a: Completion, b: Completion): Boolean = - (a, b) match { - case (as: Suggestion, bs: Suggestion) => as.append == bs.append - case (ad: DisplayOnly, bd: DisplayOnly) => ad.display == bd.display - case (at: Token, bt: Token) => at.display == bt.display && at.append == bt.append - case _ => false - } - - def hashCode(a: Completion): Int = - a match { - case as: Suggestion => (0, as.append).hashCode - case ad: DisplayOnly => (1, ad.display).hashCode - case at: Token => (2, at.display, at.append).hashCode - } - - val empty: Completion = suggestion("") - def single(c: Char): Completion = suggestion(c.toString) - - // TODO: make strict in 0.13.0 to match DisplayOnly - def displayOnly(value: => String): Completion = new DisplayOnly(value) - @deprecated("Use displayOnly.", "0.12.1") - def displayStrict(value: String): Completion = displayOnly(value) - - // TODO: make strict in 0.13.0 to match Token - def token(prepend: => String, append: => String): Completion = new Token(prepend + append, append) - @deprecated("Use token.", "0.12.1") - def tokenStrict(prepend: String, append: String): Completion = token(prepend, append) - - /** @since 0.12.1 */ - def tokenDisplay(append: String, display: String): Completion = new Token(display, append) - - // TODO: make strict in 0.13.0 to match Suggestion - def suggestion(value: => String): Completion = new Suggestion(value) - @deprecated("Use suggestion.", "0.12.1") - def suggestStrict(value: String): Completion = suggestion(value) -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/EditDistance.scala b/util/complete/src/main/scala/sbt/complete/EditDistance.scala deleted file mode 100644 index 95ed0c91f..000000000 --- a/util/complete/src/main/scala/sbt/complete/EditDistance.scala +++ /dev/null @@ -1,41 +0,0 @@ -package sbt.complete - -import java.lang.Character.{ toLowerCase => lower } - -/** @author Paul Phillips*/ -object EditDistance { - /** - * Translated from the java version at - * http://www.merriampark.com/ld.htm - * which is declared to be public domain. - */ - def levenshtein(s: String, t: String, insertCost: Int = 1, deleteCost: Int = 1, subCost: Int = 1, transposeCost: Int = 1, matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false): Int = { - val n = s.length - val m = t.length - if (n == 0) return m - if (m == 0) return n - - val d = Array.ofDim[Int](n + 1, m + 1) - 0 to n foreach (x => d(x)(0) = x) - 0 to m foreach (x => d(0)(x) = x) - - for (i <- 1 to n; s_i = s(i - 1); j <- 1 to m) { - val t_j = t(j - 1) - val cost = if (s_i == t_j) matchCost else if (lower(s_i) == lower(t_j)) caseCost else subCost - val tcost = if (s_i == t_j) matchCost else transposeCost - - val c1 = d(i - 1)(j) + deleteCost - val c2 = d(i)(j - 1) + insertCost - val c3 = d(i - 1)(j - 1) + cost - - d(i)(j) = c1 min c2 min c3 - - if (transpositions) { - if (i > 1 && j > 1 && s(i - 1) == t(j - 2) && s(i - 2) == t(j - 1)) - d(i)(j) = d(i)(j) min (d(i - 2)(j - 2) + cost) - } - } - - d(n)(m) - } -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/ExampleSource.scala b/util/complete/src/main/scala/sbt/complete/ExampleSource.scala deleted file mode 100644 index 52d96246b..000000000 --- a/util/complete/src/main/scala/sbt/complete/ExampleSource.scala +++ /dev/null @@ -1,59 +0,0 @@ -package sbt.complete - -import java.io.File -import sbt.IO - -/** - * These sources of examples are used in parsers for user input completion. An example of such a source is the - * [[sbt.complete.FileExamples]] class, which provides a list of suggested files to the user as they press the - * TAB key in the console. - */ -trait ExampleSource { - /** - * @return a (possibly lazy) list of completion example strings. These strings are continuations of user's input. The - * user's input is incremented with calls to [[withAddedPrefix]]. - */ - def apply(): Iterable[String] - - /** - * @param addedPrefix a string that just typed in by the user. - * @return a new source of only those examples that start with the string typed by the user so far (with addition of - * the just added prefix). - */ - def withAddedPrefix(addedPrefix: String): ExampleSource -} - -/** - * A convenience example source that wraps any collection of strings into a source of examples. - * @param examples the examples that will be displayed to the user when they press the TAB key. - */ -sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource { - override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix)) - - override def apply(): Iterable[String] = examples - - private def examplesWithRemovedPrefix(prefix: String) = examples.collect { - case example if example startsWith prefix => example substring prefix.length - } -} - -/** - * Provides path completion examples based on files in the base directory. - * @param base the directory within which this class will search for completion examples. - * @param prefix the part of the path already written by the user. - */ -class FileExamples(base: File, prefix: String = "") extends ExampleSource { - override def apply(): Stream[String] = files(base).map(_ substring prefix.length) - - override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix) - - protected def files(directory: File): Stream[String] = { - val childPaths = IO.listFiles(directory).toStream - val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter { _ startsWith prefix } - val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter { dirStartsWithPrefix } - prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir))) - } - - private def dirStartsWithPrefix(relativizedPath: String): Boolean = - (relativizedPath startsWith prefix) || (prefix startsWith relativizedPath) -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/History.scala b/util/complete/src/main/scala/sbt/complete/History.scala deleted file mode 100644 index 26d0a27c6..000000000 --- a/util/complete/src/main/scala/sbt/complete/History.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package complete - -import History.number -import java.io.File - -final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) extends NotNull { - private def reversed = lines.reverse - - def all: Seq[String] = lines - def size = lines.length - def !! : Option[String] = !-(1) - def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i); None } - def !(i: Int): Option[String] = apply(i) - - def !(s: String): Option[String] = - number(s) match { - case Some(n) => if (n < 0) !-(-n) else apply(n) - case None => nonEmpty(s) { reversed.find(_.startsWith(s)) } - } - def !-(n: Int): Option[String] = apply(size - n - 1) - - def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) } - - private def nonEmpty[T](s: String)(act: => Option[T]): Option[T] = - if (s.isEmpty) { - sys.error("No action specified to history command") - None - } else - act - - def list(historySize: Int, show: Int): Seq[String] = - lines.toList.drop((lines.size - historySize) max 0).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1) -} - -object History { - def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path, sys.error) - - def number(s: String): Option[Int] = - try { Some(s.toInt) } - catch { case e: NumberFormatException => None } -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala b/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala deleted file mode 100644 index 1e124c583..000000000 --- a/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ /dev/null @@ -1,73 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt -package complete - -import java.io.File - -object HistoryCommands { - val Start = "!" - // second characters - val Contains = "?" - val Last = "!" - val ListCommands = ":" - - def ContainsFull = h(Contains) - def LastFull = h(Last) - def ListFull = h(ListCommands) - - def ListN = ListFull + "n" - def ContainsString = ContainsFull + "string" - def StartsWithString = Start + "string" - def Previous = Start + "-n" - def Nth = Start + "n" - - private def h(s: String) = Start + s - def plainCommands = Seq(ListFull, Start, LastFull, ContainsFull) - - def descriptions = Seq( - LastFull -> "Execute the last command again", - ListFull -> "Show all previous commands", - ListN -> "Show the last n commands", - Nth -> ("Execute the command with index n, as shown by the " + ListFull + " command"), - Previous -> "Execute the nth command before this one", - StartsWithString -> "Execute the most recent command starting with 'string'", - ContainsString -> "Execute the most recent command containing 'string'" - ) - def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") - def printHelp(): Unit = - println(helpString) - def printHistory(history: complete.History, historySize: Int, show: Int): Unit = - history.list(historySize, show).foreach(println) - - import DefaultParsers._ - - val MaxLines = 500 - lazy val num = token(NatBasic, "") - lazy val last = Last ^^^ { execute(_ !!) } - lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => - (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } - } - lazy val execStr = flag('?') ~ token(any.+.string, "") map { - case (contains, str) => - execute(h => if (contains) h !? str else h ! str) - } - lazy val execInt = flag('-') ~ num map { - case (neg, value) => - execute(h => if (neg) h !- value else h ! value) - } - lazy val help = success((h: History) => { printHelp(); Some(Nil) }) - - def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => - { - val command = f(h).filterNot(_.startsWith(Start)) - val lines = h.lines.toArray - command.foreach(lines(lines.length - 1) = _) - h.path foreach { h => IO.writeLines(h, lines) } - Some(command.toList) - } - - val actionParser: Parser[complete.History => Option[List[String]]] = - Start ~> (help | last | execInt | list | execStr) // execStr must come last -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala b/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala deleted file mode 100644 index fed89541f..000000000 --- a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala +++ /dev/null @@ -1,156 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt.complete - -import jline.console.ConsoleReader -import jline.console.completer.{ CandidateListCompletionHandler, Completer, CompletionHandler } -import scala.annotation.tailrec -import collection.JavaConversions - -object JLineCompletion { - def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit = - installCustomCompletor(reader)(parserAsCompletor(parser)) - def installCustomCompletor(reader: ConsoleReader)(complete: (String, Int) => (Seq[String], Seq[String])): Unit = - installCustomCompletor(customCompletor(complete), reader) - def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit = - { - reader.removeCompleter(DummyCompletor) - reader.addCompleter(DummyCompletor) - reader.setCompletionHandler(new CustomHandler(complete)) - } - - private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) extends CompletionHandler { - private[this] var previous: Option[(String, Int)] = None - private[this] var level: Int = 1 - override def complete(reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int) = { - val current = Some(bufferSnapshot(reader)) - level = if (current == previous) level + 1 else 1 - previous = current - try completeImpl(reader, level) - catch { - case e: Exception => - reader.print("\nException occurred while determining completions.") - e.printStackTrace() - false - } - } - } - - // always provides dummy completions so that the custom completion handler gets called - // (ConsoleReader doesn't call the handler if there aren't any completions) - // the custom handler will then throw away the candidates and call the custom function - private[this] final object DummyCompletor extends Completer { - override def complete(buffer: String, cursor: Int, candidates: java.util.List[CharSequence]): Int = - { - candidates.asInstanceOf[java.util.List[String]] add "dummy" - 0 - } - } - - def parserAsCompletor(p: Parser[_]): (String, Int) => (Seq[String], Seq[String]) = - (str, level) => convertCompletions(Parser.completions(p, str, level)) - - def convertCompletions(c: Completions): (Seq[String], Seq[String]) = - { - val cs = c.get - if (cs.isEmpty) - (Nil, "{invalid input}" :: Nil) - else - convertCompletions(cs) - } - def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = - { - val (insert, display) = - ((Set.empty[String], Set.empty[String]) /: cs) { - case (t @ (insert, display), comp) => - if (comp.isEmpty) t else (insert + comp.append, appendNonEmpty(display, comp.display)) - } - (insert.toSeq, display.toSeq.sorted) - } - def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add - - def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean = - (reader, level) => { - val success = complete(beforeCursor(reader), reader => f(reader, level), reader) - reader.flush() - success - } - - def bufferSnapshot(reader: ConsoleReader): (String, Int) = - { - val b = reader.getCursorBuffer - (b.buffer.toString, b.cursor) - } - def beforeCursor(reader: ConsoleReader): String = - { - val b = reader.getCursorBuffer - b.buffer.substring(0, b.cursor) - } - - // returns false if there was nothing to insert and nothing to display - def complete(beforeCursor: String, completions: String => (Seq[String], Seq[String]), reader: ConsoleReader): Boolean = - { - val (insert, display) = completions(beforeCursor) - val common = commonPrefix(insert) - if (common.isEmpty) - if (display.isEmpty) - () - else - showCompletions(display, reader) - else - appendCompletion(common, reader) - - !(common.isEmpty && display.isEmpty) - } - - def appendCompletion(common: String, reader: ConsoleReader): Unit = { - reader.getCursorBuffer.write(common) - reader.redrawLine() - } - - /** - * `display` is assumed to be the exact strings requested to be displayed. - * In particular, duplicates should have been removed already. - */ - def showCompletions(display: Seq[String], reader: ConsoleReader): Unit = { - printCompletions(display, reader) - reader.drawLine() - } - def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = { - val print = shouldPrint(cs, reader) - reader.println() - if (print) printLinesAndColumns(cs, reader) - } - def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = { - val (lines, columns) = cs partition hasNewline - for (line <- lines) { - reader.print(line) - if (line.charAt(line.length - 1) != '\n') - reader.println() - } - reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim))) - } - def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0 - def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = - { - val size = cs.size - (size <= reader.getAutoprintThreshold) || - confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader) - } - def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = - { - reader.println() - reader.print(prompt) - reader.flush() - reader.readCharacter(trueC, falseC) == trueC - } - - def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix - def commonPrefix(a: String, b: String): String = - { - val len = a.length min b.length - @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) - a.substring(0, loop(0)) - } -} diff --git a/util/complete/src/main/scala/sbt/complete/Parser.scala b/util/complete/src/main/scala/sbt/complete/Parser.scala deleted file mode 100644 index 892119f6c..000000000 --- a/util/complete/src/main/scala/sbt/complete/Parser.scala +++ /dev/null @@ -1,848 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2010, 2011 Mark Harrah - */ -package sbt.complete - -import Parser._ -import sbt.Types.{ left, right, some } -import sbt.Util.{ makeList, separate } - -/** - * A String parser that provides semi-automatic tab completion. - * A successful parse results in a value of type `T`. - * The methods in this trait are what must be implemented to define a new Parser implementation, but are not typically useful for common usage. - * Instead, most useful methods for combining smaller parsers into larger parsers are implicitly added by the [[RichParser]] type. - */ -sealed trait Parser[+T] { - def derive(i: Char): Parser[T] - def resultEmpty: Result[T] - def result: Option[T] - def completions(level: Int): Completions - def failure: Option[Failure] - def isTokenStart = false - def ifValid[S](p: => Parser[S]): Parser[S] - def valid: Boolean -} -sealed trait RichParser[A] { - /** Apply the original Parser and then apply `next` (in order). The result of both is provides as a pair. */ - def ~[B](next: Parser[B]): Parser[(A, B)] - - /** Apply the original Parser one or more times and provide the non-empty sequence of results.*/ - def + : Parser[Seq[A]] - - /** Apply the original Parser zero or more times and provide the (potentially empty) sequence of results.*/ - def * : Parser[Seq[A]] - - /** Apply the original Parser zero or one times, returning None if it was applied zero times or the result wrapped in Some if it was applied once.*/ - def ? : Parser[Option[A]] - - /** Apply either the original Parser or `b`.*/ - def |[B >: A](b: Parser[B]): Parser[B] - - /** Apply either the original Parser or `b`.*/ - def ||[B](b: Parser[B]): Parser[Either[A, B]] - - /** Apply the original Parser to the input and then apply `f` to the result.*/ - def map[B](f: A => B): Parser[B] - - /** - * Returns the original parser. This is useful for converting literals to Parsers. - * For example, `'c'.id` or `"asdf".id` - */ - def id: Parser[A] - - /** Apply the original Parser, but provide `value` as the result if it succeeds. */ - def ^^^[B](value: B): Parser[B] - - /** Apply the original Parser, but provide `alt` as the result if it fails.*/ - def ??[B >: A](alt: B): Parser[B] - - /** - * Produces a Parser that applies the original Parser and then applies `next` (in order), discarding the result of `next`. - * (The arrow point in the direction of the retained result.) - */ - def <~[B](b: Parser[B]): Parser[A] - - /** - * Produces a Parser that applies the original Parser and then applies `next` (in order), discarding the result of the original parser. - * (The arrow point in the direction of the retained result.) - */ - def ~>[B](b: Parser[B]): Parser[B] - - /** Uses the specified message if the original Parser fails.*/ - def !!!(msg: String): Parser[A] - - /** - * If an exception is thrown by the original Parser, - * capture it and fail locally instead of allowing the exception to propagate up and terminate parsing. - */ - def failOnException: Parser[A] - - @deprecated("Use `not` and explicitly provide the failure message", "0.12.2") - def unary_- : Parser[Unit] - - /** - * Apply the original parser, but only succeed if `o` also succeeds. - * Note that `o` does not need to consume the same amount of input to satisfy this condition. - */ - def &(o: Parser[_]): Parser[A] - - @deprecated("Use `and` and `not` and explicitly provide the failure message", "0.12.2") - def -(o: Parser[_]): Parser[A] - - /** Explicitly defines the completions for the original Parser.*/ - def examples(s: String*): Parser[A] - - /** Explicitly defines the completions for the original Parser.*/ - def examples(s: Set[String], check: Boolean = false): Parser[A] - - /** - * @param exampleSource the source of examples when displaying completions to the user. - * @param maxNumberOfExamples limits the number of examples that the source of examples should return. This can - * prevent lengthy pauses and avoids bad interactive user experience. - * @param removeInvalidExamples indicates whether completion examples should be checked for validity (against the - * given parser). Invalid examples will be filtered out and only valid suggestions will - * be displayed. - * @return a new parser with a new source of completions. - */ - def examples(exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] - - /** - * @param exampleSource the source of examples when displaying completions to the user. - * @return a new parser with a new source of completions. It displays at most 25 completion examples and does not - * remove invalid examples. - */ - def examples(exampleSource: ExampleSource): Parser[A] = examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false) - - /** Converts a Parser returning a Char sequence to a Parser returning a String.*/ - def string(implicit ev: A <:< Seq[Char]): Parser[String] - - /** - * Produces a Parser that filters the original parser. - * If 'f' is not true when applied to the output of the original parser, the Parser returned by this method fails. - * The failure message is constructed by applying `msg` to the String that was successfully parsed by the original parser. - */ - def filter(f: A => Boolean, msg: String => String): Parser[A] - - /** Applies the original parser, applies `f` to the result to get the next parser, and applies that parser and uses its result for the overall result. */ - def flatMap[B](f: A => Parser[B]): Parser[B] -} - -/** Contains Parser implementation helper methods not typically needed for using parsers. */ -object Parser extends ParserMain { - sealed abstract class Result[+T] { - def isFailure: Boolean - def isValid: Boolean - def errors: Seq[String] - def or[B >: T](b: => Result[B]): Result[B] - def either[B](b: => Result[B]): Result[Either[T, B]] - def map[B](f: T => B): Result[B] - def flatMap[B](f: T => Result[B]): Result[B] - def &&(b: => Result[_]): Result[T] - def filter(f: T => Boolean, msg: => String): Result[T] - def seq[B](b: => Result[B]): Result[(T, B)] = app(b)((m, n) => (m, n)) - def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] - def toEither: Either[() => Seq[String], T] - } - final case class Value[+T](value: T) extends Result[T] { - def isFailure = false - def isValid: Boolean = true - def errors = Nil - def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match { - case fail: Failure => fail - case Value(bv) => Value(f(value, bv)) - } - def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this } - def or[B >: T](b: => Result[B]): Result[B] = this - def either[B](b: => Result[B]): Result[Either[T, B]] = Value(Left(value)) - def map[B](f: T => B): Result[B] = Value(f(value)) - def flatMap[B](f: T => Result[B]): Result[B] = f(value) - def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg) - def toEither = Right(value) - } - final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) extends Result[Nothing] { - lazy val errors: Seq[String] = mkErrors - def isFailure = true - def isValid = false - def map[B](f: Nothing => B) = this - def flatMap[B](f: Nothing => Result[B]) = this - def or[B](b: => Result[B]): Result[B] = b match { - case v: Value[B] => v - case f: Failure => if (definitive) this else this ++ f - } - def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match { - case Value(v) => Value(Right(v)) - case f: Failure => if (definitive) this else this ++ f - } - def filter(f: Nothing => Boolean, msg: => String) = this - def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this - def &&(b: => Result[_]) = this - def toEither = Left(() => errors) - - private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors) - } - def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive) - def mkFailure(error: => String, definitive: Boolean = false): Failure = new Failure(error :: Nil, definitive) - - @deprecated("This method is deprecated and will be removed in the next major version. Use the parser directly to check for invalid completions.", since = "0.13.2") - def checkMatches(a: Parser[_], completions: Seq[String]): Unit = { - val bad = completions.filter(apply(a)(_).resultEmpty.isFailure) - if (bad.nonEmpty) sys.error("Invalid example completions: " + bad.mkString("'", "', '", "'")) - } - - def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] = - (a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None } - - def mapParser[A, B](a: Parser[A], f: A => B): Parser[B] = - a.ifValid { - a.result match { - case Some(av) => success(f(av)) - case None => new MapParser(a, f) - } - } - - def bindParser[A, B](a: Parser[A], f: A => Parser[B]): Parser[B] = - a.ifValid { - a.result match { - case Some(av) => f(av) - case None => new BindParser(a, f) - } - } - - def filterParser[T](a: Parser[T], f: T => Boolean, seen: String, msg: String => String): Parser[T] = - a.ifValid { - a.result match { - case Some(av) if f(av) => success(av) - case _ => new Filter(a, f, seen, msg) - } - } - - def seqParser[A, B](a: Parser[A], b: Parser[B]): Parser[(A, B)] = - a.ifValid { - b.ifValid { - (a.result, b.result) match { - case (Some(av), Some(bv)) => success((av, bv)) - case (Some(av), None) => b map { bv => (av, bv) } - case (None, Some(bv)) => a map { av => (av, bv) } - case (None, None) => new SeqParser(a, b) - } - } - } - - def choiceParser[A, B](a: Parser[A], b: Parser[B]): Parser[Either[A, B]] = - if (a.valid) - if (b.valid) new HetParser(a, b) else a.map(left.fn) - else - b.map(right.fn) - - def opt[T](a: Parser[T]): Parser[Option[T]] = - if (a.valid) new Optional(a) else success(None) - - def onFailure[T](delegate: Parser[T], msg: String): Parser[T] = - if (delegate.valid) new OnFailure(delegate, msg) else failure(msg) - def trapAndFail[T](delegate: Parser[T]): Parser[T] = - delegate.ifValid(new TrapAndFail(delegate)) - - def zeroOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 0, Infinite) - def oneOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 1, Infinite) - - def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] = - repeat(None, p, min, max, Nil) - private[complete] def repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, revAcc: List[T]): Parser[Seq[T]] = - { - assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") - assume(max >= min, "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")") - - def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = - repeated match { - case i: Invalid if min == 0 => invalidButOptional - case i: Invalid => i - case _ => - repeated.result match { - case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here - case None => if (max.isZero) success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc) - } - } - - partial match { - case Some(part) => - part.ifValid { - part.result match { - case Some(value) => repeat(None, repeated, min, max, value :: revAcc) - case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse)) - } - } - case None => checkRepeated(success(Nil)) - } - } - - @deprecated("Explicitly call `and` and `not` to provide the failure message.", "0.12.2") - def sub[T](a: Parser[T], b: Parser[_]): Parser[T] = and(a, not(b)) - - def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b))) -} -trait ParserMain { - /** Provides combinators for Parsers.*/ - implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] { - def ~[B](b: Parser[B]) = seqParser(a, b) - def ||[B](b: Parser[B]) = choiceParser(a, b) - def |[B >: A](b: Parser[B]) = homParser(a, b) - def ? = opt(a) - def * = zeroOrMore(a) - def + = oneOrMore(a) - def map[B](f: A => B) = mapParser(a, f) - def id = a - - def ^^^[B](value: B): Parser[B] = a map { _ => value } - def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt } - def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av } - def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv } - def !!!(msg: String): Parser[A] = onFailure(a, msg) - def failOnException: Parser[A] = trapAndFail(a) - - def unary_- = not(a) - def &(o: Parser[_]) = and(a, o) - def -(o: Parser[_]) = sub(a, o) - def examples(s: String*): Parser[A] = examples(s.toSet) - def examples(s: Set[String], check: Boolean = false): Parser[A] = examples(new FixedSetExamples(s), s.size, check) - def examples(s: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples) - def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) - def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) - def flatMap[B](f: A => Parser[B]) = bindParser(a, f) - } - - implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) - implicit def literalRichStringParser(s: String): RichParser[String] = richParser(s) - - /** - * Construct a parser that is valid, but has no valid result. This is used as a way - * to provide a definitive Failure when a parser doesn't match empty input. For example, - * in `softFailure(...) | p`, if `p` doesn't match the empty sequence, the failure will come - * from the Parser constructed by the `softFailure` method. - */ - private[sbt] def softFailure(msg: => String, definitive: Boolean = false): Parser[Nothing] = - SoftInvalid(mkFailures(msg :: Nil, definitive)) - - /** - * Defines a parser that always fails on any input with messages `msgs`. - * If `definitive` is `true`, any failures by later alternatives are discarded. - */ - def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] = Invalid(mkFailures(msgs, definitive)) - - /** - * Defines a parser that always fails on any input with message `msg`. - * If `definitive` is `true`, any failures by later alternatives are discarded. - */ - def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] = invalid(msg :: Nil, definitive) - - /** Defines a parser that always succeeds on empty input with the result `value`.*/ - def success[T](value: T): Parser[T] = new ValidParser[T] { - override def result = Some(value) - def resultEmpty = Value(value) - def derive(c: Char) = Parser.failure("Expected end of input.") - def completions(level: Int) = Completions.empty - override def toString = "success(" + value + ")" - } - - /** Presents a Char range as a Parser. A single Char is parsed only if it is in the given range.*/ - implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] = - charClass(r contains _).examples(r.map(_.toString): _*) - - /** Defines a Parser that parses a single character only if it is contained in `legal`.*/ - def chars(legal: String): Parser[Char] = - { - val set = legal.toSet - charClass(set, "character in '" + legal + "'") examples (set.map(_.toString)) - } - - /** - * Defines a Parser that parses a single character only if the predicate `f` returns true for that character. - * If this parser fails, `label` is used as the failure message. - */ - def charClass(f: Char => Boolean, label: String = ""): Parser[Char] = new CharacterClass(f, label) - - /** Presents a single Char `ch` as a Parser that only parses that exact character. */ - implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] { - def result = None - def resultEmpty = mkFailure("Expected '" + ch + "'") - def derive(c: Char) = if (c == ch) success(ch) else new Invalid(resultEmpty) - def completions(level: Int) = Completions.single(Completion.suggestStrict(ch.toString)) - override def toString = "'" + ch + "'" - } - /** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/ - implicit def literal(s: String): Parser[String] = stringLiteral(s, 0) - - /** See [[unapply]]. */ - object ~ { - /** Convenience for destructuring a tuple that mirrors the `~` combinator.*/ - def unapply[A, B](t: (A, B)): Some[(A, B)] = Some(t) - } - - /** Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If unsuccessful, an error message is provided in `Left`.*/ - def parse[T](str: String, parser: Parser[T]): Either[String, T] = - Parser.result(parser, str).left.map { failures => - val (msgs, pos) = failures() - ProcessError(str, msgs, pos) - } - - /** - * Convenience method to use when developing a parser. - * `parser` is applied to the input `str`. - * If `completions` is true, the available completions for the input are displayed. - * Otherwise, the result of parsing is printed using the result's `toString` method. - * If parsing fails, the error message is displayed. - * - * See also [[sampleParse]] and [[sampleCompletions]]. - */ - def sample(str: String, parser: Parser[_], completions: Boolean = false): Unit = - if (completions) sampleCompletions(str, parser) else sampleParse(str, parser) - - /** - * Convenience method to use when developing a parser. - * `parser` is applied to the input `str` and the result of parsing is printed using the result's `toString` method. - * If parsing fails, the error message is displayed. - */ - def sampleParse(str: String, parser: Parser[_]): Unit = - parse(str, parser) match { - case Left(msg) => println(msg) - case Right(v) => println(v) - } - - /** - * Convenience method to use when developing a parser. - * `parser` is applied to the input `str` and the available completions are displayed on separate lines. - * If parsing fails, the error message is displayed. - */ - def sampleCompletions(str: String, parser: Parser[_], level: Int = 1): Unit = - Parser.completions(parser, str, level).get foreach println - - // intended to be temporary pending proper error feedback - def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = - { - def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] = - a match { - case Invalid(f) => Left(() => (f.errors, i)) - case _ => - val ci = i + 1 - if (ci >= s.length) - a.resultEmpty.toEither.left.map { msgs0 => - () => - val msgs = msgs0() - val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs - (nonEmpty, ci) - } - else - loop(ci, a derive s(ci)) - } - loop(-1, p) - } - - /** Applies parser `p` to input `s`. */ - def apply[T](p: Parser[T])(s: String): Parser[T] = - (p /: s)(derive1) - - /** Applies parser `p` to a single character of input. */ - def derive1[T](p: Parser[T], c: Char): Parser[T] = - if (p.valid) p.derive(c) else p - - /** - * Applies parser `p` to input `s` and returns the completions at verbosity `level`. - * The interpretation of `level` is up to parser definitions, but 0 is the default by convention, - * with increasing positive numbers corresponding to increasing verbosity. Typically no more than - * a few levels are defined. - */ - def completions(p: Parser[_], s: String, level: Int): Completions = - // The x Completions.empty removes any trailing token completions where append.isEmpty - apply(p)(s).completions(level) x Completions.empty - - def examples[A](a: Parser[A], completions: Set[String], check: Boolean = false): Parser[A] = - examples(a, new FixedSetExamples(completions), completions.size, check) - - /** - * @param a the parser to decorate with a source of examples. All validation and parsing is delegated to this parser, - * only [[Parser.completions]] is modified. - * @param completions the source of examples when displaying completions to the user. - * @param maxNumberOfExamples limits the number of examples that the source of examples should return. This can - * prevent lengthy pauses and avoids bad interactive user experience. - * @param removeInvalidExamples indicates whether completion examples should be checked for validity (against the given parser). An - * exception is thrown if the example source contains no valid completion suggestions. - * @tparam A the type of values that are returned by the parser. - * @return - */ - def examples[A](a: Parser[A], completions: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = - if (a.valid) { - a.result match { - case Some(av) => success(av) - case None => - new ParserWithExamples(a, completions, maxNumberOfExamples, removeInvalidExamples) - } - } else a - - def matched(t: Parser[_], seen: Vector[Char] = Vector.empty, partial: Boolean = false): Parser[String] = - t match { - case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i - case _ => - if (t.result.isEmpty) - new MatchedString(t, seen, partial) - else - success(seen.mkString) - } - - /** - * Establishes delegate parser `t` as a single token of tab completion. - * When tab completion of part of this token is requested, the completions provided by the delegate `t` or a later derivative are appended to - * the prefix String already seen by this parser. - */ - def token[T](t: Parser[T]): Parser[T] = token(t, TokenCompletions.default) - - /** - * Establishes delegate parser `t` as a single token of tab completion. - * When tab completion of part of this token is requested, no completions are returned if `hide` returns true for the current tab completion level. - * Otherwise, the completions provided by the delegate `t` or a later derivative are appended to the prefix String already seen by this parser. - */ - def token[T](t: Parser[T], hide: Int => Boolean): Parser[T] = token(t, TokenCompletions.default.hideWhen(hide)) - - /** - * Establishes delegate parser `t` as a single token of tab completion. - * When tab completion of part of this token is requested, `description` is displayed for suggestions and no completions are ever performed. - */ - def token[T](t: Parser[T], description: String): Parser[T] = token(t, TokenCompletions.displayOnly(description)) - - /** - * Establishes delegate parser `t` as a single token of tab completion. - * When tab completion of part of this token is requested, `display` is used as the printed suggestion, but the completions from the delegate - * parser `t` are used to complete if unambiguous. - */ - def tokenDisplay[T](t: Parser[T], display: String): Parser[T] = - token(t, TokenCompletions.overrideDisplay(display)) - - def token[T](t: Parser[T], complete: TokenCompletions): Parser[T] = - mkToken(t, "", complete) - - @deprecated("Use a different `token` overload.", "0.12.1") - def token[T](t: Parser[T], seen: String, track: Boolean, hide: Int => Boolean): Parser[T] = - { - val base = if (track) TokenCompletions.default else TokenCompletions.displayOnly(seen) - token(t, base.hideWhen(hide)) - } - - private[sbt] def mkToken[T](t: Parser[T], seen: String, complete: TokenCompletions): Parser[T] = - if (t.valid && !t.isTokenStart) - if (t.result.isEmpty) new TokenStart(t, seen, complete) else t - else - t - - def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] = (a, b) match { - case (Invalid(af), Invalid(bf)) => Invalid(af ++ bf) - case (Invalid(_), bv) => bv - case (av, Invalid(_)) => av - case (av, bv) => new HomParser(a, b) - } - - @deprecated("Explicitly specify the failure message.", "0.12.2") - def not(p: Parser[_]): Parser[Unit] = not(p, "Excluded.") - - def not(p: Parser[_], failMessage: String): Parser[Unit] = p.result match { - case None => new Not(p, failMessage) - case Some(_) => failure(failMessage) - } - - def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _) - def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil) - def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = - { - val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors); case ok => Right(ok) } - def combinedErrors = errors ++ newErrors.flatten - if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) - } - - def stringLiteral(s: String, start: Int): Parser[String] = - { - val len = s.length - if (len == 0) sys.error("String literal cannot be empty") else if (start >= len) success(s) else new StringLiteral(s, start) - } -} -sealed trait ValidParser[T] extends Parser[T] { - final def valid = true - final def failure = None - final def ifValid[S](p: => Parser[S]): Parser[S] = p -} -private final case class Invalid(fail: Failure) extends Parser[Nothing] { - def failure = Some(fail) - def result = None - def resultEmpty = fail - def derive(c: Char) = sys.error("Invalid.") - def completions(level: Int) = Completions.nil - override def toString = fail.errors.mkString("; ") - def valid = false - def ifValid[S](p: => Parser[S]): Parser[S] = this -} - -private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing] { - def result = None - def resultEmpty = fail - def derive(c: Char) = Invalid(fail) - def completions(level: Int) = Completions.nil - override def toString = fail.errors.mkString("; ") -} - -private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] { - def result = try { a.result } catch { case e: Exception => None } - def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) } - def derive(c: Char) = try { trapAndFail(a derive c) } catch { case e: Exception => Invalid(fail(e)) } - def completions(level: Int) = try { a.completions(level) } catch { case e: Exception => Completions.nil } - override def toString = "trap(" + a + ")" - override def isTokenStart = a.isTokenStart - private[this] def fail(e: Exception): Failure = mkFailure(e.toString) -} - -private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] { - def result = a.result - def resultEmpty = a.resultEmpty match { case f: Failure => mkFailure(message); case v: Value[A] => v } - def derive(c: Char) = onFailure(a derive c, message) - def completions(level: Int) = a.completions(level) - override def toString = "(" + a + " !!! \"" + message + "\" )" - override def isTokenStart = a.isTokenStart -} -private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)] { - lazy val result = tuple(a.result, b.result) - lazy val resultEmpty = a.resultEmpty seq b.resultEmpty - def derive(c: Char) = - { - val common = a.derive(c) ~ b - a.resultEmpty match { - case Value(av) => common | b.derive(c).map(br => (av, br)) - case _: Failure => common - } - } - def completions(level: Int) = a.completions(level) x b.completions(level) - override def toString = "(" + a + " ~ " + b + ")" -} - -private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser[A] { - lazy val result = tuple(a.result, b.result) map (_._1) - def derive(c: Char) = (a derive c) | (b derive c) - lazy val resultEmpty = a.resultEmpty or b.resultEmpty - def completions(level: Int) = a.completions(level) ++ b.completions(level) - override def toString = "(" + a + " | " + b + ")" -} -private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A, B]] { - lazy val result = tuple(a.result, b.result) map { case (a, b) => Left(a) } - def derive(c: Char) = (a derive c) || (b derive c) - lazy val resultEmpty = a.resultEmpty either b.resultEmpty - def completions(level: Int) = a.completions(level) ++ b.completions(level) - override def toString = "(" + a + " || " + b + ")" -} -private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] { - assert(a.nonEmpty) - lazy val resultEmpty: Result[Seq[T]] = - { - val res = a.map(_.resultEmpty) - val (failures, values) = separate(res)(_.toEither) - // if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - } - def result = { - val success = a.flatMap(_.result) - if (success.length == a.length) Some(success) else None - } - def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _) - def derive(c: Char) = seq0(a.map(_ derive c), errors) - override def toString = "seq(" + a + ")" -} - -private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B] { - lazy val result = a.result flatMap { av => f(av).result } - lazy val resultEmpty = a.resultEmpty flatMap { av => f(av).resultEmpty } - def completions(level: Int) = - a.completions(level) flatMap { c => - apply(a)(c.append).resultEmpty match { - case _: Failure => Completions.strict(Set.empty + c) - case Value(av) => c x f(av).completions(level) - } - } - - def derive(c: Char) = - { - val common = a derive c flatMap f - a.resultEmpty match { - case Value(av) => common | derive1(f(av), c) - case _: Failure => common - } - } - override def isTokenStart = a.isTokenStart - override def toString = "bind(" + a + ")" -} -private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] { - lazy val result = a.result map f - lazy val resultEmpty = a.resultEmpty map f - def derive(c: Char) = (a derive c) map f - def completions(level: Int) = a.completions(level) - override def isTokenStart = a.isTokenStart - override def toString = "map(" + a + ")" -} -private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) extends ValidParser[T] { - def filterResult(r: Result[T]) = r.filter(f, msg(seen)) - lazy val result = p.result filter f - lazy val resultEmpty = filterResult(p.resultEmpty) - def derive(c: Char) = filterParser(p derive c, f, seen + c, msg) - def completions(level: Int) = p.completions(level) filterS { s => filterResult(apply(p)(s).resultEmpty).isValid } - override def toString = "filter(" + p + ")" - override def isTokenStart = p.isTokenStart -} -private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) extends ValidParser[String] { - lazy val seen = seenV.mkString - def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial) - def completions(level: Int) = delegate.completions(level) - def result = if (delegate.result.isDefined) Some(seen) else None - def resultEmpty = delegate.resultEmpty match { case f: Failure if !partial => f; case _ => Value(seen) } - override def isTokenStart = delegate.isTokenStart - override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")" -} -private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) extends ValidParser[T] { - def derive(c: Char) = mkToken(delegate derive c, seen + c, complete) - def completions(level: Int) = complete match { - case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level)) - case fc: TokenCompletions.Fixed => fc.completions(seen, level) - } - def result = delegate.result - def resultEmpty = delegate.resultEmpty - override def isTokenStart = true - override def toString = "token('" + complete + ", " + delegate + ")" -} -private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T] { - lazy val result = tuple(a.result, b.result) map { _._1 } - def derive(c: Char) = (a derive c) & (b derive c) - def completions(level: Int) = a.completions(level).filterS(s => apply(b)(s).resultEmpty.isValid) - lazy val resultEmpty = a.resultEmpty && b.resultEmpty - override def toString = "(%s) && (%s)".format(a, b) -} - -private final class Not(delegate: Parser[_], failMessage: String) extends ValidParser[Unit] { - def derive(c: Char) = if (delegate.valid) not(delegate derive c, failMessage) else this - def completions(level: Int) = Completions.empty - def result = None - lazy val resultEmpty = delegate.resultEmpty match { - case f: Failure => Value(()) - case v: Value[_] => mkFailure(failMessage) - } - override def toString = " -(%s)".format(delegate) -} - -/** - * This class wraps an existing parser (the delegate), and replaces the delegate's completions with examples from - * the given example source. - * - * This class asks the example source for a limited amount of examples (to prevent lengthy and expensive - * computations and large amounts of allocated data). It then passes these examples on to the UI. - * - * @param delegate the parser to decorate with completion examples (i.e., completion of user input). - * @param exampleSource the source from which this class will take examples (potentially filter them with the delegate - * parser), and pass them to the UI. - * @param maxNumberOfExamples the maximum number of completions to read from the example source and pass to the UI. This - * limit prevents lengthy example generation and allocation of large amounts of memory. - * @param removeInvalidExamples indicates whether to remove examples that are deemed invalid by the delegate parser. - * @tparam T the type of value produced by the parser. - */ -private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean) extends ValidParser[T] { - def derive(c: Char) = - examples(delegate derive c, exampleSource.withAddedPrefix(c.toString), maxNumberOfExamples, removeInvalidExamples) - - def result = delegate.result - - lazy val resultEmpty = delegate.resultEmpty - - def completions(level: Int) = { - if (exampleSource().isEmpty) - if (resultEmpty.isValid) Completions.nil else Completions.empty - else { - val examplesBasedOnTheResult = filteredExamples.take(maxNumberOfExamples).toSet - Completions(examplesBasedOnTheResult.map(ex => Completion.suggestion(ex))) - } - } - - override def toString = "examples(" + delegate + ", " + exampleSource().take(2).toList + ")" - - private def filteredExamples: Iterable[String] = { - if (removeInvalidExamples) - exampleSource().filter(isExampleValid) - else - exampleSource() - } - - private def isExampleValid(example: String): Boolean = { - apply(delegate)(example).resultEmpty.isValid - } -} -private final class StringLiteral(str: String, start: Int) extends ValidParser[String] { - assert(0 <= start && start < str.length) - def failMsg = "Expected '" + str + "'" - def resultEmpty = mkFailure(failMsg) - def result = None - def derive(c: Char) = if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty) - def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start))) - override def toString = '"' + str + '"' -} -private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] { - def result = None - def resultEmpty = mkFailure("Expected " + label) - def derive(c: Char) = if (f(c)) success(c) else Invalid(resultEmpty) - def completions(level: Int) = Completions.empty - override def toString = "class(" + label + ")" -} -private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]] { - def result = delegate.result map some.fn - def resultEmpty = Value(None) - def derive(c: Char) = (delegate derive c).map(some.fn) - def completions(level: Int) = Completion.empty +: delegate.completions(level) - override def toString = delegate.toString + "?" -} -private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, accumulatedReverse: List[T]) extends ValidParser[Seq[T]] { - assume(0 <= min, "Minimum occurences must be non-negative") - assume(max >= min, "Minimum occurences must be less than the maximum occurences") - - def derive(c: Char) = - partial match { - case Some(part) => - val partD = repeat(Some(part derive c), repeated, min, max, accumulatedReverse) - part.resultEmpty match { - case Value(pv) => partD | repeatDerive(c, pv :: accumulatedReverse) - case _: Failure => partD - } - case None => repeatDerive(c, accumulatedReverse) - } - - def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, (min - 1) max 0, max.decrement, accRev) - - def completions(level: Int) = - { - def pow(comp: Completions, exp: Completions, n: Int): Completions = - if (n == 1) comp else pow(comp x exp, exp, n - 1) - - val repC = repeated.completions(level) - val fin = if (min == 0) Completion.empty +: repC else pow(repC, repC, min) - partial match { - case Some(p) => p.completions(level) x fin - case None => fin - } - } - def result = None - lazy val resultEmpty: Result[Seq[T]] = - { - val partialAccumulatedOption = - partial match { - case None => Value(accumulatedReverse) - case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse) - } - (partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _) - } - private def repeatedParseEmpty: Result[List[T]] = - { - if (min == 0) - Value(Nil) - else - // forced determinism - for (value <- repeated.resultEmpty) yield makeList(min, value) - } - override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")" -} diff --git a/util/complete/src/main/scala/sbt/complete/Parsers.scala b/util/complete/src/main/scala/sbt/complete/Parsers.scala deleted file mode 100644 index 3183929e8..000000000 --- a/util/complete/src/main/scala/sbt/complete/Parsers.scala +++ /dev/null @@ -1,268 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt.complete - -import Parser._ -import java.io.File -import java.net.URI -import java.lang.Character.{ getType, MATH_SYMBOL, OTHER_SYMBOL, DASH_PUNCTUATION, OTHER_PUNCTUATION, MODIFIER_SYMBOL, CURRENCY_SYMBOL } - -/** Provides standard implementations of commonly useful [[Parser]]s. */ -trait Parsers { - /** Matches the end of input, providing no useful result on success. */ - lazy val EOF = not(any) - - /** Parses any single character and provides that character as the result. */ - lazy val any: Parser[Char] = charClass(_ => true, "any character") - - /** Set that contains each digit in a String representation.*/ - lazy val DigitSet = Set("0", "1", "2", "3", "4", "5", "6", "7", "8", "9") - - /** Parses any single digit and provides that digit as a Char as the result.*/ - lazy val Digit = charClass(_.isDigit, "digit") examples DigitSet - - /** Set containing Chars for hexadecimal digits 0-9 and A-F (but not a-f). */ - lazy val HexDigitSet = Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') - - /** Parses a single hexadecimal digit (0-9, a-f, A-F). */ - lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map(_.toString) - - /** Parses a single letter, according to Char.isLetter, into a Char. */ - lazy val Letter = charClass(_.isLetter, "letter") - - /** Parses the first Char in an sbt identifier, which must be a [[Letter]].*/ - def IDStart = Letter - - /** Parses an identifier Char other than the first character. This includes letters, digits, dash `-`, and underscore `_`.*/ - lazy val IDChar = charClass(isIDChar, "ID character") - - /** Parses an identifier String, which must start with [[IDStart]] and contain zero or more [[IDChar]]s after that. */ - lazy val ID = identifier(IDStart, IDChar) - - /** Parses a single operator Char, as allowed by [[isOpChar]]. */ - lazy val OpChar = charClass(isOpChar, "symbol") - - /** Parses a non-empty operator String, which consists only of characters allowed by [[OpChar]]. */ - lazy val Op = OpChar.+.string - - /** Parses either an operator String defined by [[Op]] or a non-symbolic identifier defined by [[ID]]. */ - lazy val OpOrID = ID | Op - - /** Parses a single, non-symbolic Scala identifier Char. Valid characters are letters, digits, and the underscore character `_`. */ - lazy val ScalaIDChar = charClass(isScalaIDChar, "Scala identifier character") - - /** Parses a non-symbolic Scala-like identifier. The identifier must start with [[IDStart]] and contain zero or more [[ScalaIDChar]]s after that.*/ - lazy val ScalaID = identifier(IDStart, ScalaIDChar) - - /** Parses a String that starts with `start` and is followed by zero or more characters parsed by `rep`.*/ - def identifier(start: Parser[Char], rep: Parser[Char]): Parser[String] = - start ~ rep.* map { case x ~ xs => (x +: xs).mkString } - - def opOrIDSpaced(s: String): Parser[Char] = - if (DefaultParsers.matches(ID, s)) - OpChar | SpaceClass - else if (DefaultParsers.matches(Op, s)) - IDChar | SpaceClass - else - any - - /** Returns true if `c` an operator character. */ - def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c)) - def isOpType(cat: Int) = cat match { case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | CURRENCY_SYMBOL => true; case _ => false } - /** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */ - def isIDChar(c: Char) = isScalaIDChar(c) || c == '-' - - /** Returns true if `c` is a letter, digit, or an underscore `_`. */ - def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_' - - def isDelimiter(c: Char) = c match { case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false } - - /** Matches a single character that is not a whitespace character. */ - lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character") - - /** Matches a single whitespace character, as determined by Char.isWhitespace.*/ - lazy val SpaceClass = charClass(_.isWhitespace, "whitespace character") - - /** Matches a non-empty String consisting of non-whitespace characters. */ - lazy val NotSpace = NotSpaceClass.+.string - - /** Matches a possibly empty String consisting of non-whitespace characters. */ - lazy val OptNotSpace = NotSpaceClass.*.string - - /** - * Matches a non-empty String consisting of whitespace characters. - * The suggested tab completion is a single, constant space character. - */ - lazy val Space = SpaceClass.+.examples(" ") - - /** - * Matches a possibly empty String consisting of whitespace characters. - * The suggested tab completion is a single, constant space character. - */ - lazy val OptSpace = SpaceClass.*.examples(" ") - - /** Parses a non-empty String that contains only valid URI characters, as defined by [[URIChar]].*/ - lazy val URIClass = URIChar.+.string !!! "Invalid URI" - - /** Triple-quotes, as used for verbatim quoting.*/ - lazy val VerbatimDQuotes = "\"\"\"" - - /** Double quote character. */ - lazy val DQuoteChar = '\"' - - /** Backslash character. */ - lazy val BackslashChar = '\\' - - /** Matches a single double quote. */ - lazy val DQuoteClass = charClass(_ == DQuoteChar, "double-quote character") - - /** Matches any character except a double quote or whitespace. */ - lazy val NotDQuoteSpaceClass = - charClass({ c: Char => (c != DQuoteChar) && !c.isWhitespace }, "non-double-quote-space character") - - /** Matches any character except a double quote or backslash. */ - lazy val NotDQuoteBackslashClass = - charClass({ c: Char => (c != DQuoteChar) && (c != BackslashChar) }, "non-double-quote-backslash character") - - /** Matches a single character that is valid somewhere in a URI. */ - lazy val URIChar = charClass(alphanum) | chars("_-!.~'()*,;:$&+=?/[]@%#") - - /** Returns true if `c` is an ASCII letter or digit. */ - def alphanum(c: Char) = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') - - /** - * @param base the directory used for completion proposals (when the user presses the TAB key). Only paths under this - * directory will be proposed. - * @return the file that was parsed from the input string. The returned path may or may not exist. - */ - def fileParser(base: File): Parser[File] = - OptSpace ~> StringBasic - .examples(new FileExamples(base)) - .map(new File(_)) - - /** Parses a port number. Currently, this accepts any integer and presents a tab completion suggestion of ``. */ - lazy val Port = token(IntBasic, "") - - /** Parses a signed integer. */ - lazy val IntBasic = mapOrFail('-'.? ~ Digit.+)(Function.tupled(toInt)) - - /** Parses an unsigned integer. */ - lazy val NatBasic = mapOrFail(Digit.+)(_.mkString.toInt) - - private[this] def toInt(neg: Option[Char], digits: Seq[Char]): Int = - (neg.toSeq ++ digits).mkString.toInt - - /** Parses the lower-case values `true` and `false` into their respesct Boolean values. */ - lazy val Bool = ("true" ^^^ true) | ("false" ^^^ false) - - /** - * Parses a potentially quoted String value. The value may be verbatim quoted ([[StringVerbatim]]), - * quoted with interpreted escapes ([[StringEscapable]]), or unquoted ([[NotQuoted]]). - */ - lazy val StringBasic = StringVerbatim | StringEscapable | NotQuoted - - /** - * Parses a verbatim quoted String value, discarding the quotes in the result. This kind of quoted text starts with triple quotes `"""` - * and ends at the next triple quotes and may contain any character in between. - */ - lazy val StringVerbatim: Parser[String] = VerbatimDQuotes ~> - any.+.string.filter(!_.contains(VerbatimDQuotes), _ => "Invalid verbatim string") <~ - VerbatimDQuotes - - /** - * Parses a string value, interpreting escapes and discarding the surrounding quotes in the result. - * See [[EscapeSequence]] for supported escapes. - */ - lazy val StringEscapable: Parser[String] = - (DQuoteChar ~> (NotDQuoteBackslashClass | EscapeSequence).+.string <~ DQuoteChar | - (DQuoteChar ~ DQuoteChar) ^^^ "") - - /** - * Parses a single escape sequence into the represented Char. - * Escapes start with a backslash and are followed by `u` for a [[UnicodeEscape]] or by `b`, `t`, `n`, `f`, `r`, `"`, `'`, `\` for standard escapes. - */ - lazy val EscapeSequence: Parser[Char] = - BackslashChar ~> ('b' ^^^ '\b' | 't' ^^^ '\t' | 'n' ^^^ '\n' | 'f' ^^^ '\f' | 'r' ^^^ '\r' | - '\"' ^^^ '\"' | '\'' ^^^ '\'' | '\\' ^^^ '\\' | UnicodeEscape) - - /** - * Parses a single unicode escape sequence into the represented Char. - * A unicode escape begins with a backslash, followed by a `u` and 4 hexadecimal digits representing the unicode value. - */ - lazy val UnicodeEscape: Parser[Char] = - ("u" ~> repeat(HexDigit, 4, 4)) map { seq => Integer.parseInt(seq.mkString, 16).toChar } - - /** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/ - lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s } - - /** - * Applies `rep` zero or more times, separated by `sep`. - * The result is the (possibly empty) sequence of results from the multiple `rep` applications. The `sep` results are discarded. - */ - def repsep[T](rep: Parser[T], sep: Parser[_]): Parser[Seq[T]] = - rep1sep(rep, sep) ?? Nil - - /** - * Applies `rep` one or more times, separated by `sep`. - * The result is the non-empty sequence of results from the multiple `rep` applications. The `sep` results are discarded. - */ - def rep1sep[T](rep: Parser[T], sep: Parser[_]): Parser[Seq[T]] = - (rep ~ (sep ~> rep).*).map { case (x ~ xs) => x +: xs } - - /** Wraps the result of `p` in `Some`.*/ - def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => Some(v) } - - /** - * Applies `f` to the result of `p`, transforming any exception when evaluating - * `f` into a parse failure with the exception `toString` as the message. - */ - def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] = - p flatMap { s => try { success(f(s)) } catch { case e: Exception => failure(e.toString) } } - - /** - * Parses a space-delimited, possibly empty sequence of arguments. - * The arguments may use quotes and escapes according to [[StringBasic]]. - */ - def spaceDelimited(display: String): Parser[Seq[String]] = (token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.* - - /** Applies `p` and uses `true` as the result if it succeeds and turns failure into a result of `false`. */ - def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false - - /** - * Defines a sequence parser where the parser used for each part depends on the previously parsed values. - * `p` is applied to the (possibly empty) sequence of already parsed values to obtain the next parser to use. - * The parsers obtained in this way are separated by `sep`, whose result is discarded and only the sequence - * of values from the parsers returned by `p` is used for the result. - */ - def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = - { - def loop(acc: Seq[A]): Parser[Seq[A]] = { - val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) } - next ?? acc - } - p(Vector()) flatMap { first => loop(Seq(first)) } - } - - /** Applies String.trim to the result of `p`. */ - def trimmed(p: Parser[String]) = p map { _.trim } - - /** Parses a URI that is valid according to the single argument java.net.URI constructor. */ - lazy val basicUri = mapOrFail(URIClass)(uri => new URI(uri)) - - /** Parses a URI that is valid according to the single argument java.net.URI constructor, using `ex` as tab completion examples. */ - def Uri(ex: Set[URI]) = basicUri examples (ex.map(_.toString)) -} - -/** Provides standard [[Parser]] implementations. */ -object Parsers extends Parsers - -/** Provides common [[Parser]] implementations and helper methods.*/ -object DefaultParsers extends Parsers with ParserMain { - /** Applies parser `p` to input `s` and returns `true` if the parse was successful. */ - def matches(p: Parser[_], s: String): Boolean = - apply(p)(s).resultEmpty.isValid - - /** Returns `true` if `s` parses successfully according to [[ID]].*/ - def validID(s: String): Boolean = matches(ID, s) -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/ProcessError.scala b/util/complete/src/main/scala/sbt/complete/ProcessError.scala deleted file mode 100644 index 7e6c9794e..000000000 --- a/util/complete/src/main/scala/sbt/complete/ProcessError.scala +++ /dev/null @@ -1,29 +0,0 @@ -package sbt.complete - -object ProcessError { - def apply(command: String, msgs: Seq[String], index: Int): String = - { - val (line, modIndex) = extractLine(command, index) - val point = pointerSpace(command, modIndex) - msgs.mkString("\n") + "\n" + line + "\n" + point + "^" - } - def extractLine(s: String, i: Int): (String, Int) = - { - val notNewline = (c: Char) => c != '\n' && c != '\r' - val left = takeRightWhile(s.substring(0, i))(notNewline) - val right = s substring i takeWhile notNewline - (left + right, left.length) - } - def takeRightWhile(s: String)(pred: Char => Boolean): String = - { - def loop(i: Int): String = - if (i < 0) - s - else if (pred(s(i))) - loop(i - 1) - else - s.substring(i + 1) - loop(s.length - 1) - } - def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString; -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala b/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala deleted file mode 100644 index 96e70d2f1..000000000 --- a/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala +++ /dev/null @@ -1,37 +0,0 @@ -package sbt.complete - -import Completion.{ displayStrict, token => ctoken, tokenDisplay } - -sealed trait TokenCompletions { - def hideWhen(f: Int => Boolean): TokenCompletions -} -object TokenCompletions { - private[sbt] abstract class Delegating extends TokenCompletions { outer => - def completions(seen: String, level: Int, delegate: Completions): Completions - final def hideWhen(hide: Int => Boolean): TokenCompletions = new Delegating { - def completions(seen: String, level: Int, delegate: Completions): Completions = - if (hide(level)) Completions.nil else outer.completions(seen, level, delegate) - } - } - private[sbt] abstract class Fixed extends TokenCompletions { outer => - def completions(seen: String, level: Int): Completions - final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed { - def completions(seen: String, level: Int) = - if (hide(level)) Completions.nil else outer.completions(seen, level) - } - } - - val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append)) - - def displayOnly(msg: String): TokenCompletions = new Fixed { - def completions(seen: String, level: Int) = Completions.single(displayStrict(msg)) - } - def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) - - def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed { - def completions(seen: String, level: Int) = f(seen, level) - } - def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = new Delegating { - def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c))) - } -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/TypeString.scala b/util/complete/src/main/scala/sbt/complete/TypeString.scala deleted file mode 100644 index 6bf89ac05..000000000 --- a/util/complete/src/main/scala/sbt/complete/TypeString.scala +++ /dev/null @@ -1,79 +0,0 @@ -package sbt.complete - -import DefaultParsers._ -import TypeString._ - -/** - * Basic representation of types parsed from Manifest.toString. - * This can only represent the structure of parameterized types. - * All other types are represented by a TypeString with an empty `args`. - */ -private[sbt] final class TypeString(val base: String, val args: List[TypeString]) { - override def toString = - if (base.startsWith(FunctionName)) - args.dropRight(1).mkString("(", ",", ")") + " => " + args.last - else if (base.startsWith(TupleName)) - args.mkString("(", ",", ")") - else - cleanupTypeName(base) + (if (args.isEmpty) "" else args.mkString("[", ",", "]")) -} - -private[sbt] object TypeString { - /** Makes the string representation of a type as returned by Manifest.toString more readable.*/ - def cleanup(typeString: String): String = - parse(typeString, typeStringParser) match { - case Right(ts) => ts.toString - case Left(err) => typeString - } - - /** - * Makes a fully qualified type name provided by Manifest.toString more readable. - * The argument should be just a name (like scala.Tuple2) and not a full type (like scala.Tuple2[Int,Boolean]) - */ - def cleanupTypeName(base: String): String = - dropPrefix(base).replace('$', '.') - - /** - * Removes prefixes from a fully qualified type name that are unnecessary in the presence of standard imports for an sbt setting. - * This does not use the compiler and is therefore a conservative approximation. - */ - def dropPrefix(base: String): String = - if (base.startsWith(SbtPrefix)) - base.substring(SbtPrefix.length) - else if (base.startsWith(CollectionPrefix)) { - val simple = base.substring(CollectionPrefix.length) - if (ShortenCollection(simple)) simple else base - } else if (base.startsWith(ScalaPrefix)) - base.substring(ScalaPrefix.length) - else if (base.startsWith(JavaPrefix)) - base.substring(JavaPrefix.length) - else - TypeMap.getOrElse(base, base) - - final val CollectionPrefix = "scala.collection." - final val FunctionName = "scala.Function" - final val TupleName = "scala.Tuple" - final val SbtPrefix = "sbt." - final val ScalaPrefix = "scala." - final val JavaPrefix = "java.lang." - /* scala.collection.X -> X */ - val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable") - val TypeMap = Map( - "java.io.File" -> "File", - "java.net.URL" -> "URL", - "java.net.URI" -> "URI" - ) - - /** - * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. - * This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types. - */ - lazy val typeStringParser: Parser[TypeString] = - { - def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' - lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character")) - lazy val tpe: Parser[TypeString] = - for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) yield new TypeString(id, args.toList.flatten) - tpe - } -} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/UpperBound.scala b/util/complete/src/main/scala/sbt/complete/UpperBound.scala deleted file mode 100644 index 66a32e1a2..000000000 --- a/util/complete/src/main/scala/sbt/complete/UpperBound.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008,2010 Mark Harrah - */ -package sbt.complete - -sealed trait UpperBound { - /** True if and only if the given value meets this bound.*/ - def >=(min: Int): Boolean - /** True if and only if this bound is one.*/ - def isOne: Boolean - /** True if and only if this bound is zero.*/ - def isZero: Boolean - /** - * If this bound is zero or Infinite, `decrement` returns this bound. - * Otherwise, this bound is finite and greater than zero and `decrement` returns the bound that is one less than this bound. - */ - def decrement: UpperBound - /** True if and only if this is unbounded.*/ - def isInfinite: Boolean -} -/** Represents unbounded. */ -case object Infinite extends UpperBound { - /** All finite numbers meet this bound. */ - def >=(min: Int) = true - def isOne = false - def isZero = false - def decrement = this - def isInfinite = true - override def toString = "Infinity" -} -/** - * Represents a finite upper bound. The maximum allowed value is 'value', inclusive. - * It must positive. - */ -final case class Finite(value: Int) extends UpperBound { - assume(value >= 0, "Maximum occurences must be nonnegative.") - - def >=(min: Int) = value >= min - def isOne = value == 1 - def isZero = value == 0 - def decrement = Finite((value - 1) max 0) - def isInfinite = false - override def toString = value.toString -} -object UpperBound { - implicit def intToFinite(i: Int): Finite = Finite(i) -} \ No newline at end of file diff --git a/util/complete/src/test/scala/ParserTest.scala b/util/complete/src/test/scala/ParserTest.scala deleted file mode 100644 index f02431e53..000000000 --- a/util/complete/src/test/scala/ParserTest.scala +++ /dev/null @@ -1,148 +0,0 @@ -package sbt.complete - -object JLineTest { - import DefaultParsers._ - - val one = "blue" | "green" | "black" - val two = token("color" ~> Space) ~> token(one) - val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black")) - val four = token("color" ~> Space) ~> token(ID, "") - - val num = token(NatBasic) - val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap { - case a ~ "+" ~ b => token((a + b).toString) - case a ~ "-" ~ b => token((a - b).toString) - } - - val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five) - def main(args: Array[String]): Unit = { - import jline.TerminalFactory - import jline.console.ConsoleReader - val reader = new ConsoleReader() - TerminalFactory.get.init - - val parser = parsers(args(0)) - JLineCompletion.installCustomCompletor(reader, parser) - def loop(): Unit = { - val line = reader.readLine("> ") - if (line ne null) { - println("Result: " + apply(parser)(line).resultEmpty) - loop() - } - } - loop() - } -} - -import Parser._ -import org.scalacheck._ - -object ParserTest extends Properties("Completing Parser") { - import Parsers._ - import DefaultParsers.matches - - val nested = (token("a1") ~ token("b2")) ~ "c3" - val nestedDisplay = (token("a1", "") ~ token("b2", "")) ~ "c3" - - val spacePort = token(Space) ~> Port - - def p[T](f: T): T = { println(f); f } - - def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) = - (("token '" + in + "'") |: checkOne(in, nested, expect)) && - (("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay)) - - def checkOne(in: String, parser: Parser[_], expect: Completion): Prop = - completions(parser, in, 1) == Completions.single(expect) - - def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = - { - val cs = completions(parser, in, 1) - ("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop) - } - - def checkInvalid(in: String) = - (("token '" + in + "'") |: checkInv(in, nested)) && - (("display '" + in + "'") |: checkInv(in, nestedDisplay)) - - def checkInv(in: String, parser: Parser[_]): Prop = - { - val cs = completions(parser, in, 1) - ("completions: " + cs) |: (cs == Completions.nil: Prop) - } - - property("nested tokens a") = checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("")) - property("nested tokens a1") = checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("")) - property("nested tokens a inv") = checkInvalid("b") - property("nested tokens b") = checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("")) - property("nested tokens b2") = checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("")) - property("nested tokens b inv") = checkInvalid("a1a") - property("nested tokens c") = checkSingle("a1b2", Completion.suggestion("c3"))() - property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestion("3"))() - property("nested tokens c inv") = checkInvalid("a1b2a") - - property("suggest space") = checkOne("", spacePort, Completion.token("", " ")) - property("suggest port") = checkOne(" ", spacePort, Completion.displayOnly("")) - property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestion("")) - property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestion("")) - property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion("")) - property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion("")) - - val colors = Set("blue", "green", "red") - val base = (seen: Seq[String]) => token(ID examples (colors -- seen)) - val sep = token(Space) - val repeat = repeatDep(base, sep) - def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.token("", s) }) - - property("repeatDep no suggestions for bad input") = checkInv(".", repeat) - property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) - property("repeatDep suggest remaining two") = { - val first = colors.toSeq.head - checkAll(first + " ", repeat, completionStrings(colors - first)) - } - property("repeatDep suggest remaining one") = { - val take = colors.toSeq.take(2) - checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take)) - } - property("repeatDep requires at least one token") = !matches(repeat, "") - property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) - property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) -} -object ParserExample { - val ws = charClass(_.isWhitespace)+ - val notws = charClass(!_.isWhitespace)+ - - val name = token("test") - val options = (ws ~> token("quick" | "failed" | "new"))* - val exampleSet = Set("am", "is", "are", "was", "were") - val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false)))* - - val t = name ~ options ~ include - - // Get completions for some different inputs - println(completions(t, "te", 1)) - println(completions(t, "test ", 1)) - println(completions(t, "test w", 1)) - - // Get the parsed result for different inputs - println(apply(t)("te").resultEmpty) - println(apply(t)("test").resultEmpty) - println(apply(t)("test w").resultEmpty) - println(apply(t)("test was were").resultEmpty) - - def run(n: Int): Unit = { - val a = 'a'.id - val aq = a.? - val aqn = repeat(aq, min = n, max = n) - val an = repeat(a, min = n, max = n) - val ann = aqn ~ an - - def r = apply(ann)("a" * (n * 2)).resultEmpty - println(r.isValid) - } - def run2(n: Int): Unit = { - val ab = "ab".?.* - val r = apply(ab)("a" * n).resultEmpty - println(r) - } -} diff --git a/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala deleted file mode 100644 index effd9be78..000000000 --- a/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ /dev/null @@ -1,89 +0,0 @@ -package sbt.complete - -import org.specs2.mutable.Specification -import org.specs2.specification.Scope -import sbt.IO.withTemporaryDirectory -import java.io.File -import sbt.IO._ - -class FileExamplesTest extends Specification { - - "listing all files in an absolute base directory" should { - "produce the entire base directory's contents" in new directoryStructure { - fileExamples().toList should containTheSameElementsAs(allRelativizedPaths) - } - } - - "listing files with a prefix that matches none" should { - "produce an empty list" in new directoryStructure(withCompletionPrefix = "z") { - fileExamples().toList should beEmpty - } - } - - "listing single-character prefixed files" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - } - - "listing directory-prefixed files" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - - "produce sub-dir contents only when appending a file separator to the directory" in new directoryStructure(withCompletionPrefix = "far" + File.separator) { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - } - - "listing files with a sub-path prefix" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - } - - "completing a full path" should { - "produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") { - fileExamples().toList shouldEqual List("") - } - } - - class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit { - var fileExamples: FileExamples = _ - var baseDir: File = _ - var childFiles: List[File] = _ - var childDirectories: List[File] = _ - var nestedFiles: List[File] = _ - var nestedDirectories: List[File] = _ - - def allRelativizedPaths: List[String] = - (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get) - - def prefixedPathsOnly: List[String] = - allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length) - - override def delayedInit(testBody: => Unit): Unit = { - withTemporaryDirectory { - tempDir => - createSampleDirStructure(tempDir) - fileExamples = new FileExamples(baseDir, withCompletionPrefix) - testBody - } - } - - private def createSampleDirStructure(tempDir: File): Unit = { - childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar")) - childDirectories = toChildFiles(tempDir, List("moo", "far")) - nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2")) - nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2")) - - (childDirectories ++ nestedDirectories).map(_.mkdirs()) - (childFiles ++ nestedFiles).map(_.createNewFile()) - - baseDir = tempDir - } - - private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _)) - } - -} diff --git a/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala deleted file mode 100644 index b5aa14250..000000000 --- a/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ /dev/null @@ -1,26 +0,0 @@ -package sbt.complete - -import org.specs2.mutable.Specification -import org.specs2.specification.Scope - -class FixedSetExamplesTest extends Specification { - - "adding a prefix" should { - "produce a smaller set of examples with the prefix removed" in new examples { - fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u")) - fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol")) - fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar")) - } - } - - "without a prefix" should { - "produce the original set" in new examples { - fixedSetExamples() mustEqual exampleSet - } - } - - trait examples extends Scope { - val exampleSet = List("foo", "bar", "fool", "fu") - val fixedSetExamples = FixedSetExamples(exampleSet) - } -} diff --git a/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala deleted file mode 100644 index dff68803c..000000000 --- a/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ /dev/null @@ -1,93 +0,0 @@ -package sbt.complete - -import org.specs2.mutable.Specification -import org.specs2.specification.Scope -import Completion._ - -class ParserWithExamplesTest extends Specification { - - "listing a limited number of completions" should { - "grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples { - parserWithExamples.completions(0) - examples.size shouldEqual maxNumberOfExamples - } - } - - "listing only valid completions" should { - "use the delegate parser to remove invalid examples" in new parserWithValidExamples { - val validCompletions = Completions(Set( - suggestion("blue"), - suggestion("red") - )) - parserWithExamples.completions(0) shouldEqual validCompletions - } - } - - "listing valid completions in a derived parser" should { - "produce only valid examples that start with the character of the derivation" in new parserWithValidExamples { - val derivedCompletions = Completions(Set( - suggestion("lue") - )) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } - } - - "listing valid and invalid completions" should { - "produce the entire source of examples" in new parserWithAllExamples { - val completions = Completions(examples.map(suggestion(_)).toSet) - parserWithExamples.completions(0) shouldEqual completions - } - } - - "listing valid and invalid completions in a derived parser" should { - "produce only examples that start with the character of the derivation" in new parserWithAllExamples { - val derivedCompletions = Completions(Set( - suggestion("lue"), - suggestion("lock") - )) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } - } - - class parserWithLazyExamples extends parser(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false) - - class parserWithValidExamples extends parser(removeInvalidExamples = true) - - class parserWithAllExamples extends parser(removeInvalidExamples = false) - - case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), - maxNumberOfExamples: Int = 25, - removeInvalidExamples: Boolean) extends Scope { - - import DefaultParsers._ - - val colorParser = "blue" | "green" | "black" | "red" - val parserWithExamples: Parser[String] = new ParserWithExamples[String]( - colorParser, - FixedSetExamples(examples), - maxNumberOfExamples, - removeInvalidExamples - ) - } - - case class GrowableSourceOfExamples() extends Iterable[String] { - private var numberOfIteratedElements: Int = 0 - - override def iterator: Iterator[String] = { - new Iterator[String] { - var currentElement = 0 - - override def next(): String = { - currentElement += 1 - numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements) - numberOfIteratedElements.toString - } - - override def hasNext: Boolean = true - } - } - - override def size: Int = numberOfIteratedElements - } - -} diff --git a/util/control/NOTICE b/util/control/NOTICE deleted file mode 100644 index 76a30965a..000000000 --- a/util/control/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Control Component -Copyright 2009 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/control/src/main/scala/sbt/ErrorHandling.scala b/util/control/src/main/scala/sbt/ErrorHandling.scala deleted file mode 100644 index 70eba7d2f..000000000 --- a/util/control/src/main/scala/sbt/ErrorHandling.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.IOException - -object ErrorHandling { - def translate[T](msg: => String)(f: => T) = - try { f } - catch { - case e: IOException => throw new TranslatedIOException(msg + e.toString, e) - case e: Exception => throw new TranslatedException(msg + e.toString, e) - } - - def wideConvert[T](f: => T): Either[Throwable, T] = - try { Right(f) } - catch { - case ex @ (_: Exception | _: StackOverflowError) => Left(ex) - case err @ (_: ThreadDeath | _: VirtualMachineError) => throw err - case x: Throwable => Left(x) - } - - def convert[T](f: => T): Either[Exception, T] = - try { Right(f) } - catch { case e: Exception => Left(e) } - - def reducedToString(e: Throwable): String = - if (e.getClass == classOf[RuntimeException]) { - val msg = e.getMessage - if (msg == null || msg.isEmpty) e.toString else msg - } else - e.toString -} -sealed class TranslatedException private[sbt] (msg: String, cause: Throwable) extends RuntimeException(msg, cause) { - override def toString = msg -} -final class TranslatedIOException private[sbt] (msg: String, cause: IOException) extends TranslatedException(msg, cause) diff --git a/util/control/src/main/scala/sbt/ExitHook.scala b/util/control/src/main/scala/sbt/ExitHook.scala deleted file mode 100644 index 8ee5ddf86..000000000 --- a/util/control/src/main/scala/sbt/ExitHook.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package sbt - -/** Defines a function to call as sbt exits.*/ -trait ExitHook { - /** Subclasses should implement this method, which is called when this hook is executed. */ - def runBeforeExiting(): Unit -} -object ExitHook { - def apply(f: => Unit): ExitHook = new ExitHook { def runBeforeExiting() = f } -} - -object ExitHooks { - /** Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to run. */ - def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] = - exitHooks.flatMap(hook => - ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption - ) -} \ No newline at end of file diff --git a/util/control/src/main/scala/sbt/MessageOnlyException.scala b/util/control/src/main/scala/sbt/MessageOnlyException.scala deleted file mode 100644 index ab4727b95..000000000 --- a/util/control/src/main/scala/sbt/MessageOnlyException.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Mark Harrah - */ -package sbt - -final class MessageOnlyException(override val toString: String) extends RuntimeException(toString) - -/** - * A dummy exception for the top-level exception handler to know that an exception - * has been handled, but is being passed further up to indicate general failure. - */ -final class AlreadyHandledException(val underlying: Throwable) extends RuntimeException - -/** - * A marker trait for a top-level exception handler to know that this exception - * doesn't make sense to display. - */ -trait UnprintableException extends Throwable - -/** - * A marker trait that refines UnprintableException to indicate to a top-level exception handler - * that the code throwing this exception has already provided feedback to the user about the error condition. - */ -trait FeedbackProvidedException extends UnprintableException diff --git a/util/cross/src/main/input_sources/CrossVersionUtil.scala b/util/cross/src/main/input_sources/CrossVersionUtil.scala deleted file mode 100644 index 6a63c9139..000000000 --- a/util/cross/src/main/input_sources/CrossVersionUtil.scala +++ /dev/null @@ -1,71 +0,0 @@ -package ${{cross.package0}}.${{cross.package1}} - -object CrossVersionUtil -{ - val trueString = "true" - val falseString = "false" - val fullString = "full" - val noneString = "none" - val disabledString = "disabled" - val binaryString = "binary" - val TransitionScalaVersion = "2.10" - val TransitionSbtVersion = "0.12" - - def isFull(s: String): Boolean = (s == trueString) || (s == fullString) - def isDisabled(s: String): Boolean = (s == falseString) || (s == noneString) || (s == disabledString) - def isBinary(s: String): Boolean = (s == binaryString) - - private[${{cross.package0}}] def isSbtApiCompatible(v: String): Boolean = sbtApiVersion(v).isDefined - /** Returns sbt binary interface x.y API compatible with the given version string v. - * RCs for x.y.0 are considered API compatible. - * Compatibile versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12). - */ - private[${{cross.package0}}] def sbtApiVersion(v: String): Option[(Int, Int)] = - { - val ReleaseV = """(\d+)\.(\d+)\.(\d+)(-\d+)?""".r - val CandidateV = """(\d+)\.(\d+)\.(\d+)(-RC\d+)""".r - val NonReleaseV = """(\d+)\.(\d+)\.(\d+)([-\w+]*)""".r - v match { - case ReleaseV(x, y, z, ht) => Some((x.toInt, y.toInt)) - case CandidateV(x, y, z, ht) => Some((x.toInt, y.toInt)) - case NonReleaseV(x, y, z, ht) if z.toInt > 0 => Some((x.toInt, y.toInt)) - case _ => None - } - } - private[${{cross.package0}}] def isScalaApiCompatible(v: String): Boolean = scalaApiVersion(v).isDefined - /** Returns Scala binary interface x.y API compatible with the given version string v. - * Compatibile versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1. - */ - private[${{cross.package0}}] def scalaApiVersion(v: String): Option[(Int, Int)] = - { - val ReleaseV = """(\d+)\.(\d+)\.(\d+)(-\d+)?""".r - val BinCompatV = """(\d+)\.(\d+)\.(\d+)-bin(-.*)?""".r - val NonReleaseV = """(\d+)\.(\d+)\.(\d+)(-\w+)""".r - v match { - case ReleaseV(x, y, z, ht) => Some((x.toInt, y.toInt)) - case BinCompatV(x, y, z, ht) => Some((x.toInt, y.toInt)) - case NonReleaseV(x, y, z, ht) if z.toInt > 0 => Some((x.toInt, y.toInt)) - case _ => None - } - } - private[${{cross.package0}}] val PartialVersion = """(\d+)\.(\d+)(?:\..+)?""".r - private[${{cross.package0}}] def partialVersion(s: String): Option[(Int,Int)] = - s match { - case PartialVersion(major, minor) => Some((major.toInt, minor.toInt)) - case _ => None - } - def binaryScalaVersion(full: String): String = binaryVersionWithApi(full, TransitionScalaVersion)(scalaApiVersion) - def binarySbtVersion(full: String): String = binaryVersionWithApi(full, TransitionSbtVersion)(sbtApiVersion) - private[${{cross.package0}}] def binaryVersion(full: String, cutoff: String): String = binaryVersionWithApi(full, cutoff)(scalaApiVersion) - private[this] def isNewer(major: Int, minor: Int, minMajor: Int, minMinor: Int): Boolean = - major > minMajor || (major == minMajor && minor >= minMinor) - private[this] def binaryVersionWithApi(full: String, cutoff: String)(apiVersion: String => Option[(Int,Int)]): String = - { - def sub(major: Int, minor: Int) = major + "." + minor - (apiVersion(full), partialVersion(cutoff)) match { - case (Some((major, minor)), None) => sub(major, minor) - case (Some((major, minor)), Some((minMajor, minMinor))) if isNewer(major, minor, minMajor, minMinor) => sub(major, minor) - case _ => full - } - } -} diff --git a/util/datatype/NOTICE b/util/datatype/NOTICE deleted file mode 100644 index b5b026415..000000000 --- a/util/datatype/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Datatype Generator Component -Copyright 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/datatype/src/main/scala/xsbt/datatype/DatatypeParser.scala b/util/datatype/src/main/scala/xsbt/datatype/DatatypeParser.scala deleted file mode 100644 index 71392c3fe..000000000 --- a/util/datatype/src/main/scala/xsbt/datatype/DatatypeParser.scala +++ /dev/null @@ -1,76 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt -package datatype - -import java.io.File -import sbt.IO.readLines -import Function.tupled -import java.util.regex.Pattern - -class DatatypeParser extends NotNull { - val WhitespacePattern = Pattern compile """\s*""" //(?>\#(.*))?""" - val EnumPattern = Pattern compile """enum\s+(\S+)\s*:\s*(.+)""" - val ClassPattern = Pattern compile """(\t*)(\S+)\s*""" - val MemberPattern = Pattern compile """(\t*)(\S+)\s*:\s*([^\s*]+)([*]?)""" - - def processWhitespaceLine(l: Array[String], line: Int) = new WhitespaceLine(l.mkString, line) - def processEnumLine(l: Array[String], line: Int) = new EnumLine(l(0), l(1).split(",").map(_.trim), line) - def processClassLine(l: Array[String], line: Int) = new ClassLine(l(1), l(0).length, line) - def processMemberLine(l: Array[String], line: Int) = new MemberLine(l(1), l(2), l(3).isEmpty, l(0).length, line) - - def error(l: Line, msg: String): Nothing = error(l.line, msg) - def error(line: Int, msg: String): Nothing = throw new RuntimeException("{line " + line + "} " + msg) - - def parseFile(file: File): Seq[Definition] = - { - val (open, closed) = ((Array[ClassDef](), List[Definition]()) /: parseLines(file)) { - case ((open, defs), line) => processLine(open, defs, line) - } - open ++ closed - } - def parseLines(file: File): Seq[Line] = readLines(file).zipWithIndex.map(tupled(parseLine)) - def parseLine(line: String, lineNumber: Int): Line = - matchPattern(WhitespacePattern -> processWhitespaceLine _, EnumPattern -> processEnumLine _, - ClassPattern -> processClassLine _, MemberPattern -> processMemberLine _)(line, lineNumber) - type Handler = (Array[String], Int) => Line - def matchPattern(patterns: (Pattern, Handler)*)(line: String, lineNumber: Int): Line = - patterns.flatMap { case (pattern, f) => matchPattern(pattern, f)(line, lineNumber) }.headOption.getOrElse { - error(lineNumber, "Invalid line, expected enum, class, or member definition") - } - def matchPattern(pattern: Pattern, f: Handler)(line: String, lineNumber: Int): Option[Line] = - { - val matcher = pattern.matcher(line) - if (matcher.matches) { - val count = matcher.groupCount - val groups = (for (i <- 1 to count) yield matcher.group(i)).toArray[String] - Some(f(groups, lineNumber)) - } else - None - } - - def processLine(open: Array[ClassDef], definitions: List[Definition], line: Line): (Array[ClassDef], List[Definition]) = - { - def makeAbstract(x: ClassDef) = new ClassDef(x.name, x.parent, x.members, true) - - line match { - case w: WhitespaceLine => (open, definitions) - case e: EnumLine => (Array(), new EnumDef(e.name, e.members) :: open.toList ::: definitions) - case m: MemberLine => - if (m.level == 0 || m.level > open.length) error(m, "Member must be declared in a class definition") - else withCurrent(open, definitions, m.level) { c => List(c + m) } - case c: ClassLine => - if (c.level == 0) (Array(new ClassDef(c.name, None, Nil, false)), open.toList ::: definitions) - else if (c.level > open.length) error(c, "Class must be declared as top level or as a subclass") - else withCurrent(open, definitions, c.level) { p => val p1 = makeAbstract(p); p1 :: new ClassDef(c.name, Some(p1), Nil, false) :: Nil } - } - } - private def withCurrent(open: Array[ClassDef], definitions: List[Definition], level: Int)(onCurrent: ClassDef => Seq[ClassDef]): (Array[ClassDef], List[Definition]) = - { - require(0 < level && level <= open.length) - val closed = open.drop(level).toList - val newOpen = open.take(level - 1) ++ onCurrent(open(level - 1)) - (newOpen.toArray, closed ::: definitions) - } -} diff --git a/util/datatype/src/main/scala/xsbt/datatype/Definition.scala b/util/datatype/src/main/scala/xsbt/datatype/Definition.scala deleted file mode 100644 index 797218f43..000000000 --- a/util/datatype/src/main/scala/xsbt/datatype/Definition.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.datatype - -sealed trait Definition extends NotNull { - val name: String -} -final class ClassDef(val name: String, val parent: Option[ClassDef], val members: Seq[MemberDef], val isAbstract: Boolean) extends Definition { - def allMembers = members ++ inheritedMembers - def inheritedMembers: Seq[MemberDef] = parent.toList.flatMap(_.allMembers) - def hasLazyMembers = members exists (_.lzy) - def +(m: MemberLine) = new ClassDef(name, parent, members ++ Seq(new MemberDef(m.name, m.tpe.stripPrefix("~"), m.single, m.tpe.startsWith("~"))), isAbstract) -} -final class EnumDef(val name: String, val members: Seq[String]) extends Definition - -final class MemberDef(val name: String, val tpe: String, val single: Boolean, val lzy: Boolean) extends NotNull { - def javaType(accessor: Boolean) = - { - val base = tpe + (if (single) "" else "[]") - if (!accessor && lzy) "Lazy<" + base + ">" else base - } - def scalaType(accessor: Boolean) = - { - val base = if (single) tpe else "Array[" + tpe + "]" - if (!accessor && lzy) "Lazy[" + base + "]" else base - } - def asGet = name + (if (lzy) ".get()" else "") - def asScalaDeclaration(accessor: Boolean) = name + ": " + scalaType(accessor) - def asJavaDeclaration(accessor: Boolean) = javaType(accessor) + " " + name - def mapType(f: String => String) = new MemberDef(name, f(tpe), single, lzy) -} \ No newline at end of file diff --git a/util/datatype/src/main/scala/xsbt/datatype/GenerateDatatypes.scala b/util/datatype/src/main/scala/xsbt/datatype/GenerateDatatypes.scala deleted file mode 100644 index c55ccc4ab..000000000 --- a/util/datatype/src/main/scala/xsbt/datatype/GenerateDatatypes.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.datatype - -import java.io.File -import java.util.Locale - -/** Generates a datatype hierarchy from a definition file.*/ -object GenerateDatatypes { - /** Arguments: +*/ - def main(args: Array[String]): Unit = { - if (args.length < 3) { - System.err.println("Invalid number of arguments, expected package, base directory, and files to process") - System.exit(1) - } else { - val packageName = args(0).trim - require(!packageName.isEmpty) - - val baseDirectory = new File(args(1)) - baseDirectory.mkdirs - - val files = args.drop(2).map(new File(_)) - // parse the files, getting all interface and enums - val parser = new DatatypeParser - val definitions = files.flatMap(parser.parseFile) - - // create the interfaces, enums, and class implementations - val generator = new ImmutableGenerator(packageName, baseDirectory) - generator.writeDefinitions(definitions) - } - } -} diff --git a/util/datatype/src/main/scala/xsbt/datatype/Generator.scala b/util/datatype/src/main/scala/xsbt/datatype/Generator.scala deleted file mode 100644 index 453d43adb..000000000 --- a/util/datatype/src/main/scala/xsbt/datatype/Generator.scala +++ /dev/null @@ -1,168 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt -package datatype - -import java.io.File -import sbt.IO.write - -import Generator._ -import java.util.Locale - -abstract class GeneratorBase(val basePkgName: String, val baseDirectory: File) { - def writeDefinitions(ds: Iterable[Definition]) = Generator.writeDefinitions(ds)(writeDefinition) - def writeDefinition(d: Definition) = d match { case e: EnumDef => writeEnum(e); case c: ClassDef => writeClass(c) } - def writeEnum(e: EnumDef): Unit = { - val content = - "public enum " + e.name + " {" + - e.members.mkString("\n\t", ",\n\t", "\n") + - "}" - writeSource(e.name, basePkgName, content) - } - def writeClass(c: ClassDef): Unit - - def writeSource(name: String, pkgName: String, content: String): Unit = { - val file = new File(new File(baseDirectory, packagePath(pkgName)), name + ".java") - file.getParentFile.mkdirs() - write(file, "package " + pkgName + ";\n\n" + content) - } - private def packagePath(pkgName: String) = pkgName.replace('.', File.separatorChar) -} - -/** - * Creates immutable datatype classes in Java from the intermediate Definition representation. - * - * A ClassDef is written as a class with an optional parent class. The class has a single constructor with - * parameters for all declared and inherited members. Declared members are listed first in the constructor. - * Inherited members are passed to the super constructor. The value of each member is held in a private - * final field and is accessed by a method of the same name. - * - * A toString method is generated for debugging. - * The classes implement java.io.Serializable. - * - * .@param baseDirectory output directory for sources - * @param pkgName package that classes will be defined in - */ -class ImmutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(pkgName, baseDir) { - def writeClass(c: ClassDef): Unit = writeSource(c.name, basePkgName, classContent(c)) - def classContent(c: ClassDef): String = - { - val abstractStr = if (c.isAbstract) "abstract " else "final " - val allMembers = c.allMembers.map(normalize) - val normalizedMembers = c.members.map(normalize) - val fields = normalizedMembers.map(m => "private final " + m.asJavaDeclaration(false) + ";") - val accessors = normalizedMembers.map(m => "public final " + m.asJavaDeclaration(true) + "()\n\t{\n\t\treturn " + m.asGet + ";\n\t}") - val parameters = allMembers.map(_.asJavaDeclaration(false)) - val assignments = normalizedMembers.map(m => "this." + m.name + " = " + m.name + ";") - val superConstructor = - { - val inherited = c.inheritedMembers - if (inherited.isEmpty) "" else "super(" + inherited.map(_.name).mkString(", ") + ");\n\t\t" - } - - val constructor = "public " + c.name + "(" + parameters.mkString(", ") + ")\n\t" + - "{\n\t\t" + - superConstructor + - assignments.mkString("\n\t\t") + "\n\t" + - "}" - "import java.util.Arrays;\n" + - "import java.util.List;\n" + - "public " + abstractStr + "class " + c.name + c.parent.map(" extends " + _.name + " ").getOrElse(" implements java.io.Serializable") + "\n" + - "{\n\t" + - constructor + "\n\t" + - (fields ++ accessors).mkString("\n\t") + "\n" + - (if (!c.isAbstract) "\t" + equalsMethod(c) + "\n\t" + hashCodeMethod(c) + "\n\t" + toStringMethod(c) + "\n" else "") + - "}\n" - } -} - -object Generator { - def methodSignature(modifiers: String, returnType: String, name: String, parameters: String) = - modifiers + " " + returnType + " " + name + "(" + parameters + ")" - def method(modifiers: String, returnType: String, name: String, parameters: String, body: String) = - methodSignature(modifiers, returnType, name, parameters) + "\n\t{\n\t\t " + body + "\n\t}" - def fieldToString(name: String, single: Boolean) = "\"" + name + ": \" + " + fieldString(name + "()", single) - def fieldString(arg: String, single: Boolean) = if (single) arg else "Arrays.toString(" + arg + ")" - def fieldEquals(arg: String, single: Boolean, primitive: Boolean) = { - if (single) { - if (primitive) arg + " == o." + arg else arg + ".equals(o." + arg + ")" - } else { - "Arrays." + (if (primitive) "equals" else "deepEquals") + "(" + arg + ", o." + arg + ")" - } - } - def normalize(m: MemberDef): MemberDef = - m.mapType(tpe => if (isPrimitive(tpe)) tpe.toLowerCase(Locale.ENGLISH) else tpe) - def isPrimitive(tpe: String) = primitives(tpe.toLowerCase(Locale.ENGLISH)) - private val primitives = Set("int", "boolean", "float", "long", "short", "byte", "char", "double") - - def equalsMethod(c: ClassDef): String = - { - val content = if (c.hasLazyMembers) { - "return this == obj; // We have lazy members, so use object identity to avoid circularity." - } else { - val allMembers = c.allMembers.map(normalize) - val memberComparisons = if (allMembers.isEmpty) "true" else allMembers.map(m => fieldEquals(m.name + "()", m.single, isPrimitive(m.tpe))).mkString(" && ") - "if (this == obj) {\n\t\t\t return true;\n\t\t} else if (!(obj instanceof " + c.name + ")) {\n\t\t\t return false;\n\t\t} else {\n\t\t\t" + c.name + " o = (" + c.name + ")obj;\n\t\t\treturn " + memberComparisons + ";\n\t\t}" - } - method("public", "boolean", "equals", "Object obj", content) - } - - def hashCodeMethod(c: ClassDef): String = - { - def hashCodeExprForMember(m: MemberDef) = - { - val primitive = isPrimitive(m.tpe) - val f = m.name + "()" // Assumes m has already been normalized. - if (m.single) { - if (primitive) { - m.tpe.toLowerCase match { - case "boolean" => "(" + f + " ? 0 : 1)" - case "long" => "(int)(" + f + " ^ (" + f + " >>> 32))" - case "float" => "Float.floatToIntBits(" + f + ")" - case "double" => "(int)(Double.doubleToLongBits(" + f + ") ^ (Double.doubleToLongBits(" + f + ") >>> 32))" - case "int" => f - case _ => "(int)" + f - } - } else { - f + ".hashCode()" - } - } else { - "Arrays." + (if (primitive) "hashCode" else "deepHashCode") + "(" + f + ")" - } - } - val hashCodeExpr = if (c.hasLazyMembers) { - "super.hashCode()" - } else { - val allMembers = c.allMembers.map(normalize) - val memberHashCodes = allMembers.map(hashCodeExprForMember) - ("17" /: memberHashCodes) { "37 * (" + _ + ") + " + _ } - } - method("public", "int", "hashCode", "", "return " + hashCodeExpr + ";") - } - - def toStringMethod(c: ClassDef): String = - { - val content = if (c.hasLazyMembers) { - "return super.toString();" - } else { - val allMembers = c.allMembers.map(normalize) - val parametersString = if (allMembers.isEmpty) "\"\"" else allMembers.map(m => fieldToString(m.name, m.single)).mkString(" + \", \" + ") - "return \"" + c.name + "(\" + " + parametersString + " + \")\";" - } - method("public", "String", "toString", "", content) - } - - def writeDefinitions(ds: Iterable[Definition])(writeDefinition: Definition => Unit): Unit = { - val (_, duplicates) = - ((Set[String](), Set[String]()) /: ds.map(_.name)) { - case ((nameSet, duplicates), name) => - if (nameSet.contains(name)) (nameSet, duplicates + name) else (nameSet + name, duplicates) - } - if (duplicates.isEmpty) - ds.foreach(writeDefinition) - else - sys.error("Duplicate names:\n\t" + duplicates.mkString("\n\t")) - } - def implName(name: String) = name + "0" -} diff --git a/util/datatype/src/main/scala/xsbt/datatype/Line.scala b/util/datatype/src/main/scala/xsbt/datatype/Line.scala deleted file mode 100644 index e4efbc442..000000000 --- a/util/datatype/src/main/scala/xsbt/datatype/Line.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.datatype - -sealed trait Line extends NotNull { val line: Int } -final class ClassLine(val name: String, val level: Int, val line: Int) extends Line -final class EnumLine(val name: String, val members: Seq[String], val line: Int) extends Line -final class MemberLine(val name: String, val tpe: String, val single: Boolean, val level: Int, val line: Int) extends Line -final class WhitespaceLine(val comment: String, val line: Int) extends Line diff --git a/util/io/NOTICE b/util/io/NOTICE deleted file mode 100644 index 52187da2e..000000000 --- a/util/io/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: I/O Component -Copyright 2008, 2009, 2010 Mark Harrah, Viktor Klang, Ross McDonald -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/DeferredWriter.scala b/util/io/src/main/scala/sbt/DeferredWriter.scala deleted file mode 100644 index 6223883ce..000000000 --- a/util/io/src/main/scala/sbt/DeferredWriter.scala +++ /dev/null @@ -1,29 +0,0 @@ -package sbt - -import java.io.Writer - -/** A `Writer` that avoids constructing the underlying `Writer` with `make` until a method other than `close` is called on this `Writer`. */ -final class DeferredWriter(make: => Writer) extends Writer { - private[this] var opened = false - private[this] var delegate0: Writer = _ - private[this] def delegate: Writer = synchronized { - if (delegate0 eq null) { - delegate0 = make - opened = true - } - delegate0 - } - override def close() = synchronized { - if (opened) delegate0.close() - } - - override def append(c: Char): Writer = delegate.append(c) - override def append(csq: CharSequence): Writer = delegate.append(csq) - override def append(csq: CharSequence, start: Int, end: Int): Writer = delegate.append(csq, start, end) - override def flush() = delegate.flush() - override def write(cbuf: Array[Char]) = delegate.write(cbuf) - override def write(cbuf: Array[Char], off: Int, len: Int): Unit = delegate.write(cbuf, off, len) - override def write(c: Int): Unit = delegate.write(c) - override def write(s: String): Unit = delegate.write(s) - override def write(s: String, off: Int, len: Int): Unit = delegate.write(s, off, len) -} \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/Hash.scala b/util/io/src/main/scala/sbt/Hash.scala deleted file mode 100644 index daf9854ff..000000000 --- a/util/io/src/main/scala/sbt/Hash.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.{ ByteArrayInputStream, File, InputStream } -import java.net.{ URI, URL } - -object Hash { - private val BufferSize = 8192 - - /** Converts an array of `bytes` to a hexadecimal representation String.*/ - def toHex(bytes: Array[Byte]): String = - { - val buffer = new StringBuilder(bytes.length * 2) - for (i <- 0 until bytes.length) { - val b = bytes(i) - val bi: Int = if (b < 0) b + 256 else b - buffer append toHex((bi >>> 4).asInstanceOf[Byte]) - buffer append toHex((bi & 0x0F).asInstanceOf[Byte]) - } - buffer.toString - } - - /** - * Converts the provided hexadecimal representation `hex` to an array of bytes. - * The hexadecimal representation must have an even number of characters in the range 0-9, a-f, or A-F. - */ - def fromHex(hex: String): Array[Byte] = - { - require((hex.length & 1) == 0, "Hex string must have length 2n.") - val array = new Array[Byte](hex.length >> 1) - for (i <- 0 until hex.length by 2) { - val c1 = hex.charAt(i) - val c2 = hex.charAt(i + 1) - array(i >> 1) = ((fromHex(c1) << 4) | fromHex(c2)).asInstanceOf[Byte] - } - array - } - - /** Truncates the last half of `s` if the string has at least four characters. Otherwise, the original string is returned. */ - def halve(s: String): String = if (s.length > 3) s.substring(0, s.length / 2) else s - - /** Computes the SHA-1 hash of `s` and returns the first `i` characters of the hexadecimal representation of the hash. */ - def trimHashString(s: String, i: Int): String = toHex(apply(s)).take(i) - - /** Computes the SHA-1 hash of `s` and truncates the hexadecimal representation of the hash via [[halve]]. */ - def halfHashString(s: String): String = halve(toHex(apply(s))) - - /** Calculates the SHA-1 hash of the given String.*/ - def apply(s: String): Array[Byte] = apply(s.getBytes("UTF-8")) - /** Calculates the SHA-1 hash of the given Array[Byte].*/ - def apply(as: Array[Byte]): Array[Byte] = apply(new ByteArrayInputStream(as)) - /** Calculates the SHA-1 hash of the given file.*/ - def apply(file: File): Array[Byte] = Using.fileInputStream(file)(apply) - /** Calculates the SHA-1 hash of the given resource.*/ - def apply(url: URL): Array[Byte] = Using.urlInputStream(url)(apply) - - /** - * If the URI represents a local file (the scheme is "file"), - * this method calculates the SHA-1 hash of the contents of that file. - * Otherwise, this methods calculates the SHA-1 hash of the normalized string representation of the URI. - */ - def contentsIfLocal(uri: URI): Array[Byte] = - if (uri.getScheme == "file") apply(uri.toURL) else apply(uri.normalize.toString) - - /** Calculates the SHA-1 hash of the given stream, closing it when finished.*/ - def apply(stream: InputStream): Array[Byte] = - { - import java.security.{ MessageDigest, DigestInputStream } - val digest = MessageDigest.getInstance("SHA") - try { - val dis = new DigestInputStream(stream, digest) - val buffer = new Array[Byte](BufferSize) - while (dis.read(buffer) >= 0) {} - dis.close() - digest.digest - } finally { stream.close() } - } - - private def toHex(b: Byte): Char = - { - require(b >= 0 && b <= 15, "Byte " + b + " was not between 0 and 15") - if (b < 10) - ('0'.asInstanceOf[Int] + b).asInstanceOf[Char] - else - ('a'.asInstanceOf[Int] + (b - 10)).asInstanceOf[Char] - } - private def fromHex(c: Char): Int = - { - val b = - if (c >= '0' && c <= '9') - (c - '0') - else if (c >= 'a' && c <= 'f') - (c - 'a') + 10 - else if (c >= 'A' && c <= 'F') - (c - 'A') + 10 - else - throw new RuntimeException("Invalid hex character: '" + c + "'.") - b - } -} \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/IO.scala b/util/io/src/main/scala/sbt/IO.scala deleted file mode 100644 index 348fd0ab3..000000000 --- a/util/io/src/main/scala/sbt/IO.scala +++ /dev/null @@ -1,873 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah, Viktor Klang, Ross McDonald - */ -package sbt - -import Using._ -import ErrorHandling.translate - -import java.io.{ BufferedReader, ByteArrayOutputStream, BufferedWriter, File, FileInputStream, InputStream, OutputStream, PrintWriter } -import java.io.{ ObjectInputStream, ObjectStreamClass } -import java.net.{ URI, URISyntaxException, URL } -import java.nio.charset.Charset -import java.util.Properties -import java.util.jar.{ Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest } -import java.util.zip.{ CRC32, GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream } -import scala.collection.immutable.TreeSet -import scala.collection.mutable.{ HashMap, HashSet } -import scala.reflect.{ Manifest => SManifest } -import Function.tupled - -/** A collection of File, URL, and I/O utility methods.*/ -object IO { - /** The maximum number of times a unique temporary filename is attempted to be created.*/ - private val MaximumTries = 10 - /** The producer of randomness for unique name generation.*/ - private lazy val random = new java.util.Random - val temporaryDirectory = new File(System.getProperty("java.io.tmpdir")) - /** The size of the byte or char buffer used in various methods.*/ - private val BufferSize = 8192 - /** File scheme name */ - private[sbt] val FileScheme = "file" - - /** The newline string for this system, as obtained by the line.separator system property. */ - val Newline = System.getProperty("line.separator") - - val utf8 = Charset.forName("UTF-8") - - /** - * Returns a URL for the directory or jar containing the the class file `cl`. - * If the location cannot be determined, an error is generated. - * Note that Java standard library classes typically do not have a location associated with them. - */ - def classLocation(cl: Class[_]): URL = - { - val codeSource = cl.getProtectionDomain.getCodeSource - if (codeSource == null) sys.error("No class location for " + cl) - else codeSource.getLocation - } - - /** - * Returns the directory or jar file containing the the class file `cl`. - * If the location cannot be determined or it is not a file, an error is generated. - * Note that Java standard library classes typically do not have a location associated with them. - */ - def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl)) - - /** - * Returns a URL for the directory or jar containing the class file for type `T` (as determined by an implicit Manifest). - * If the location cannot be determined, an error is generated. - * Note that Java standard library classes typically do not have a location associated with them. - */ - def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.runtimeClass) - - /** - * Returns the directory or jar file containing the the class file for type `T` (as determined by an implicit Manifest). - * If the location cannot be determined, an error is generated. - * Note that Java standard library classes typically do not have a location associated with them. - */ - def classLocationFile[T](implicit mf: SManifest[T]): File = classLocationFile(mf.runtimeClass) - - /** - * 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. - */ - def toFile(url: URL): File = - try { new File(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. */ - def asFile(url: URL): File = urlAsFile(url) getOrElse sys.error("URL is not a file: " + url) - def urlAsFile(url: URL): Option[File] = - url.getProtocol match { - case FileScheme => Some(toFile(url)) - case "jar" => - val path = url.getPath - val end = path.indexOf('!') - Some(uriToFile(if (end == -1) path else path.substring(0, end))) - case _ => None - } - - private[this] def uriToFile(uriString: String): 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) - } - } - - def assertDirectory(file: File): Unit = - assert(file.isDirectory, (if (file.exists) "Not a directory: " else "Directory not found: ") + file) - - def assertDirectories(file: File*): Unit = file.foreach(assertDirectory) - - // "base.extension" -> (base, extension) - /** - * Splits the given string into base and extension strings. - * If `name` contains no period, the base string is the input string and the extension is the empty string. - * Otherwise, the base is the substring up until the last period (exclusive) and - * the extension is the substring after the last period. - * - * For example, `split("Build.scala") == ("Build", "scala")` - */ - def split(name: String): (String, String) = - { - val lastDot = name.lastIndexOf('.') - if (lastDot >= 0) - (name.substring(0, lastDot), name.substring(lastDot + 1)) - else - (name, "") - } - - /** - * Each input file in `files` is created if it doesn't exist. - * If a file already exists, the last modified time is set to the current time. - * It is not guaranteed that all files will have the same last modified time after this call. - */ - def touch(files: Traversable[File]): Unit = files.foreach(f => touch(f)) - - /** - * Creates a file at the given location if it doesn't exist. - * If the file already exists and `setModified` is true, this method sets the last modified time to the current time. - */ - def touch(file: File, setModified: Boolean = true): Unit = { - val absFile = file.getAbsoluteFile - createDirectory(absFile.getParentFile) - val created = translate("Could not create file " + absFile) { absFile.createNewFile() } - if (created || absFile.isDirectory) - () - else if (setModified && !absFile.setLastModified(System.currentTimeMillis)) - sys.error("Could not update last modified time for file " + absFile) - } - - /** Creates directories `dirs` and all parent directories. It tries to work around a race condition in `File.mkdirs()` by retrying up to a limit.*/ - def createDirectories(dirs: Traversable[File]): Unit = - dirs.foreach(createDirectory) - - /** Creates directory `dir` and all parent directories. It tries to work around a race condition in `File.mkdirs()` by retrying up to a limit.*/ - def createDirectory(dir: File): Unit = - { - def failBase = "Could not create directory " + dir - // Need a retry because mkdirs() has a race condition - var tryCount = 0 - while (!dir.exists && !dir.mkdirs() && tryCount < 100) { tryCount += 1 } - if (dir.isDirectory) - () - else if (dir.exists) { - sys.error(failBase + ": file exists and is not a directory.") - } else - sys.error(failBase) - } - - /** Gzips the file 'in' and writes it to 'out'. 'in' cannot be the same file as 'out'. */ - def gzip(in: File, out: File): Unit = { - require(in != out, "Input file cannot be the same as the output file.") - Using.fileInputStream(in) { inputStream => - Using.fileOutputStream()(out) { outputStream => - gzip(inputStream, outputStream) - } - } - } - /** Gzips the InputStream 'in' and writes it to 'output'. Neither stream is closed.*/ - def gzip(input: InputStream, output: OutputStream): Unit = - gzipOutputStream(output) { gzStream => transfer(input, gzStream) } - - /** Gunzips the file 'in' and writes it to 'out'. 'in' cannot be the same file as 'out'. */ - def gunzip(in: File, out: File): Unit = { - require(in != out, "Input file cannot be the same as the output file.") - Using.fileInputStream(in) { inputStream => - Using.fileOutputStream()(out) { outputStream => - gunzip(inputStream, outputStream) - } - } - } - /** Gunzips the InputStream 'input' and writes it to 'output'. Neither stream is closed.*/ - def gunzip(input: InputStream, output: OutputStream): Unit = - gzipInputStream(input) { gzStream => transfer(gzStream, output) } - - def unzip(from: File, toDirectory: File, filter: NameFilter = AllPassFilter, preserveLastModified: Boolean = true): Set[File] = - fileInputStream(from)(in => unzipStream(in, toDirectory, filter, preserveLastModified)) - def unzipURL(from: URL, toDirectory: File, filter: NameFilter = AllPassFilter, preserveLastModified: Boolean = true): Set[File] = - urlInputStream(from)(in => unzipStream(in, toDirectory, filter, preserveLastModified)) - def unzipStream(from: InputStream, toDirectory: File, filter: NameFilter = AllPassFilter, preserveLastModified: Boolean = true): Set[File] = - { - createDirectory(toDirectory) - zipInputStream(from) { zipInput => extract(zipInput, toDirectory, filter, preserveLastModified) } - } - private def extract(from: ZipInputStream, toDirectory: File, filter: NameFilter, preserveLastModified: Boolean) = - { - val set = new HashSet[File] - def next(): Unit = { - val entry = from.getNextEntry - if (entry == null) - () - else { - val name = entry.getName - if (filter.accept(name)) { - val target = new File(toDirectory, name) - //log.debug("Extracting zip entry '" + name + "' to '" + target + "'") - if (entry.isDirectory) - createDirectory(target) - else { - set += target - translate("Error extracting zip entry '" + name + "' to '" + target + "': ") { - fileOutputStream(false)(target) { out => transfer(from, out) } - } - } - if (preserveLastModified) - target.setLastModified(entry.getTime) - } else { - //log.debug("Ignoring zip entry '" + name + "'") - } - from.closeEntry() - next() - } - } - next() - Set() ++ set - } - - /** Retrieves the content of the given URL and writes it to the given File. */ - def download(url: URL, to: File) = - Using.urlInputStream(url) { inputStream => - transfer(inputStream, to) - } - - /** Copies the contents of `in` to `out`.*/ - def transfer(in: File, out: File): Unit = - fileInputStream(in) { in => transfer(in, out) } - - /** - * Copies the contents of the input file `in` to the `out` stream. - * The output stream is not closed by this method. - */ - def transfer(in: File, out: OutputStream): Unit = - fileInputStream(in) { in => transfer(in, out) } - - /** Copies all bytes from the given input stream to the given File. The input stream is not closed by this method.*/ - def transfer(in: InputStream, to: File): Unit = - Using.fileOutputStream()(to) { outputStream => - transfer(in, outputStream) - } - - /** - * Copies all bytes from the given input stream to the given output stream. - * Neither stream is closed. - */ - def transfer(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, false) - /** - * Copies all bytes from the given input stream to the given output stream. The - * input stream is closed after the method completes. - */ - def transferAndClose(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, true) - private def transferImpl(in: InputStream, out: OutputStream, close: Boolean): Unit = { - try { - val buffer = new Array[Byte](BufferSize) - def read(): Unit = { - val byteCount = in.read(buffer) - if (byteCount >= 0) { - out.write(buffer, 0, byteCount) - read() - } - } - read() - } finally { if (close) in.close } - } - - /** - * Creates a temporary directory and provides its location to the given function. The directory - * is deleted after the function returns. - */ - def withTemporaryDirectory[T](action: File => T): T = - { - val dir = createTemporaryDirectory - try { action(dir) } - finally { delete(dir) } - } - - /** Creates a directory in the default temporary directory with a name generated from a random integer. */ - def createTemporaryDirectory: File = createUniqueDirectory(temporaryDirectory) - - /** Creates a directory in `baseDirectory` with a name generated from a random integer */ - def createUniqueDirectory(baseDirectory: File): File = - { - def create(tries: Int): File = - { - if (tries > MaximumTries) - sys.error("Could not create temporary directory.") - else { - val randomName = "sbt_" + java.lang.Integer.toHexString(random.nextInt) - val f = new File(baseDirectory, randomName) - - try { createDirectory(f); f } - catch { case e: Exception => create(tries + 1) } - } - } - create(0) - } - /** - * Creates a file in the default temporary directory, calls `action` with the file, deletes the file, and returns the result of calling `action`. - * The name of the file will begin with `prefix`, which must be at least three characters long, and end with `postfix`, which has no minimum length. - */ - def withTemporaryFile[T](prefix: String, postfix: String)(action: File => T): T = - { - val file = File.createTempFile(prefix, postfix) - try { action(file) } - finally { file.delete() } - } - - private[sbt] def jars(dir: File): Iterable[File] = listFiles(dir, GlobFilter("*.jar")) - - /** Deletes all empty directories in the set. Any non-empty directories are ignored. */ - def deleteIfEmpty(dirs: collection.Set[File]): Unit = - { - val isEmpty = new HashMap[File, Boolean] - def visit(f: File): Boolean = isEmpty.getOrElseUpdate(f, dirs(f) && f.isDirectory && (f.listFiles forall visit)) - - dirs foreach visit - for ((f, true) <- isEmpty) f.delete - } - - /** Deletes each file or directory (recursively) in `files`.*/ - def delete(files: Iterable[File]): Unit = files.foreach(delete) - - /** Deletes each file or directory in `files` recursively. Any empty parent directories are deleted, recursively.*/ - def deleteFilesEmptyDirs(files: Iterable[File]): Unit = - { - def isEmptyDirectory(dir: File) = dir.isDirectory && listFiles(dir).isEmpty - def parents(fs: Set[File]) = fs flatMap { f => Option(f.getParentFile) } - def deleteEmpty(dirs: Set[File]): Unit = { - val empty = dirs filter isEmptyDirectory - if (empty.nonEmpty) // looks funny, but this is true if at least one of `dirs` is an empty directory - { - empty foreach { _.delete() } - deleteEmpty(parents(empty)) - } - } - - delete(files) - deleteEmpty(parents(files.toSet)) - } - - /** Deletes `file`, recursively if it is a directory. */ - def delete(file: File): Unit = { - translate("Error deleting file " + file + ": ") { - val deleted = file.delete() - if (!deleted && file.isDirectory) { - delete(listFiles(file)) - file.delete - } - } - } - - /** Returns the children of directory `dir` that match `filter` in a non-null array.*/ - def listFiles(filter: java.io.FileFilter)(dir: File): Array[File] = wrapNull(dir.listFiles(filter)) - - /** Returns the children of directory `dir` that match `filter` in a non-null array.*/ - def listFiles(dir: File, filter: java.io.FileFilter): Array[File] = wrapNull(dir.listFiles(filter)) - - /** Returns the children of directory `dir` in a non-null array.*/ - def listFiles(dir: File): Array[File] = wrapNull(dir.listFiles()) - - private[sbt] def wrapNull(a: Array[File]) = - if (a == null) - new Array[File](0) - else - a - - /** - * Creates a jar file. - * @param sources The files to include in the jar file paired with the entry name in the jar. Only the pairs explicitly listed are included. - * @param outputJar The file to write the jar to. - * @param manifest The manifest for the jar. - */ - def jar(sources: Traversable[(File, String)], outputJar: File, manifest: Manifest): Unit = - archive(sources.toSeq, outputJar, Some(manifest)) - - /** - * Creates a zip file. - * @param sources The files to include in the zip file paired with the entry name in the zip. Only the pairs explicitly listed are included. - * @param outputZip The file to write the zip to. - */ - def zip(sources: Traversable[(File, String)], outputZip: File): Unit = - archive(sources.toSeq, outputZip, None) - - private def archive(sources: Seq[(File, String)], outputFile: File, manifest: Option[Manifest]): Unit = { - if (outputFile.isDirectory) - sys.error("Specified output file " + outputFile + " is a directory.") - else { - val outputDir = outputFile.getParentFile - createDirectory(outputDir) - withZipOutput(outputFile, manifest) { output => - val createEntry: (String => ZipEntry) = if (manifest.isDefined) new JarEntry(_) else new ZipEntry(_) - writeZip(sources, output)(createEntry) - } - } - } - private def writeZip(sources: Seq[(File, String)], output: ZipOutputStream)(createEntry: String => ZipEntry): Unit = { - val files = sources.flatMap { case (file, name) => if (file.isFile) (file, normalizeName(name)) :: Nil else Nil } - - val now = System.currentTimeMillis - // The CRC32 for an empty value, needed to store directories in zip files - val emptyCRC = new CRC32().getValue() - - def addDirectoryEntry(name: String): Unit = { - output putNextEntry makeDirectoryEntry(name) - output.closeEntry() - } - - def makeDirectoryEntry(name: String) = - { - // log.debug("\tAdding directory " + relativePath + " ...") - val e = createEntry(name) - e setTime now - e setSize 0 - e setMethod ZipEntry.STORED - e setCrc emptyCRC - e - } - - def makeFileEntry(file: File, name: String) = - { - // log.debug("\tAdding " + file + " as " + name + " ...") - val e = createEntry(name) - e setTime file.lastModified - e - } - def addFileEntry(file: File, name: String): Unit = { - output putNextEntry makeFileEntry(file, name) - transfer(file, output) - output.closeEntry() - } - - //Calculate directories and add them to the generated Zip - allDirectoryPaths(files) foreach addDirectoryEntry - - //Add all files to the generated Zip - files foreach { case (file, name) => addFileEntry(file, name) } - } - - // map a path a/b/c to List("a", "b") - private def relativeComponents(path: String): List[String] = - path.split("/").toList.dropRight(1) - - // map components List("a", "b", "c") to List("a/b/c/", "a/b/", "a/", "") - private def directories(path: List[String]): List[String] = - path.foldLeft(List(""))((e, l) => (e.head + l + "/") :: e) - - // map a path a/b/c to List("a/b/", "a/") - private def directoryPaths(path: String): List[String] = - directories(relativeComponents(path)).filter(_.length > 1) - - // produce a sorted list of all the subdirectories of all provided files - private def allDirectoryPaths(files: Iterable[(File, String)]) = - TreeSet[String]() ++ (files flatMap { case (file, name) => directoryPaths(name) }) - - private def normalizeDirName(name: String) = - { - val norm1 = normalizeName(name) - if (norm1.endsWith("/")) norm1 else (norm1 + "/") - } - private def normalizeName(name: String) = - { - val sep = File.separatorChar - if (sep == '/') name else name.replace(sep, '/') - } - - private def withZipOutput(file: File, manifest: Option[Manifest])(f: ZipOutputStream => Unit): Unit = { - fileOutputStream(false)(file) { fileOut => - val (zipOut, ext) = - manifest match { - case Some(mf) => - { - import Attributes.Name.MANIFEST_VERSION - val main = mf.getMainAttributes - if (!main.containsKey(MANIFEST_VERSION)) - main.put(MANIFEST_VERSION, "1.0") - (new JarOutputStream(fileOut, mf), "jar") - } - case None => (new ZipOutputStream(fileOut), "zip") - } - try { f(zipOut) } - finally { zipOut.close } - } - } - - /** - * Returns the relative file for `file` relative to directory `base` or None if `base` is not a parent of `file`. - * If `file` or `base` are not absolute, they are first resolved against the current working directory. - */ - def relativizeFile(base: File, file: File): Option[File] = relativize(base, file).map { path => new File(path) } - - /** - * Returns the path for `file` relative to directory `base` or None if `base` is not a parent of `file`. - * If `file` or `base` are not absolute, they are first resolved against the current working directory. - */ - def relativize(base: File, file: File): Option[String] = - { - val pathString = file.getAbsolutePath - baseFileString(base) flatMap - { - baseString => - { - if (pathString.startsWith(baseString)) - Some(pathString.substring(baseString.length)) - else - None - } - } - } - private def baseFileString(baseFile: File): Option[String] = - { - if (baseFile.isDirectory) { - val cp = baseFile.getAbsolutePath - assert(cp.length > 0) - val normalized = if (cp.charAt(cp.length - 1) == File.separatorChar) cp else cp + File.separatorChar - Some(normalized) - } else - None - } - - /** - * For each pair in `sources`, copies the contents of the first File (the source) to the location of the second File (the target). - * - * A source file is always copied if `overwrite` is true. - * If `overwrite` is false, the source is only copied if the target is missing or is older than the source file according to last modified times. - * If the source is a directory, the corresponding directory is created. - * - * If `preserveLastModified` is `true`, the last modified times are transferred as well. - * Any parent directories that do not exist are created. - * The set of all target files is returned, whether or not they were updated by this method. - */ - def copy(sources: Traversable[(File, File)], overwrite: Boolean = false, preserveLastModified: Boolean = false): Set[File] = - sources.map(tupled(copyImpl(overwrite, preserveLastModified))).toSet - - private def copyImpl(overwrite: Boolean, preserveLastModified: Boolean)(from: File, to: File): File = - { - if (overwrite || !to.exists || from.lastModified > to.lastModified) { - if (from.isDirectory) - createDirectory(to) - else { - createDirectory(to.getParentFile) - copyFile(from, to, preserveLastModified) - } - } - to - } - - /** - * Copies the contents of each file in the `source` directory to the corresponding file in the `target` directory. - * A source file is always copied if `overwrite` is true. - * If `overwrite` is false, the source is only copied if the target is missing or is older than the source file according to last modified times. - * Files in `target` without a corresponding file in `source` are left unmodified in any case. - * If `preserveLastModified` is `true`, the last modified times are transferred as well. - * Any parent directories that do not exist are created. - */ - def copyDirectory(source: File, target: File, overwrite: Boolean = false, preserveLastModified: Boolean = false): Unit = - copy((PathFinder(source) ***) pair Path.rebase(source, target), overwrite, preserveLastModified) - - /** - * Copies the contents of `sourceFile` to the location of `targetFile`, overwriting any existing content. - * If `preserveLastModified` is `true`, the last modified time is transferred as well. - */ - def copyFile(sourceFile: File, targetFile: File, preserveLastModified: Boolean = false): Unit = { - // NOTE: when modifying this code, test with larger values of CopySpec.MaxFileSizeBits than default - - require(sourceFile.exists, "Source file '" + sourceFile.getAbsolutePath + "' does not exist.") - require(!sourceFile.isDirectory, "Source file '" + sourceFile.getAbsolutePath + "' is a directory.") - fileInputChannel(sourceFile) { in => - fileOutputChannel(targetFile) { out => - // maximum bytes per transfer according to from http://dzone.com/snippets/java-filecopy-using-nio - val max = (64 * 1024 * 1024) - (32 * 1024) - val total = in.size - def loop(offset: Long): Long = - if (offset < total) - loop(offset + out.transferFrom(in, offset, max)) - else - offset - val copied = loop(0) - if (copied != in.size) - sys.error("Could not copy '" + sourceFile + "' to '" + targetFile + "' (" + copied + "/" + in.size + " bytes copied)") - } - } - if (preserveLastModified) - copyLastModified(sourceFile, targetFile) - } - /** Transfers the last modified time of `sourceFile` to `targetFile`. */ - def copyLastModified(sourceFile: File, targetFile: File) = { - val last = sourceFile.lastModified - // lastModified can return a negative number, but setLastModified doesn't accept it - // see Java bug #6791812 - targetFile.setLastModified(math.max(last, 0L)) - } - /** The default Charset used when not specified: UTF-8. */ - def defaultCharset = utf8 - - /** - * Writes `content` to `file` using `charset` or UTF-8 if `charset` is not explicitly specified. - * If `append` is `false`, the existing contents of `file` are overwritten. - * If `append` is `true`, the new `content` is appended to the existing contents. - * If `file` or any parent directories do not exist, they are created. - */ - def write(file: File, content: String, charset: Charset = defaultCharset, append: Boolean = false): Unit = - writer(file, content, charset, append) { _.write(content) } - - def writer[T](file: File, content: String, charset: Charset, append: Boolean = false)(f: BufferedWriter => T): T = - if (charset.newEncoder.canEncode(content)) - fileWriter(charset, append)(file) { f } - else - sys.error("String cannot be encoded by charset " + charset.name) - - def reader[T](file: File, charset: Charset = defaultCharset)(f: BufferedReader => T): T = - fileReader(charset)(file) { f } - - /** Reads the full contents of `file` into a String using `charset` or UTF-8 if `charset` is not explicitly specified. */ - def read(file: File, charset: Charset = defaultCharset): String = - { - val out = new ByteArrayOutputStream(file.length.toInt) - transfer(file, out) - out.toString(charset.name) - } - - /** Reads the full contents of `in` into a byte array. This method does not close `in`.*/ - def readStream(in: InputStream, charset: Charset = defaultCharset): String = - { - val out = new ByteArrayOutputStream - transfer(in, out) - out.toString(charset.name) - } - - /** Reads the full contents of `in` into a byte array. */ - def readBytes(file: File): Array[Byte] = fileInputStream(file)(readBytes) - - /** Reads the full contents of `in` into a byte array. This method does not close `in`. */ - def readBytes(in: InputStream): Array[Byte] = - { - val out = new ByteArrayOutputStream - transfer(in, out) - out.toByteArray - } - - /** - * Appends `content` to the existing contents of `file` using `charset` or UTF-8 if `charset` is not explicitly specified. - * If `file` does not exist, it is created, as are any parent directories. - */ - def append(file: File, content: String, charset: Charset = defaultCharset): Unit = - write(file, content, charset, true) - - /** - * Appends `bytes` to the existing contents of `file`. - * If `file` does not exist, it is created, as are any parent directories. - */ - def append(file: File, bytes: Array[Byte]): Unit = - writeBytes(file, bytes, true) - - /** - * Writes `bytes` to `file`, overwriting any existing content. - * If any parent directories do not exist, they are first created. - */ - def write(file: File, bytes: Array[Byte]): Unit = - writeBytes(file, bytes, false) - - private def writeBytes(file: File, bytes: Array[Byte], append: Boolean): Unit = - fileOutputStream(append)(file) { _.write(bytes) } - - /** Reads all of the lines from `url` using the provided `charset` or UTF-8 if `charset` is not explicitly specified. */ - def readLinesURL(url: URL, charset: Charset = defaultCharset): List[String] = - urlReader(charset)(url)(readLines) - - /** Reads all of the lines in `file` using the provided `charset` or UTF-8 if `charset` is not explicitly specified. */ - def readLines(file: File, charset: Charset = defaultCharset): List[String] = - fileReader(charset)(file)(readLines) - - /** Reads all of the lines from `in`. This method does not close `in`.*/ - def readLines(in: BufferedReader): List[String] = - foldLines[List[String]](in, Nil)((accum, line) => line :: accum).reverse - - /** Applies `f` to each line read from `in`. This method does not close `in`.*/ - def foreachLine(in: BufferedReader)(f: String => Unit): Unit = - foldLines(in, ())((_, line) => f(line)) - - /** - * Applies `f` to each line read from `in` and the accumulated value of type `T`, with initial value `init`. - * This method does not close `in`. - */ - def foldLines[T](in: BufferedReader, init: T)(f: (T, String) => T): T = - { - def readLine(accum: T): T = - { - val line = in.readLine() - if (line eq null) accum else readLine(f(accum, line)) - } - readLine(init) - } - - /** - * Writes `lines` to `file` using the given `charset` or UTF-8 if `charset` is not explicitly specified. - * If `append` is `false`, the contents of the file are overwritten. - * If `append` is `true`, the lines are appended to the file. - * A newline is written after each line and NOT before the first line. - * If any parent directories of `file` do not exist, they are first created. - */ - def writeLines(file: File, lines: Seq[String], charset: Charset = defaultCharset, append: Boolean = false): Unit = - writer(file, lines.headOption.getOrElse(""), charset, append) { w => - lines.foreach { line => w.write(line); w.newLine() } - } - - /** Writes `lines` to `writer` using `writer`'s `println` method. */ - def writeLines(writer: PrintWriter, lines: Seq[String]): Unit = - lines foreach writer.println - - /** - * Writes `properties` to the File `to`, using `label` as the comment on the first line. - * If any parent directories of `to` do not exist, they are first created. - */ - def write(properties: Properties, label: String, to: File) = - fileOutputStream()(to) { output => properties.store(output, label) } - - /** Reads the properties in `from` into `properties`. If `from` does not exist, `properties` is left unchanged.*/ - def load(properties: Properties, from: File): Unit = - if (from.exists) - fileInputStream(from) { input => properties.load(input) } - - /** A pattern used to split a String by path separator characters.*/ - private val PathSeparatorPattern = java.util.regex.Pattern.compile(File.pathSeparator) - - /** Splits a String around the platform's path separator characters. */ - def pathSplit(s: String) = PathSeparatorPattern.split(s) - - /** - * Move the provided files to a temporary location. - * If 'f' returns normally, delete the files. - * If 'f' throws an Exception, return the files to their original location. - */ - def stash[T](files: Set[File])(f: => T): T = - withTemporaryDirectory { dir => - val stashed = stashLocations(dir, files.toArray) - move(stashed) - - try { f } catch { - case e: Exception => - try { move(stashed.map(_.swap)); throw e } - catch { case _: Exception => throw e } - } - } - - private def stashLocations(dir: File, files: Array[File]) = - for ((file, index) <- files.zipWithIndex) yield (file, new File(dir, index.toHexString)) - - // TODO: the reference to the other move overload does not resolve, probably due to a scaladoc bug - /** - * For each pair in `files`, moves the contents of the first File to the location of the second. - * See [[move(File,File)]] for the behavior of the individual move operations. - */ - def move(files: Traversable[(File, File)]): Unit = - files.foreach(Function.tupled(move)) - - /** - * Moves the contents of `a` to the location specified by `b`. - * This method deletes any content already at `b` and creates any parent directories of `b` if they do not exist. - * It will first try `File.renameTo` and if that fails, resort to copying and then deleting the original file. - * In either case, the original File will not exist on successful completion of this method. - */ - def move(a: File, b: File): Unit = - { - if (b.exists) - delete(b) - createDirectory(b.getParentFile) - if (!a.renameTo(b)) { - copyFile(a, b, true) - delete(a) - } - } - - /** - * Applies `f` to a buffered gzip `OutputStream` for `file`. - * The streams involved are opened before calling `f` and closed after it returns. - * The result is the result of `f`. - */ - def gzipFileOut[T](file: File)(f: OutputStream => T): T = - Using.fileOutputStream()(file) { fout => - Using.gzipOutputStream(fout) { outg => - Using.bufferedOutputStream(outg)(f) - } - } - - /** - * Applies `f` to a buffered gzip `InputStream` for `file`. - * The streams involved are opened before calling `f` and closed after it returns. - * The result is the result of `f`. - */ - def gzipFileIn[T](file: File)(f: InputStream => T): T = - Using.fileInputStream(file) { fin => - Using.gzipInputStream(fin) { ing => - Using.bufferedInputStream(ing)(f) - } - } - - /** - * Converts an absolute File to a URI. The File is converted to a URI (toURI), - * normalized (normalize), encoded (toASCIIString), and a forward slash ('/') is appended to the path component if - * it does not already end with a slash. - */ - def directoryURI(dir: File): URI = - { - assertAbsolute(dir) - directoryURI(dir.toURI.normalize) - } - - /** - * Converts an absolute File to a URI. The File is converted to a URI (toURI), - * normalized (normalize), encoded (toASCIIString), and a forward slash ('/') is appended to the path component if - * it does not already end with a slash. - */ - def directoryURI(uri: URI): URI = - { - if (!uri.isAbsolute) return uri; //assertAbsolute(uri) - val str = uri.toASCIIString - val dirStr = if (str.endsWith("/") || uri.getScheme != FileScheme || Option(uri.getRawFragment).isDefined) str else str + "/" - (new URI(dirStr)).normalize - } - /** Converts the given File to a URI. If the File is relative, the URI is relative, unlike File.toURI*/ - def toURI(f: File): URI = - // need to use the three argument URI constructor because the single argument version doesn't encode - if (f.isAbsolute) f.toURI else new URI(null, normalizeName(f.getPath), null) - - /** - * Resolves `f` against `base`, which must be an absolute directory. - * The result is guaranteed to be absolute. - * If `f` is absolute, it is returned without changes. - */ - def resolve(base: File, f: File): File = - { - assertAbsolute(base) - val fabs = if (f.isAbsolute) f else new File(directoryURI(new File(base, f.getPath))) - assertAbsolute(fabs) - fabs - } - def assertAbsolute(f: File) = assert(f.isAbsolute, "Not absolute: " + f) - def assertAbsolute(uri: URI) = assert(uri.isAbsolute, "Not absolute: " + uri) - - /** Parses a classpath String into File entries according to the current platform's path separator.*/ - def parseClasspath(s: String): Seq[File] = IO.pathSplit(s).map(new File(_)).toSeq - - /** - * Constructs an `ObjectInputStream` on `wrapped` that uses `loader` to load classes. - * See also [[https://github.com/sbt/sbt/issues/136 issue 136]]. - */ - def objectInputStream(wrapped: InputStream, loader: ClassLoader): ObjectInputStream = new ObjectInputStream(wrapped) { - override def resolveClass(osc: ObjectStreamClass): Class[_] = { - val c = Class.forName(osc.getName, false, loader) - if (c eq null) super.resolveClass(osc) else c - } - } -} diff --git a/util/io/src/main/scala/sbt/NameFilter.scala b/util/io/src/main/scala/sbt/NameFilter.scala deleted file mode 100644 index b8fe9f625..000000000 --- a/util/io/src/main/scala/sbt/NameFilter.scala +++ /dev/null @@ -1,119 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import java.io.File -import java.util.regex.Pattern - -/** A `java.io.FileFilter` with additional methods for combining filters. */ -trait FileFilter extends java.io.FileFilter with NotNull { - /** Constructs a filter that accepts a `File` if it matches either this filter or the given `filter`. */ - def ||(filter: FileFilter): FileFilter = new SimpleFileFilter(file => accept(file) || filter.accept(file)) - - /** Constructs a filter that accepts a `File` if it matches both this filter and the given `filter`. */ - def &&(filter: FileFilter): FileFilter = new SimpleFileFilter(file => accept(file) && filter.accept(file)) - - /** Constructs a filter that accepts a `File` if it matches this filter but does not match the given `filter`. */ - def --(filter: FileFilter): FileFilter = new SimpleFileFilter(file => accept(file) && !filter.accept(file)) - - /** Constructs a filter that accepts a `File` if it does not match this filter. */ - def unary_- : FileFilter = new SimpleFileFilter(file => !accept(file)) -} - -/** A filter on Strings. This also functions as a [[FileFilter]] by applying the String filter to the value of a File's `getName`. */ -trait NameFilter extends FileFilter with NotNull { - /** Returns `true` to include the `name`, `false` to exclude it. */ - def accept(name: String): Boolean - - /** Accepts `File` if its `getName` method is accepted by this filter. */ - final def accept(file: File): Boolean = accept(file.getName) - - /** Constructs a filter that accepts a `String` if it matches either this filter or the given `filter`. */ - def |(filter: NameFilter): NameFilter = new SimpleFilter(name => accept(name) || filter.accept(name)) - - /** Constructs a filter that accepts a `String` if it matches both this filter and the given `filter`. */ - def &(filter: NameFilter): NameFilter = new SimpleFilter(name => accept(name) && filter.accept(name)) - - /** Constructs a filter that accepts a `String` if it matches this filter but not the given `filter`. */ - def -(filter: NameFilter): NameFilter = new SimpleFilter(name => accept(name) && !filter.accept(name)) - - /** Constructs a filter that accepts a `String` if it does not match this filter. */ - override def unary_- : NameFilter = new SimpleFilter(name => !accept(name)) -} - -/** A [[FileFilter]] that selects files that are hidden according to `java.io.File.isHidden` or if they start with a dot (`.`). */ -object HiddenFileFilter extends FileFilter { - def accept(file: File) = file.isHidden && file.getName != "." -} - -/** A [[FileFilter]] that selects files that exist according to `java.io.File.exists`. */ -object ExistsFileFilter extends FileFilter { - def accept(file: File) = file.exists -} - -/** A [[FileFilter]] that selects files that are a directory according to `java.io.File.isDirectory`. */ -object DirectoryFilter extends FileFilter { - def accept(file: File) = file.isDirectory -} - -/** A [[FileFilter]] that selects files according the predicate `acceptFunction`. */ -final class SimpleFileFilter(val acceptFunction: File => Boolean) extends FileFilter { - def accept(file: File) = acceptFunction(file) -} - -/** A [[NameFilter]] that accepts a name if it is exactly equal to `matchName`. */ -final class ExactFilter(val matchName: String) extends NameFilter { - def accept(name: String) = matchName == name -} - -/** A [[NameFilter]] that accepts a name if the predicate `acceptFunction` accepts it. */ -final class SimpleFilter(val acceptFunction: String => Boolean) extends NameFilter { - def accept(name: String) = acceptFunction(name) -} - -/** A [[NameFilter]] that accepts a name if it matches the regular expression defined by `pattern`. */ -final class PatternFilter(val pattern: Pattern) extends NameFilter { - def accept(name: String) = pattern.matcher(name).matches -} - -/** A [[NameFilter]] that accepts all names. That is, `accept` always returns `true`. */ -object AllPassFilter extends NameFilter { - def accept(name: String) = true -} - -/** A [[NameFilter]] that accepts nothing. That is, `accept` always returns `false`. */ -object NothingFilter extends NameFilter { - def accept(name: String) = false -} - -object NameFilter { - implicit def fnToNameFilter(f: String => Boolean): NameFilter = new NameFilter { - def accept(name: String) = f(name) - } -} -object FileFilter { - /** Allows a String to be used where a `NameFilter` is expected and any asterisks (`*`) will be interpreted as wildcards. See [[sbt.GlobFilter]].*/ - implicit def globFilter(s: String): NameFilter = GlobFilter(s) -} - -/** Constructs a filter from a String, interpreting wildcards. See the [[apply]] method. */ -object GlobFilter { - /** - * Constructs a [[NameFilter]] from a String, interpreting `*` as a wildcard. - * Control characters, as determined by `java.lang.Character.isISOControl` are not allowed - * due to the implementation restriction of using Java's `Pattern` and `Pattern.quote`, - * which do not handle these characters. - */ - def apply(expression: String): NameFilter = - { - require(!expression.exists(java.lang.Character.isISOControl), "Control characters not allowed in filter expression.") - if (expression == "*") - AllPassFilter - else if (expression.indexOf('*') < 0) // includes case where expression is empty - new ExactFilter(expression) - else - new PatternFilter(Pattern.compile(expression.split("\\*", -1).map(quote).mkString(".*"))) - } - private def quote(s: String) = if (s.isEmpty) "" else Pattern.quote(s.replaceAll("\n", """\n""")) -} \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/Pack.scala b/util/io/src/main/scala/sbt/Pack.scala deleted file mode 100644 index ba7796627..000000000 --- a/util/io/src/main/scala/sbt/Pack.scala +++ /dev/null @@ -1,73 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.io.{ File, FileOutputStream } -import java.util.jar.{ JarEntry, JarFile, JarOutputStream, Pack200 } -import IO._ - -object Pack { - def pack(jarPath: File, out: File): Unit = pack(jarPath, out, defaultPackerOptions) - def pack(jarPath: File, out: File, options: Iterable[(String, String)]): Unit = { - val packer = Pack200.newPacker - import collection.JavaConversions._ - packer.properties ++= options - - Using.jarFile(false)(jarPath) { f => - Using.fileOutputStream()(out) { stream => - packer.pack(f, stream) - } - } - } - def unpack(packedPath: File, toJarPath: File): Unit = { - val unpacker = Pack200.newUnpacker - Using.fileOutputStream()(toJarPath) { fileStream => - Using.jarOutputStream(fileStream) { jarOut => - unpacker.unpack(packedPath, jarOut) - } - } - } - def defaultPackerOptions = scala.collection.immutable.Map() -} - -import java.net.URL -/** - * This is somewhat of a mess and is not entirely correct. jarsigner doesn't work properly - * on scalaz and it is difficult to determine whether a jar is both signed and valid. - */ -object SignJar { - final class SignOption private[SignJar] (val toList: List[String], val signOnly: Boolean) { - override def toString = toList.mkString(" ") - } - def keyStore(url: URL) = new SignOption("-keystore" :: url.toExternalForm :: Nil, true) - def signedJar(p: File) = new SignOption("-signedjar" :: p.getAbsolutePath :: Nil, true) - def verbose = new SignOption("-verbose" :: Nil, false) - def sigFile(name: String) = new SignOption("-sigfile" :: name :: Nil, true) - def storeType(t: String) = new SignOption("-storetype" :: t :: Nil, false) - def provider(p: String) = new SignOption("-provider" :: p :: Nil, false) - def providerName(p: String) = new SignOption("-providerName" :: p :: Nil, false) - def storePassword(p: String) = new SignOption("-storepass" :: p :: Nil, true) - def keyPassword(p: String) = new SignOption("-keypass" :: p :: Nil, true) - - private def VerifyOption = "-verify" - - /** Uses jarsigner to sign the given jar. */ - def sign(jarPath: File, alias: String, options: Seq[SignOption])(fork: (String, List[String]) => Int): Unit = { - require(!alias.trim.isEmpty, "Alias cannot be empty") - val arguments = options.toList.flatMap(_.toList) ::: jarPath.getAbsolutePath :: alias :: Nil - execute("signing", arguments)(fork) - } - /** Uses jarsigner to verify the given jar.*/ - def verify(jarPath: File, options: Seq[SignOption])(fork: (String, List[String]) => Int): Unit = { - val arguments = options.filter(!_.signOnly).toList.flatMap(_.toList) ::: VerifyOption :: jarPath.getAbsolutePath :: Nil - execute("verifying", arguments)(fork) - } - private def execute(action: String, arguments: List[String])(fork: (String, List[String]) => Int): Unit = { - val exitCode = fork(CommandName, arguments) - if (exitCode != 0) - sys.error("Error " + action + " jar (exit code was " + exitCode + ".)") - } - - private val CommandName = "jarsigner" -} diff --git a/util/io/src/main/scala/sbt/Path.scala b/util/io/src/main/scala/sbt/Path.scala deleted file mode 100644 index 8fa2a25ac..000000000 --- a/util/io/src/main/scala/sbt/Path.scala +++ /dev/null @@ -1,242 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import Path._ -import IO.{ pathSplit, wrapNull } -import java.io.File -import java.net.URL -import scala.collection.{ generic, immutable, mutable } - -final class RichFile(val asFile: File) { - def /(component: String): File = if (component == ".") asFile else new File(asFile, component) - /** True if and only if the wrapped file exists.*/ - def exists = asFile.exists - /** True if and only if the wrapped file is a directory.*/ - def isDirectory = asFile.isDirectory - /** The last modified time of the wrapped file.*/ - def lastModified = asFile.lastModified - /* True if and only if the wrapped file `asFile` exists and the file 'other' - * does not exist or was modified before the `asFile`.*/ - def newerThan(other: File): Boolean = Path.newerThan(asFile, other) - /* True if and only if the wrapped file `asFile` does not exist or the file `other` - * exists and was modified after `asFile`.*/ - def olderThan(other: File): Boolean = Path.newerThan(other, asFile) - /** The wrapped file converted to a URL.*/ - def asURL = asFile.toURI.toURL - def absolutePath: String = asFile.getAbsolutePath - - /** The last component of this path.*/ - def name = asFile.getName - /** The extension part of the name of this path. This is the part of the name after the last period, or the empty string if there is no period.*/ - def ext = baseAndExt._2 - /** The base of the name of this path. This is the part of the name before the last period, or the full name if there is no period.*/ - def base = baseAndExt._1 - def baseAndExt: (String, String) = - { - val nme = name - val dot = nme.lastIndexOf('.') - if (dot < 0) (nme, "") else (nme.substring(0, dot), nme.substring(dot + 1)) - } - - def relativize(sub: File): Option[File] = Path.relativizeFile(asFile, sub) - def relativeTo(base: File): Option[File] = Path.relativizeFile(base, asFile) - - def hash: Array[Byte] = Hash(asFile) - def hashString: String = Hash.toHex(hash) - def hashStringHalf: String = Hash.halve(hashString) -} -import java.io.File -import File.pathSeparator -trait PathLow { - implicit def singleFileFinder(file: File): PathFinder = PathFinder(file) -} -trait PathExtra extends Alternatives with Mapper with PathLow { - implicit def richFile(file: File): RichFile = new RichFile(file) - implicit def filesToFinder(cc: Traversable[File]): PathFinder = PathFinder.strict(cc) -} -object Path extends PathExtra { - def apply(f: File): RichFile = new RichFile(f) - def apply(f: String): RichFile = new RichFile(new File(f)) - def fileProperty(name: String): File = new File(System.getProperty(name)) - def userHome: File = fileProperty("user.home") - - def absolute(file: File): File = new File(file.toURI.normalize).getAbsoluteFile - def makeString(paths: Seq[File]): String = makeString(paths, pathSeparator) - def makeString(paths: Seq[File], sep: String): String = { - val separated = paths.map(_.getAbsolutePath) - separated.find(_ contains sep).foreach(p => sys.error(s"Path '$p' contains separator '$sep'")) - separated.mkString(sep) - } - def newerThan(a: File, b: File): Boolean = a.exists && (!b.exists || a.lastModified > b.lastModified) - - /** The separator character of the platform.*/ - val sep = java.io.File.separatorChar - - @deprecated("Use IO.relativizeFile", "0.13.1") - def relativizeFile(baseFile: File, file: File): Option[File] = IO.relativizeFile(baseFile, file) - - def toURLs(files: Seq[File]): Array[URL] = files.map(_.toURI.toURL).toArray -} -object PathFinder { - /** A PathFinder that always produces the empty set of Paths.*/ - val empty = new PathFinder { private[sbt] def addTo(fileSet: mutable.Set[File]) {} } - def strict(files: Traversable[File]): PathFinder = apply(files) - def apply(files: => Traversable[File]): PathFinder = new PathFinder { - private[sbt] def addTo(fileSet: mutable.Set[File]) = fileSet ++= files - } - def apply(file: File): PathFinder = new SingleFile(file) -} - -/** - * A path finder constructs a set of paths. The set is evaluated by a call to the get - * method. The set will be different for different calls to get if the underlying filesystem - * has changed. - */ -sealed abstract class PathFinder { - /** The union of the paths found by this PathFinder with the paths found by 'paths'.*/ - def +++(paths: PathFinder): PathFinder = new Paths(this, paths) - /** Excludes all paths from excludePaths from the paths selected by this PathFinder.*/ - def ---(excludePaths: PathFinder): PathFinder = new ExcludeFiles(this, excludePaths) - /** - * Constructs a new finder that selects all paths with a name that matches filter and are - * descendants of paths selected by this finder. - */ - def **(filter: FileFilter): PathFinder = new DescendantOrSelfPathFinder(this, filter) - def *** : PathFinder = **(AllPassFilter) - /** - * Constructs a new finder that selects all paths with a name that matches filter and are - * immediate children of paths selected by this finder. - */ - def *(filter: FileFilter): PathFinder = new ChildPathFinder(this, filter) - /** - * Constructs a new finder that selects all paths with name literal that are immediate children - * of paths selected by this finder. - */ - def /(literal: String): PathFinder = new ChildPathFinder(this, new ExactFilter(literal)) - /** - * Constructs a new finder that selects all paths with name literal that are immediate children - * of paths selected by this finder. - */ - final def \(literal: String): PathFinder = this / literal - - @deprecated("Use pair.", "0.13.1") - def x_![T](mapper: File => Option[T]): Traversable[(File, T)] = pair(mapper, false) - - /** - * Applies `mapper` to each path selected by this PathFinder and returns the path paired with the non-empty result. - * If the result is empty (None) and `errorIfNone` is true, an exception is thrown. - * If `errorIfNone` is false, the path is dropped from the returned Traversable. - */ - def pair[T](mapper: File => Option[T], errorIfNone: Boolean = true): Seq[(File, T)] = - { - val apply = if (errorIfNone) mapper | fail else mapper - for (file <- get; mapped <- apply(file)) yield (file, mapped) - } - - @deprecated("Use pair.", "0.13.1") - def x[T](mapper: File => Option[T], errorIfNone: Boolean = true): Seq[(File, T)] = pair(mapper, errorIfNone) - - /** - * Selects all descendant paths with a name that matches include and do not have an intermediate - * path with a name that matches intermediateExclude. Typical usage is: - * - * descendantsExcept("*.jar", ".svn") - */ - def descendantsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder = - (this ** include) --- (this ** intermediateExclude ** include) - - /** - * Evaluates this finder and converts the results to a `Seq` of distinct `File`s. The files returned by this method will reflect the underlying filesystem at the - * time of calling. If the filesystem changes, two calls to this method might be different. - */ - final def get: Seq[File] = - { - import collection.JavaConversions._ - val pathSet: mutable.Set[File] = new java.util.LinkedHashSet[File] - addTo(pathSet) - pathSet.toSeq - } - - /** Only keeps paths for which `f` returns true. It is non-strict, so it is not evaluated until the returned finder is evaluated.*/ - final def filter(f: File => Boolean): PathFinder = PathFinder(get filter f) - /* Non-strict flatMap: no evaluation occurs until the returned finder is evaluated.*/ - final def flatMap(f: File => PathFinder): PathFinder = PathFinder(get.flatMap(p => f(p).get)) - /** Evaluates this finder and converts the results to an `Array` of `URL`s..*/ - final def getURLs: Array[URL] = get.toArray.map(_.toURI.toURL) - /** Evaluates this finder and converts the results to a distinct sequence of absolute path strings.*/ - final def getPaths: Seq[String] = get.map(_.absolutePath) - private[sbt] def addTo(fileSet: mutable.Set[File]) - - /** - * Create a PathFinder from this one where each path has a unique name. - * A single path is arbitrarily selected from the set of paths with the same name. - */ - def distinctName: PathFinder = PathFinder { get.map(p => (p.asFile.getName, p)).toMap.values } - - /** - * Same as distinctName. - */ - @deprecated("Use distinctName", "0.13.9") - def distinct: PathFinder = distinctName - - /** - * Create a PathFinder from this one where each path has a unique absolute path. - */ - def distinctPath: PathFinder = PathFinder { get.map(p => (p.absolutePath, p)).toMap.values } - - /** Constructs a string by evaluating this finder, converting the resulting Paths to absolute path strings, and joining them with the platform path separator.*/ - final def absString = Path.makeString(get) - /** Constructs a debugging string for this finder by evaluating it and separating paths by newlines.*/ - override def toString = get.mkString("\n ", "\n ", "") -} -private class SingleFile(asFile: File) extends PathFinder { - private[sbt] def addTo(fileSet: mutable.Set[File]): Unit = if (asFile.exists) fileSet += asFile -} -private abstract class FilterFiles extends PathFinder with FileFilter { - def parent: PathFinder - def filter: FileFilter - final def accept(file: File) = filter.accept(file) - - protected def handleFile(file: File, fileSet: mutable.Set[File]): Unit = - for (matchedFile <- wrapNull(file.listFiles(this))) - fileSet += new File(file, matchedFile.getName) -} -private class DescendantOrSelfPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterFiles { - private[sbt] def addTo(fileSet: mutable.Set[File]) { - for (file <- parent.get) { - if (accept(file)) - fileSet += file - handleFileDescendant(file, fileSet) - } - } - private def handleFileDescendant(file: File, fileSet: mutable.Set[File]) { - handleFile(file, fileSet) - for (childDirectory <- wrapNull(file listFiles DirectoryFilter)) - handleFileDescendant(new File(file, childDirectory.getName), fileSet) - } -} -private class ChildPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterFiles { - private[sbt] def addTo(fileSet: mutable.Set[File]): Unit = - for (file <- parent.get) - handleFile(file, fileSet) -} -private class Paths(a: PathFinder, b: PathFinder) extends PathFinder { - private[sbt] def addTo(fileSet: mutable.Set[File]) { - a.addTo(fileSet) - b.addTo(fileSet) - } -} -private class ExcludeFiles(include: PathFinder, exclude: PathFinder) extends PathFinder { - private[sbt] def addTo(pathSet: mutable.Set[File]) { - val includeSet = new mutable.LinkedHashSet[File] - include.addTo(includeSet) - - val excludeSet = new mutable.HashSet[File] - exclude.addTo(excludeSet) - - includeSet --= excludeSet - pathSet ++= includeSet - } -} diff --git a/util/io/src/main/scala/sbt/PathMapper.scala b/util/io/src/main/scala/sbt/PathMapper.scala deleted file mode 100644 index 4db009184..000000000 --- a/util/io/src/main/scala/sbt/PathMapper.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import java.io.File - -trait Mapper { - type PathMap = File => Option[String] - type FileMap = File => Option[File] - - /** A path mapper that pairs a File with the path returned by calling `getPath` on it.*/ - val basic: PathMap = f => Some(f.getPath) - - /** - * A path mapper that pairs a File with its path relative to `base`. - * If the File is not a descendant of `base`, it is not handled (None is returned by the mapper). - */ - def relativeTo(base: File): PathMap = IO.relativize(base, _) - - def relativeTo(bases: Iterable[File], zero: PathMap = transparent): PathMap = fold(zero, bases)(relativeTo) - - /** - * A path mapper that pairs a descendent of `oldBase` with `newBase` prepended to the path relative to `oldBase`. - * For example, if `oldBase = /old/x/` and `newBase = new/a/`, then `/old/x/y/z.txt` gets paired with `new/a/y/z.txt`. - */ - def rebase(oldBase: File, newBase: String): PathMap = - { - val normNewBase = normalizeBase(newBase) - (file: File) => - if (file == oldBase) - Some(if (normNewBase.isEmpty) "." else normNewBase) - else - IO.relativize(oldBase, file).map(normNewBase + _) - } - /** A mapper that throws an exception for any input. This is useful as the last mapper in a pipeline to ensure every input gets mapped.*/ - def fail: Any => Nothing = f => sys.error("No mapping for " + f) - - /** A path mapper that pairs a File with its name. For example, `/x/y/z.txt` gets paired with `z.txt`.*/ - val flat: PathMap = f => Some(f.getName) - - /** - * A path mapper that pairs a File with a path constructed from `newBase` and the file's name. - * For example, if `newBase = /new/a/`, then `/old/x/z.txt` gets paired with `/new/a/z.txt`. - */ - def flatRebase(newBase: String): PathMap = - { - val newBase0 = normalizeBase(newBase) - f => Some(newBase0 + f.getName) - } - - /** A mapper that is defined on all inputs by the function `f`.*/ - def total[A, B](f: A => B): A => Some[B] = x => Some(f(x)) - - /** A mapper that ignores all inputs.*/ - def transparent: Any => Option[Nothing] = _ => None - - def normalizeBase(base: String) = if (!base.isEmpty && !base.endsWith("/")) base + "/" else base - - /** - * Pairs a File with the absolute File obtained by calling `getAbsoluteFile`. - * Note that this usually means that relative files are resolved against the current working directory. - */ - def abs: FileMap = f => Some(f.getAbsoluteFile) - - /** - * Returns a File mapper that resolves a relative File against `newDirectory` and pairs the original File with the resolved File. - * The mapper ignores absolute files. - */ - def resolve(newDirectory: File): FileMap = file => if (file.isAbsolute) None else Some(new File(newDirectory, file.getPath)) - - def rebase(oldBases: Iterable[File], newBase: File, zero: FileMap = transparent): FileMap = - fold(zero, oldBases)(old => rebase(old, newBase)) - - /** - * Produces a File mapper that pairs a descendant of `oldBase` with a file in `newBase` that preserving the relative path of the original file against `oldBase`. - * For example, if `oldBase` is `/old/x/` and `newBase` is `/new/a/`, `/old/x/y/z.txt` gets paired with `/new/a/y/z.txt`. - */ - def rebase(oldBase: File, newBase: File): FileMap = - file => - if (file == oldBase) - Some(newBase) - else - IO.relativize(oldBase, file) map { r => new File(newBase, r) } - - /** - * Constructs a File mapper that pairs a file with a file with the same name in `newDirectory`. - * For example, if `newDirectory` is `/a/b`, then `/r/s/t/d.txt` will be paired with `/a/b/d.txt` - */ - def flat(newDirectory: File): FileMap = file => Some(new File(newDirectory, file.getName)) - - import Alternatives._ - - /** - * Selects all descendants of `base` directory and maps them to a path relative to `base`. - * `base` itself is not included. - */ - def allSubpaths(base: File): Traversable[(File, String)] = - selectSubpaths(base, AllPassFilter) - - /** - * Selects descendants of `base` directory matching `filter` and maps them to a path relative to `base`. - * `base` itself is not included. - */ - def selectSubpaths(base: File, filter: FileFilter): Traversable[(File, String)] = - (PathFinder(base) ** filter --- PathFinder(base)) pair (relativeTo(base) | flat) - - private[this] def fold[A, B, T](zero: A => Option[B], in: Iterable[T])(f: T => A => Option[B]): A => Option[B] = - (zero /: in)((mapper, base) => f(base) | mapper) -} - -trait Alternative[A, B] { def |(g: A => Option[B]): A => Option[B] } -trait Alternatives { - implicit def alternative[A, B](f: A => Option[B]): Alternative[A, B] = - new Alternative[A, B] { - def |(g: A => Option[B]) = - (a: A) => f(a) orElse g(a) - } - final def alternatives[A, B](alts: Seq[A => Option[B]]): A => Option[B] = - alts match { - case Seq(f, fs @ _*) => f | alternatives(fs) - case Seq() => a => None - } -} -object Alternatives extends Alternatives \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/Resources.scala b/util/io/src/main/scala/sbt/Resources.scala deleted file mode 100644 index 1b1e062ad..000000000 --- a/util/io/src/main/scala/sbt/Resources.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import java.io.File -import IO._ -import Resources.error - -object Resources { - def apply(basePath: String) = - { - require(basePath.startsWith("/")) - val resource = getClass.getResource(basePath) - if (resource == null) - error("Resource base directory '" + basePath + "' not on classpath.") - else { - val file = toFile(resource) - if (file.exists) - new Resources(file) - else - error("Resource base directory '" + basePath + "' does not exist.") - } - } - def error(msg: String) = throw new ResourcesException(msg) - private val LoadErrorPrefix = "Error loading initial project: " -} -class ResourcesException(msg: String) extends Exception(msg) - -class Resources(val baseDirectory: File) { - import Resources._ - // The returned directory is not actually read-only, but it should be treated that way - def readOnlyResourceDirectory(group: String, name: String): File = - { - val groupDirectory = new File(baseDirectory, group) - if (groupDirectory.isDirectory) { - val resourceDirectory = new File(groupDirectory, name) - if (resourceDirectory.isDirectory) - resourceDirectory - else - error("Resource directory '" + name + "' in group '" + group + "' not found.") - } else - error("Group '" + group + "' not found.") - } - def readWriteResourceDirectory[T](group: String, name: String)(withDirectory: File => T): T = - { - val file = readOnlyResourceDirectory(group, name) - readWriteResourceDirectory(file)(withDirectory) - } - - def readWriteResourceDirectory[T](readOnly: File)(withDirectory: File => T): T = - { - require(readOnly.isDirectory) - def readWrite(readOnly: File)(temporary: File): T = - { - val readWriteDirectory = new File(temporary, readOnly.getName) - copyDirectory(readOnly, readWriteDirectory) - withDirectory(readWriteDirectory) - } - withTemporaryDirectory(readWrite(readOnly)) - } -} \ No newline at end of file diff --git a/util/io/src/main/scala/sbt/RichURI.scala b/util/io/src/main/scala/sbt/RichURI.scala deleted file mode 100644 index 46a1c3729..000000000 --- a/util/io/src/main/scala/sbt/RichURI.scala +++ /dev/null @@ -1,54 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2011 Sanjin Sehic - */ - -package sbt - -import java.net.URI - -/** Extends `URI` with additional convenience methods. */ -class RichURI(uri: URI) { - /** - * Provides a case-class-like `copy` method for URI. - * Note that this method simply passes the individual components of this URI to the URI constructor - * that accepts each component individually. It is thus limited by the implementation restrictions of the relevant methods. - */ - def copy(scheme: String = uri.getScheme, userInfo: String = uri.getUserInfo, - host: String = uri.getHost, port: Int = uri.getPort, path: String = uri.getPath, - query: String = uri.getQuery, fragment: String = uri.getFragment) = - new URI(scheme, userInfo, host, port, path, query, fragment) - - /** Returns `true` if the fragment of the URI is defined. */ - def hasFragment = uri.getFragment ne null - - /** Returns a copy of the URI without the fragment. */ - def withoutFragment = - if (hasFragment) - new URI(uri.getScheme, uri.getSchemeSpecificPart, null) - else - uri - - /** Returns `true` if the scheme specific part of the URI is also a valid URI. */ - def hasMarkerScheme = new URI(uri.getRawSchemeSpecificPart).getScheme ne null - - /** - * Strips the wrapper scheme from this URI. - * If the URI has a fragment, the fragment is transferred to the wrapped URI. - * If this URI does not have a marker scheme, it is returned unchanged. - */ - def withoutMarkerScheme = - { - if (hasMarkerScheme) - if (hasFragment) - new URI(uri.getRawSchemeSpecificPart + "#" + uri.getRawFragment) - else - new URI(uri.getRawSchemeSpecificPart) - else - uri - } -} - -object RichURI { - /** Provides additional convenience methods for `uri`. */ - implicit def fromURI(uri: URI): RichURI = new RichURI(uri) -} diff --git a/util/io/src/main/scala/sbt/SourceModificationWatch.scala b/util/io/src/main/scala/sbt/SourceModificationWatch.scala deleted file mode 100644 index e24452f63..000000000 --- a/util/io/src/main/scala/sbt/SourceModificationWatch.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mikko Peltonen, Stuart Roebuck, Mark Harrah - */ -package sbt - -import annotation.tailrec - -object SourceModificationWatch { - @tailrec def watch(sourcesFinder: PathFinder, pollDelayMillis: Int, state: WatchState)(terminationCondition: => Boolean): (Boolean, WatchState) = - { - import state._ - - val sourceFiles: Iterable[java.io.File] = sourcesFinder.get - val sourceFilesPath: Set[String] = sourceFiles.map(_.getCanonicalPath)(collection.breakOut) - val lastModifiedTime = - (0L /: sourceFiles) { (acc, file) => math.max(acc, file.lastModified) } - - val sourcesModified = - lastModifiedTime > lastCallbackCallTime || - previousFiles != sourceFilesPath - - val (triggered, newCallbackCallTime) = - if (sourcesModified) - (false, System.currentTimeMillis) - else - (awaitingQuietPeriod, lastCallbackCallTime) - - val newState = new WatchState(newCallbackCallTime, sourceFilesPath, sourcesModified, if (triggered) count + 1 else count) - if (triggered) - (true, newState) - else { - Thread.sleep(pollDelayMillis) - if (terminationCondition) - (false, newState) - else - watch(sourcesFinder, pollDelayMillis, newState)(terminationCondition) - } - } -} -final class WatchState(val lastCallbackCallTime: Long, val previousFiles: Set[String], val awaitingQuietPeriod: Boolean, val count: Int) { - - def previousFileCount: Int = previousFiles.size - - @deprecated("Use another constructor", "0.13.6") - def this(lastCallbackCallTime: Long, previousFileCount: Int, awaitingQuietPeriod: Boolean, count: Int) { - this(lastCallbackCallTime, Set.empty[String], awaitingQuietPeriod, count) - } -} - -object WatchState { - def empty = new WatchState(0L, Set.empty[String], false, 0) -} diff --git a/util/io/src/main/scala/sbt/Using.scala b/util/io/src/main/scala/sbt/Using.scala deleted file mode 100644 index eee2ce1ad..000000000 --- a/util/io/src/main/scala/sbt/Using.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import java.io.{ Closeable, File, FileInputStream, FileOutputStream, InputStream, OutputStream } -import java.io.{ BufferedInputStream, BufferedOutputStream, ByteArrayOutputStream, InputStreamReader, OutputStreamWriter } -import java.io.{ BufferedReader, BufferedWriter, FileReader, FileWriter, Reader, Writer } -import java.util.zip.{ GZIPInputStream, GZIPOutputStream } -import java.net.{ URL, URISyntaxException } -import java.nio.charset.{ Charset, CharsetDecoder, CharsetEncoder } -import java.nio.channels.FileChannel -import java.util.jar.{ Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest } -import java.util.zip.{ GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream } - -import ErrorHandling.translate -import Using._ - -abstract class Using[Source, T] { - protected def open(src: Source): T - def apply[R](src: Source)(f: T => R): R = - { - val resource = open(src) - try { f(resource) } - finally { close(resource) } - } - protected def close(out: T): Unit -} -import scala.reflect.{ Manifest => SManifest } -abstract class WrapUsing[Source, T](implicit srcMf: SManifest[Source], targetMf: SManifest[T]) extends Using[Source, T] { - protected def label[S](m: SManifest[S]) = m.runtimeClass.getSimpleName - protected def openImpl(source: Source): T - protected final def open(source: Source): T = - translate("Error wrapping " + label(srcMf) + " in " + label(targetMf) + ": ") { openImpl(source) } -} -trait OpenFile[T] extends Using[File, T] { - protected def openImpl(file: File): T - protected final def open(file: File): T = - { - val parent = file.getParentFile - if (parent != null) - IO.createDirectory(parent) - openImpl(file) - } -} -object Using { - def wrap[Source, T <: Closeable](openF: Source => T)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source, T] = - wrap(openF, closeCloseable) - def wrap[Source, T](openF: Source => T, closeF: T => Unit)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source, T] = - new WrapUsing[Source, T] { - def openImpl(source: Source) = openF(source) - def close(t: T) = closeF(t) - } - - def resource[Source, T <: Closeable](openF: Source => T): Using[Source, T] = - resource(openF, closeCloseable) - def resource[Source, T](openF: Source => T, closeF: T => Unit): Using[Source, T] = - new Using[Source, T] { - def open(s: Source) = openF(s) - def close(s: T) = closeF(s) - } - def file[T <: Closeable](openF: File => T): OpenFile[T] = file(openF, closeCloseable) - def file[T](openF: File => T, closeF: T => Unit): OpenFile[T] = - new OpenFile[T] { - def openImpl(file: File) = openF(file) - def close(t: T) = closeF(t) - } - private def closeCloseable[T <: Closeable]: T => Unit = _.close() - - def bufferedOutputStream = wrap((out: OutputStream) => new BufferedOutputStream(out)) - def bufferedInputStream = wrap((in: InputStream) => new BufferedInputStream(in)) - def fileOutputStream(append: Boolean = false) = file(f => new BufferedOutputStream(new FileOutputStream(f, append))) - def fileInputStream = file(f => new BufferedInputStream(new FileInputStream(f))) - def urlInputStream = resource((u: URL) => translate("Error opening " + u + ": ")(new BufferedInputStream(u.openStream))) - def fileOutputChannel = file(f => new FileOutputStream(f).getChannel) - def fileInputChannel = file(f => new FileInputStream(f).getChannel) - def fileWriter(charset: Charset = IO.utf8, append: Boolean = false) = - file(f => new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f, append), charset))) - def fileReader(charset: Charset) = file(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), charset))) - def urlReader(charset: Charset) = resource((u: URL) => new BufferedReader(new InputStreamReader(u.openStream, charset))) - def jarFile(verify: Boolean) = file(f => new JarFile(f, verify), (_: JarFile).close()) - def zipFile = file(f => new ZipFile(f), (_: ZipFile).close()) - def streamReader = wrap { (_: (InputStream, Charset)) match { case (in, charset) => new InputStreamReader(in, charset) } } - def gzipInputStream = wrap((in: InputStream) => new GZIPInputStream(in, 8192)) - def zipInputStream = wrap((in: InputStream) => new ZipInputStream(in)) - def zipOutputStream = wrap((out: OutputStream) => new ZipOutputStream(out)) - def gzipOutputStream = wrap((out: OutputStream) => new GZIPOutputStream(out, 8192), (_: GZIPOutputStream).finish()) - def jarOutputStream = wrap((out: OutputStream) => new JarOutputStream(out)) - def jarInputStream = wrap((in: InputStream) => new JarInputStream(in)) - def zipEntry(zip: ZipFile) = resource((entry: ZipEntry) => - translate("Error opening " + entry.getName + " in " + zip + ": ") { zip.getInputStream(entry) }) -} \ No newline at end of file diff --git a/util/io/src/main/scala/xsbt/IPC.scala b/util/io/src/main/scala/xsbt/IPC.scala deleted file mode 100644 index 47e2fb1a1..000000000 --- a/util/io/src/main/scala/xsbt/IPC.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt - -import java.io.{ BufferedReader, BufferedWriter, InputStream, InputStreamReader, OutputStreamWriter, OutputStream } -import java.net.{ InetAddress, ServerSocket, Socket } - -object IPC { - private val portMin = 1025 - private val portMax = 65536 - private val loopback = InetAddress.getByName(null) // loopback - - def client[T](port: Int)(f: IPC => T): T = - ipc(new Socket(loopback, port))(f) - - def pullServer[T](f: Server => T): T = - { - val server = makeServer - try { f(new Server(server)) } - finally { server.close() } - } - def unmanagedServer: Server = new Server(makeServer) - def makeServer: ServerSocket = - { - val random = new java.util.Random - def nextPort = random.nextInt(portMax - portMin + 1) + portMin - def createServer(attempts: Int): ServerSocket = - if (attempts > 0) - try { new ServerSocket(nextPort, 1, loopback) } - catch { case _: Exception => createServer(attempts - 1) } - else - sys.error("Could not connect to socket: maximum attempts exceeded") - createServer(10) - } - def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f) - def server[T](port: Int)(f: IPC => Option[T]): T = - serverImpl(new ServerSocket(port, 1, loopback), f) - private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = - { - def listen(): T = - { - ipc(server.accept())(f) match { - case Some(done) => done - case None => listen() - } - } - - try { listen() } - finally { server.close() } - } - private def ipc[T](s: Socket)(f: IPC => T): T = - try { f(new IPC(s)) } - finally { s.close() } - - final class Server private[IPC] (s: ServerSocket) extends NotNull { - def port = s.getLocalPort - def close() = s.close() - def isClosed: Boolean = s.isClosed - def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f) - } -} -final class IPC private (s: Socket) extends NotNull { - def port = s.getLocalPort - private val in = new BufferedReader(new InputStreamReader(s.getInputStream)) - private val out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream)) - - def send(s: String) = { out.write(s); out.newLine(); out.flush() } - def receive: String = in.readLine() -} diff --git a/util/io/src/test/scala/CopySpec.scala b/util/io/src/test/scala/CopySpec.scala deleted file mode 100644 index 7c75d2ee8..000000000 --- a/util/io/src/test/scala/CopySpec.scala +++ /dev/null @@ -1,69 +0,0 @@ -package sbt - -import java.io.File -import java.util.Arrays -import org.scalacheck._ -import Prop._ -import Arbitrary.arbLong - -object CopySpec extends Properties("Copy") { - // set to 0.25 GB by default for success on most systems without running out of space. - // when modifying IO.copyFile, verify against 1 GB or higher, preferably > 4 GB - final val MaxFileSizeBits = 28 - final val BufferSize = 1 * 1024 * 1024 - - val randomSize = Gen.choose(0, MaxFileSizeBits).map(1L << _) - val pow2Size = (0 to (MaxFileSizeBits - 1)).toList.map(1L << _) - val derivedSize = pow2Size.map(_ - 1) ::: pow2Size.map(_ + 1) ::: pow2Size - - val fileSizeGen: Gen[Long] = - Gen.frequency( - 80 -> Gen.oneOf(derivedSize), - 8 -> randomSize, - 1 -> Gen.value(0) - ) - - property("same contents") = forAll(fileSizeGen, arbLong.arbitrary) { (size: Long, seed: Long) => - IO.withTemporaryDirectory { dir => - val f1 = new File(dir, "source") - val f2 = new File(dir, "dest") - generate(seed = seed, size = size, file = f1) - IO.copyFile(f1, f2) - checkContentsSame(f1, f2) - true - } - } - - def generate(seed: Long, size: Long, file: File): Unit = { - val rnd = new java.util.Random(seed) - - val buffer = new Array[Byte](BufferSize) - def loop(offset: Long): Unit = { - val len = math.min(size - offset, BufferSize) - if (len > 0) { - rnd.nextBytes(buffer) - IO.append(file, buffer) - loop(offset + len) - } - } - if (size == 0L) IO.touch(file) else loop(0) - } - def checkContentsSame(f1: File, f2: File): Unit = { - val len = f1.length - assert(len == f2.length, "File lengths differ: " + (len, f2.length).toString + " for " + (f1, f2).toString) - Using.fileInputStream(f1) { in1 => - Using.fileInputStream(f2) { in2 => - val buffer1 = new Array[Byte](BufferSize) - val buffer2 = new Array[Byte](BufferSize) - def loop(offset: Long): Unit = if (offset < len) { - val read1 = in1.read(buffer1) - val read2 = in2.read(buffer2) - assert(read1 == read2, "Read " + (read1, read2).toString + " bytes from " + (f1, f2).toString) - assert(Arrays.equals(buffer1, buffer2), "Contents differed.") - loop(offset + read1) - } - loop(0) - } - } - } -} diff --git a/util/io/src/test/scala/FileUtilitiesSpecification.scala b/util/io/src/test/scala/FileUtilitiesSpecification.scala deleted file mode 100644 index ff8871af5..000000000 --- a/util/io/src/test/scala/FileUtilitiesSpecification.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -import org.scalacheck._ -import Prop._ -import Arbitrary.{ arbString => _, arbChar => _, _ } -import java.io.{ File, IOException } - -object WriteContentSpecification extends Properties("Write content") { - property("Roundtrip string") = forAll(writeAndCheckString _) - property("Roundtrip bytes") = forAll(writeAndCheckBytes _) - property("Write string overwrites") = forAll(overwriteAndCheckStrings _) - property("Write bytes overwrites") = forAll(overwriteAndCheckBytes _) - property("Append string appends") = forAll(appendAndCheckStrings _) - property("Append bytes appends") = forAll(appendAndCheckBytes _) - property("Unzip doesn't stack overflow") = largeUnzip() - - implicit lazy val validChar: Arbitrary[Char] = Arbitrary(for (i <- Gen.choose(0, 0xd7ff)) yield i.toChar) - implicit lazy val validString: Arbitrary[String] = Arbitrary(arbitrary[List[Char]] map (_.mkString)) - - private def largeUnzip() = - { - testUnzip[Product] - testUnzip[scala.tools.nsc.Global] - true - } - private def testUnzip[T](implicit mf: scala.reflect.Manifest[T]) = - unzipFile(IO.classLocationFile(mf.runtimeClass)) - private def unzipFile(jar: File) = - IO.withTemporaryDirectory { tmp => - IO.unzip(jar, tmp) - } - - // make the test independent of underlying platform and allow any unicode character in Strings to be encoded - val charset = IO.utf8 - - import IO._ - private def writeAndCheckString(s: String) = - withTemporaryFile { file => - write(file, s, charset) - read(file, charset) == s - } - private def writeAndCheckBytes(b: Array[Byte]) = - withTemporaryFile { file => - write(file, b) - readBytes(file) sameElements b - } - private def overwriteAndCheckStrings(a: String, b: String) = - withTemporaryFile { file => - write(file, a, charset) - write(file, b, charset) - read(file, charset) == b - } - private def overwriteAndCheckBytes(a: Array[Byte], b: Array[Byte]) = - withTemporaryFile { file => - write(file, a) - write(file, b) - readBytes(file) sameElements b - } - private def appendAndCheckStrings(a: String, b: String) = - withTemporaryFile { file => - append(file, a, charset) - append(file, b, charset) - read(file, charset) == (a + b) - } - private def appendAndCheckBytes(a: Array[Byte], b: Array[Byte]) = - withTemporaryFile { file => - append(file, a) - append(file, b) - readBytes(file) sameElements (a ++ b) - } - - private def withTemporaryFile[T](f: File => T): T = - withTemporaryDirectory { dir => f(new java.io.File(dir, "out")) } -} diff --git a/util/io/src/test/scala/NameFilterSpecification.scala b/util/io/src/test/scala/NameFilterSpecification.scala deleted file mode 100644 index e78c22fde..000000000 --- a/util/io/src/test/scala/NameFilterSpecification.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008 Mark Harrah */ - -package sbt - -import org.scalacheck._ -import Prop._ - -object NameFilterSpecification extends Properties("NameFilter") { - property("All pass accepts everything") = forAll { (s: String) => AllPassFilter.accept(s) } - property("Exact filter matches provided string") = forAll { - (s1: String, s2: String) => (new ExactFilter(s1)).accept(s2) == (s1 == s2) - } - property("Exact filter matches valid string") = forAll { (s: String) => (new ExactFilter(s)).accept(s) } - - property("Glob filter matches provided string if no *s") = forAll { - (s1: String, s2: String) => - { - val stripped = stripAsterisksAndControl(s1) - (GlobFilter(stripped).accept(s2) == (stripped == s2)) - } - } - property("Glob filter matches valid string if no *s") = forAll { - (s: String) => - { - val stripped = stripAsterisksAndControl(s) - GlobFilter(stripped).accept(stripped) - } - } - - property("Glob filter matches valid") = forAll { - (list: List[String]) => - { - val stripped = list.map(stripAsterisksAndControl) - GlobFilter(stripped.mkString("*")).accept(stripped.mkString) - } - } - - /** - * Raw control characters are stripped because they are not allowed in expressions. - * Asterisks are stripped because they are added under the control of the tests. - */ - private def stripAsterisksAndControl(s: String) = (s filter validChar).toString - private[this] def validChar(c: Char) = - !java.lang.Character.isISOControl(c) && - c != '*' && - !Character.isHighSurrogate(c) && - !Character.isLowSurrogate(c) -} \ No newline at end of file diff --git a/util/io/src/test/scala/RichURISpecification.scala b/util/io/src/test/scala/RichURISpecification.scala deleted file mode 100755 index a4dee8cbe..000000000 --- a/util/io/src/test/scala/RichURISpecification.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2012 Eugene Vigdorchik */ - -package sbt - -import org.scalacheck._ -import Gen._ -import Prop._ -import java.net.URI -import RichURI._ - -object RichURISpecification extends Properties("Rich URI") { - val strGen = { - val charGen = frequency((1, value(' ')), (9, alphaChar)) - val withEmptyGen = for (cs <- listOf(charGen)) yield cs.mkString - withEmptyGen map (_.trim.replace(" ", "%20")) filter (!_.isEmpty) - } - - val pathGen = - for (s <- listOf1(strGen)) yield s.mkString("/", "/", "") - - def nullable[T >: Null](g: Gen[T]): Gen[T] = frequency((1, value(null)), (25, g)) - - implicit val arbitraryURI: Arbitrary[URI] = - Arbitrary( - for ( - scheme <- identifier; - path <- pathGen; - fragment <- nullable(strGen) - ) yield new URI(scheme, "file:" + path, fragment) - ) - - property("withoutFragment should drop fragment") = forAll { (uri: URI) => - uri.withoutFragment.getFragment eq null - } - - property("withoutFragment should keep scheme") = forAll { (uri: URI) => - uri.withoutFragment.getScheme == uri.getScheme - } - - property("withoutFragment should keep scheme specific part") = forAll { (uri: URI) => - uri.withoutFragment.getSchemeSpecificPart == uri.getSchemeSpecificPart - } - - property("withoutMarkerScheme should drop marker scheme") = forAll { (uri: URI) => - uri.withoutMarkerScheme.getScheme == "file" - } - - property("withoutMarkerScheme should keep path") = forAll { (uri: URI) => - uri.withoutMarkerScheme.getPath == uri.getSchemeSpecificPart.stripPrefix("file:") - } - - property("withoutMarkerScheme should keep fragment") = forAll { (uri: URI) => - uri.withoutMarkerScheme.getFragment == uri.getFragment - } -} diff --git a/util/io/src/test/scala/StashSpec.scala b/util/io/src/test/scala/StashSpec.scala deleted file mode 100644 index 566e9a347..000000000 --- a/util/io/src/test/scala/StashSpec.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah */ - -package sbt - -import org.specs2._ -import mutable.Specification - -import IO._ -import java.io.File -import Function.tupled - -object CheckStash extends Specification { - "stash" should { - "handle empty files" in { - stash(Set()) {} - true must beTrue - } - - "move files during execution" in { - WithFiles(TestFiles: _*)(checkMove) - } - - "restore files on exceptions but not errors" in { - WithFiles(TestFiles: _*)(checkRestore) - } - } - - def checkRestore(seq: Seq[File]) = { - allCorrect(seq) - - stash0(seq, throw new TestRuntimeException) must beFalse - allCorrect(seq) - - stash0(seq, throw new TestException) must beFalse - allCorrect(seq) - - stash0(seq, throw new TestError) must beFalse - noneExist(seq) - } - def checkMove(seq: Seq[File]) = { - allCorrect(seq) - stash0(seq, ()) must beTrue - noneExist(seq) - } - def stash0(seq: Seq[File], post: => Unit): Boolean = - try { - stash(Set() ++ seq) { - noneExist(seq) - post - } - true - } catch { - case _: TestError | _: TestException | _: TestRuntimeException => false - } - - def allCorrect(s: Seq[File]) = (s.toList zip TestFiles.toList).foreach((correct _).tupled) - def correct(check: File, ref: (File, String)) = - { - check.exists must beTrue - read(check) must equalTo(ref._2) - } - def noneExist(s: Seq[File]) = s.forall(!_.exists) must beTrue - - lazy val TestFiles = - Seq( - "a/b/c" -> "content1", - "a/b/e" -> "content1", - "c" -> "", - "e/g" -> "asdf", - "a/g/c" -> "other" - ) map { - case (f, c) => (new File(f), c) - } -} -class TestError extends Error -class TestRuntimeException extends RuntimeException -class TestException extends Exception diff --git a/util/io/src/test/scala/WithFiles.scala b/util/io/src/test/scala/WithFiles.scala deleted file mode 100644 index 7e3fe0a2a..000000000 --- a/util/io/src/test/scala/WithFiles.scala +++ /dev/null @@ -1,25 +0,0 @@ -package sbt - -import java.io.File -import IO.{ withTemporaryDirectory, write } - -object WithFiles { - /** - * Takes the relative path -> content pairs and writes the content to a file in a temporary directory. The written file - * path is the relative path resolved against the temporary directory path. The provided function is called with the resolved file paths - * in the same order as the inputs. - */ - def apply[T](sources: (File, String)*)(f: Seq[File] => T): T = - { - withTemporaryDirectory { dir => - val sourceFiles = - for ((file, content) <- sources) yield { - assert(!file.isAbsolute) - val to = new File(dir, file.getPath) - write(to, content) - to - } - f(sourceFiles) - } - } -} \ No newline at end of file diff --git a/util/log/NOTICE b/util/log/NOTICE deleted file mode 100644 index 2455dad65..000000000 --- a/util/log/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Logging Component -Copyright 2008, 2009, 2010 Mark Harrah, Tony Sloane -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/BasicLogger.scala b/util/log/src/main/scala/sbt/BasicLogger.scala deleted file mode 100644 index 7fe59e8c0..000000000 --- a/util/log/src/main/scala/sbt/BasicLogger.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -/** Implements the level-setting methods of Logger.*/ -abstract class BasicLogger extends AbstractLogger { - private var traceEnabledVar = java.lang.Integer.MAX_VALUE - private var level: Level.Value = Level.Info - private var successEnabledVar = true - def successEnabled = synchronized { successEnabledVar } - def setSuccessEnabled(flag: Boolean): Unit = synchronized { successEnabledVar = flag } - def getLevel = synchronized { level } - def setLevel(newLevel: Level.Value): Unit = synchronized { level = newLevel } - def setTrace(level: Int): Unit = synchronized { traceEnabledVar = level } - def getTrace = synchronized { traceEnabledVar } -} \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/BufferedLogger.scala b/util/log/src/main/scala/sbt/BufferedLogger.scala deleted file mode 100644 index 08a60577d..000000000 --- a/util/log/src/main/scala/sbt/BufferedLogger.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import scala.collection.mutable.ListBuffer - -/** - * A logger that can buffer the logging done on it and then can flush the buffer - * to the delegate logger provided in the constructor. Use 'startRecording' to - * start buffering and then 'play' from to flush the buffer to the backing logger. - * The logging level set at the time a message is originally logged is used, not - * the level at the time 'play' is called. - * - * This class assumes that it is the only client of the delegate logger. - */ -class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { - private[this] val buffer = new ListBuffer[LogEvent] - private[this] var recording = false - - /** Enables buffering. */ - def record() = synchronized { recording = true } - def buffer[T](f: => T): T = { - record() - try { f } - finally { stopQuietly() } - } - def bufferQuietly[T](f: => T): T = { - record() - try { - val result = f - clear() - result - } catch { case e: Throwable => stopQuietly(); throw e } - } - def stopQuietly() = synchronized { try { stop() } catch { case e: Exception => () } } - - /** - * Flushes the buffer to the delegate logger. This method calls logAll on the delegate - * so that the messages are written consecutively. The buffer is cleared in the process. - */ - def play(): Unit = synchronized { delegate.logAll(buffer.toList); buffer.clear() } - /** Clears buffered events and disables buffering. */ - def clear(): Unit = synchronized { buffer.clear(); recording = false } - /** Plays buffered events and disables buffering. */ - def stop(): Unit = synchronized { play(); clear() } - - override def ansiCodesSupported = delegate.ansiCodesSupported - override def setLevel(newLevel: Level.Value): Unit = synchronized { - super.setLevel(newLevel) - if (recording) - buffer += new SetLevel(newLevel) - else - delegate.setLevel(newLevel) - } - override def setSuccessEnabled(flag: Boolean): Unit = synchronized { - super.setSuccessEnabled(flag) - if (recording) - buffer += new SetSuccess(flag) - else - delegate.setSuccessEnabled(flag) - } - override def setTrace(level: Int): Unit = synchronized { - super.setTrace(level) - if (recording) - buffer += new SetTrace(level) - else - delegate.setTrace(level) - } - - def trace(t: => Throwable): Unit = - doBufferableIf(traceEnabled, new Trace(t), _.trace(t)) - def success(message: => String): Unit = - doBufferable(Level.Info, new Success(message), _.success(message)) - def log(level: Level.Value, message: => String): Unit = - doBufferable(level, new Log(level, message), _.log(level, message)) - def logAll(events: Seq[LogEvent]): Unit = synchronized { - if (recording) - buffer ++= events - else - delegate.logAll(events) - } - def control(event: ControlEvent.Value, message: => String): Unit = - doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message)) - private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit = - doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered) - private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit = synchronized { - if (condition) { - if (recording) - buffer += appendIfBuffered - else - doUnbuffered(delegate) - } - } -} \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/ConsoleLogger.scala b/util/log/src/main/scala/sbt/ConsoleLogger.scala deleted file mode 100644 index c1558cc66..000000000 --- a/util/log/src/main/scala/sbt/ConsoleLogger.scala +++ /dev/null @@ -1,215 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010, 2011 Mark Harrah - */ -package sbt - -import java.io.{ BufferedWriter, PrintStream, PrintWriter } -import java.util.Locale - -object ConsoleLogger { - @deprecated("Moved to ConsoleOut", "0.13.0") - def systemOut: ConsoleOut = ConsoleOut.systemOut - - @deprecated("Moved to ConsoleOut", "0.13.0") - def overwriteContaining(s: String): (String, String) => Boolean = ConsoleOut.overwriteContaining(s) - - @deprecated("Moved to ConsoleOut", "0.13.0") - def systemOutOverwrite(f: (String, String) => Boolean): ConsoleOut = ConsoleOut.systemOutOverwrite(f) - - @deprecated("Moved to ConsoleOut", "0.13.0") - def printStreamOut(out: PrintStream): ConsoleOut = ConsoleOut.printStreamOut(out) - - @deprecated("Moved to ConsoleOut", "0.13.0") - def printWriterOut(out: PrintWriter): ConsoleOut = ConsoleOut.printWriterOut(out) - - @deprecated("Moved to ConsoleOut", "0.13.0") - def bufferedWriterOut(out: BufferedWriter): ConsoleOut = bufferedWriterOut(out) - - /** Escape character, used to introduce an escape sequence. */ - final val ESC = '\u001B' - - /** - * An escape terminator is a character in the range `@` (decimal value 64) to `~` (decimal value 126). - * It is the final character in an escape sequence. - * - * cf. http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes - */ - @deprecated("No longer public.", "0.13.8") - def isEscapeTerminator(c: Char): Boolean = - c >= '@' && c <= '~' - - /** - * Test if the character AFTER an ESC is the ANSI CSI. - * - * see: http://en.wikipedia.org/wiki/ANSI_escape_code - * - * The CSI (control sequence instruction) codes start with ESC + '['. This is for testing the second character. - * - * There is an additional CSI (one character) that we could test for, but is not frequnetly used, and we don't - * check for it. - * - * cf. http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes - */ - private def isCSI(c: Char): Boolean = c == '[' - - /** - * Tests whether or not a character needs to immediately terminate the ANSI sequence. - * - * c.f. http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements - */ - private def isAnsiTwoCharacterTerminator(c: Char): Boolean = - (c >= '@') && (c <= '_') - - /** - * Returns true if the string contains the ESC character. - * - * TODO - this should handle raw CSI (not used much) - */ - def hasEscapeSequence(s: String): Boolean = - s.indexOf(ESC) >= 0 - - /** - * Returns the string `s` with escape sequences removed. - * An escape sequence starts with the ESC character (decimal value 27) and ends with an escape terminator. - * @see isEscapeTerminator - */ - def removeEscapeSequences(s: String): String = - if (s.isEmpty || !hasEscapeSequence(s)) - s - else { - val sb = new java.lang.StringBuilder - nextESC(s, 0, sb) - sb.toString - } - private[this] def nextESC(s: String, start: Int, sb: java.lang.StringBuilder): Unit = { - val escIndex = s.indexOf(ESC, start) - if (escIndex < 0) - sb.append(s, start, s.length) - else { - sb.append(s, start, escIndex) - val next: Int = - // If it's a CSI we skip past it and then look for a terminator. - if (isCSI(s.charAt(escIndex + 1))) skipESC(s, escIndex + 2) - else if (isAnsiTwoCharacterTerminator(s.charAt(escIndex + 1))) escIndex + 2 - else { - // There could be non-ANSI character sequences we should make sure we handle here. - skipESC(s, escIndex + 1) - } - nextESC(s, next, sb) - } - } - - /** Skips the escape sequence starting at `i-1`. `i` should be positioned at the character after the ESC that starts the sequence. */ - private[this] def skipESC(s: String, i: Int): Int = { - if (i >= s.length) { - i - } else if (isEscapeTerminator(s.charAt(i))) { - i + 1 - } else { - skipESC(s, i + 1) - } - } - - val formatEnabled = - { - import java.lang.Boolean.{ getBoolean, parseBoolean } - val value = System.getProperty("sbt.log.format") - if (value eq null) (ansiSupported && !getBoolean("sbt.log.noformat")) else parseBoolean(value) - } - private[this] def jline1to2CompatMsg = "Found class jline.Terminal, but interface was expected" - - private[this] def ansiSupported = - try { - val terminal = jline.TerminalFactory.get - terminal.restore // #460 - terminal.isAnsiSupported - } catch { - case e: Exception => !isWindows - - // sbt 0.13 drops JLine 1.0 from the launcher and uses 2.x as a normal dependency - // when 0.13 is used with a 0.12 launcher or earlier, the JLine classes from the launcher get loaded - // this results in a linkage error as detected below. The detection is likely jvm specific, but the priority - // is avoiding mistakenly identifying something as a launcher incompatibility when it is not - case e: IncompatibleClassChangeError if e.getMessage == jline1to2CompatMsg => - throw new IncompatibleClassChangeError("JLine incompatibility detected. Check that the sbt launcher is version 0.13.x or later.") - } - - val noSuppressedMessage = (_: SuppressedTraceContext) => None - - private[this] def os = System.getProperty("os.name") - private[this] def isWindows = os.toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0 - - def apply(out: PrintStream): ConsoleLogger = apply(ConsoleOut.printStreamOut(out)) - def apply(out: PrintWriter): ConsoleLogger = apply(ConsoleOut.printWriterOut(out)) - def apply(out: ConsoleOut = ConsoleOut.systemOut, ansiCodesSupported: Boolean = formatEnabled, - useColor: Boolean = formatEnabled, suppressedMessage: SuppressedTraceContext => Option[String] = noSuppressedMessage): ConsoleLogger = - new ConsoleLogger(out, ansiCodesSupported, useColor, suppressedMessage) - - private[this] val EscapeSequence = (27.toChar + "[^@-~]*[@-~]").r - def stripEscapeSequences(s: String): String = - EscapeSequence.pattern.matcher(s).replaceAll("") -} - -/** - * A logger that logs to the console. On supported systems, the level labels are - * colored. - * - * This logger is not thread-safe. - */ -class ConsoleLogger private[ConsoleLogger] (val out: ConsoleOut, override val ansiCodesSupported: Boolean, val useColor: Boolean, val suppressedMessage: SuppressedTraceContext => Option[String]) extends BasicLogger { - import scala.Console.{ BLUE, GREEN, RED, RESET, YELLOW } - def messageColor(level: Level.Value) = RESET - def labelColor(level: Level.Value) = - level match { - case Level.Error => RED - case Level.Warn => YELLOW - case _ => RESET - } - def successLabelColor = GREEN - def successMessageColor = RESET - override def success(message: => String): Unit = { - if (successEnabled) - log(successLabelColor, Level.SuccessLabel, successMessageColor, message) - } - def trace(t: => Throwable): Unit = - out.lockObject.synchronized { - val traceLevel = getTrace - if (traceLevel >= 0) - out.print(StackTrace.trimmed(t, traceLevel)) - if (traceLevel <= 2) - for (msg <- suppressedMessage(new SuppressedTraceContext(traceLevel, ansiCodesSupported && useColor))) - printLabeledLine(labelColor(Level.Error), "trace", messageColor(Level.Error), msg) - } - def log(level: Level.Value, message: => String): Unit = { - if (atLevel(level)) - log(labelColor(level), level.toString, messageColor(level), message) - } - private def reset(): Unit = setColor(RESET) - - private def setColor(color: String): Unit = { - if (ansiCodesSupported && useColor) - out.lockObject.synchronized { out.print(color) } - } - private def log(labelColor: String, label: String, messageColor: String, message: String): Unit = - out.lockObject.synchronized { - for (line <- message.split("""\n""")) - printLabeledLine(labelColor, label, messageColor, line) - } - private def printLabeledLine(labelColor: String, label: String, messageColor: String, line: String): Unit = - { - reset() - out.print("[") - setColor(labelColor) - out.print(label) - reset() - out.print("] ") - setColor(messageColor) - out.print(line) - reset() - out.println() - } - - def logAll(events: Seq[LogEvent]) = out.lockObject.synchronized { events.foreach(log) } - def control(event: ControlEvent.Value, message: => String): Unit = log(labelColor(Level.Info), Level.Info.toString, BLUE, message) -} -final class SuppressedTraceContext(val traceLevel: Int, val useColor: Boolean) diff --git a/util/log/src/main/scala/sbt/ConsoleOut.scala b/util/log/src/main/scala/sbt/ConsoleOut.scala deleted file mode 100644 index 3ce0d8cdf..000000000 --- a/util/log/src/main/scala/sbt/ConsoleOut.scala +++ /dev/null @@ -1,62 +0,0 @@ -package sbt - -import java.io.{ BufferedWriter, PrintStream, PrintWriter } - -sealed trait ConsoleOut { - val lockObject: AnyRef - def print(s: String): Unit - def println(s: String): Unit - def println(): Unit -} - -object ConsoleOut { - def systemOut: ConsoleOut = printStreamOut(System.out) - - def overwriteContaining(s: String): (String, String) => Boolean = (cur, prev) => - cur.contains(s) && prev.contains(s) - - /** Move to beginning of previous line and clear the line. */ - private[this] final val OverwriteLine = "\u001B[A\r\u001B[2K" - - /** - * ConsoleOut instance that is backed by System.out. It overwrites the previously printed line - * if the function `f(lineToWrite, previousLine)` returns true. - * - * The ConsoleOut returned by this method assumes that the only newlines are from println calls - * and not in the String arguments. - */ - def systemOutOverwrite(f: (String, String) => Boolean): ConsoleOut = new ConsoleOut { - val lockObject = System.out - private[this] var last: Option[String] = None - private[this] var current = new java.lang.StringBuffer - def print(s: String): Unit = synchronized { current.append(s) } - def println(s: String): Unit = synchronized { current.append(s); println() } - def println(): Unit = synchronized { - val s = current.toString - if (ConsoleLogger.formatEnabled && last.exists(lmsg => f(s, lmsg))) - lockObject.print(OverwriteLine) - lockObject.println(s) - last = Some(s) - current = new java.lang.StringBuffer - } - } - - def printStreamOut(out: PrintStream): ConsoleOut = new ConsoleOut { - val lockObject = out - def print(s: String) = out.print(s) - def println(s: String) = out.println(s) - def println() = out.println() - } - def printWriterOut(out: PrintWriter): ConsoleOut = new ConsoleOut { - val lockObject = out - def print(s: String) = out.print(s) - def println(s: String) = { out.println(s); out.flush() } - def println() = { out.println(); out.flush() } - } - def bufferedWriterOut(out: BufferedWriter): ConsoleOut = new ConsoleOut { - val lockObject = out - def print(s: String) = out.write(s) - def println(s: String) = { out.write(s); println() } - def println() = { out.newLine(); out.flush() } - } -} diff --git a/util/log/src/main/scala/sbt/FilterLogger.scala b/util/log/src/main/scala/sbt/FilterLogger.scala deleted file mode 100644 index 5259a6e12..000000000 --- a/util/log/src/main/scala/sbt/FilterLogger.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -/** - * A filter logger is used to delegate messages but not the logging level to another logger. This means - * that messages are logged at the higher of the two levels set by this logger and its delegate. - */ -class FilterLogger(delegate: AbstractLogger) extends BasicLogger { - override lazy val ansiCodesSupported = delegate.ansiCodesSupported - def trace(t: => Throwable): Unit = { - if (traceEnabled) - delegate.trace(t) - } - override def setSuccessEnabled(flag: Boolean): Unit = delegate.setSuccessEnabled(flag) - override def successEnabled = delegate.successEnabled - override def setTrace(level: Int): Unit = delegate.setTrace(level) - override def getTrace = delegate.getTrace - def log(level: Level.Value, message: => String): Unit = { - if (atLevel(level)) - delegate.log(level, message) - } - def success(message: => String): Unit = { - if (successEnabled) - delegate.success(message) - } - def control(event: ControlEvent.Value, message: => String): Unit = { - if (atLevel(Level.Info)) - delegate.control(event, message) - } - def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events) -} diff --git a/util/log/src/main/scala/sbt/FullLogger.scala b/util/log/src/main/scala/sbt/FullLogger.scala deleted file mode 100644 index 32873eff7..000000000 --- a/util/log/src/main/scala/sbt/FullLogger.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -/** Promotes the simple Logger interface to the full AbstractLogger interface. */ -class FullLogger(delegate: Logger) extends BasicLogger { - override val ansiCodesSupported: Boolean = delegate.ansiCodesSupported - def trace(t: => Throwable): Unit = { - if (traceEnabled) - delegate.trace(t) - } - def log(level: Level.Value, message: => String): Unit = { - if (atLevel(level)) - delegate.log(level, message) - } - def success(message: => String): Unit = - if (successEnabled) - delegate.success(message) - def control(event: ControlEvent.Value, message: => String): Unit = - info(message) - def logAll(events: Seq[LogEvent]): Unit = events.foreach(log) -} -object FullLogger { - def apply(delegate: Logger): AbstractLogger = - delegate match { - case d: AbstractLogger => d - case _ => new FullLogger(delegate) - } -} diff --git a/util/log/src/main/scala/sbt/GlobalLogging.scala b/util/log/src/main/scala/sbt/GlobalLogging.scala deleted file mode 100644 index 1cd32653b..000000000 --- a/util/log/src/main/scala/sbt/GlobalLogging.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import java.io.{ File, PrintWriter } - -/** - * Provides the current global logging configuration. - * - * `full` is the current global logger. It should not be set directly because it is generated as needed from `backing.newLogger`. - * `console` is where all logging from all ConsoleLoggers should go. - * `backed` is the Logger that other loggers should feed into. - * `backing` tracks the files that persist the global logging. - * `newLogger` creates a new global logging configuration from a sink and backing configuration. - */ -final case class GlobalLogging(full: Logger, console: ConsoleOut, backed: AbstractLogger, backing: GlobalLogBacking, newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging) - -/** - * Tracks the files that persist the global logging. - * `file` is the current backing file. `last` is the previous backing file, if there is one. - * `newBackingFile` creates a new temporary location for the next backing file. - */ -final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile: () => File) { - /** Shifts the current backing file to `last` and sets the current backing to `newFile`. */ - def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newBackingFile) - - /** Shifts the current backing file to `last` and sets the current backing to a new temporary file generated by `newBackingFile`. */ - def shiftNew() = shift(newBackingFile()) - - /** - * If there is a previous backing file in `last`, that becomes the current backing file and the previous backing is cleared. - * Otherwise, no changes are made. - */ - def unshift = GlobalLogBacking(last getOrElse file, None, newBackingFile) -} -object GlobalLogBacking { - def apply(newBackingFile: => File): GlobalLogBacking = GlobalLogBacking(newBackingFile, None, newBackingFile _) -} -object GlobalLogging { - def initial(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File, console: ConsoleOut): GlobalLogging = - { - val log = ConsoleLogger(console) - GlobalLogging(log, console, log, GlobalLogBacking(newBackingFile), newLogger) - } -} \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/Level.scala b/util/log/src/main/scala/sbt/Level.scala deleted file mode 100644 index 7744b9495..000000000 --- a/util/log/src/main/scala/sbt/Level.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -/** - * An enumeration defining the levels available for logging. A level includes all of the levels - * with id larger than its own id. For example, Warn (id=3) includes Error (id=4). - */ -object Level extends Enumeration { - val Debug = Value(1, "debug") - val Info = Value(2, "info") - val Warn = Value(3, "warn") - val Error = Value(4, "error") - /** - * Defines the label to use for success messages. - * Because the label for levels is defined in this module, the success label is also defined here. - */ - val SuccessLabel = "success" - - def union(a: Value, b: Value) = if (a.id < b.id) a else b - def unionAll(vs: Seq[Value]) = vs reduceLeft union - - /** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */ - def apply(s: String) = values.find(s == _.toString) - /** Same as apply, defined for use in pattern matching. */ - private[sbt] def unapply(s: String) = apply(s) -} \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/LogEvent.scala b/util/log/src/main/scala/sbt/LogEvent.scala deleted file mode 100644 index d48957c75..000000000 --- a/util/log/src/main/scala/sbt/LogEvent.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package sbt - -sealed trait LogEvent extends NotNull -final class Success(val msg: String) extends LogEvent -final class Log(val level: Level.Value, val msg: String) extends LogEvent -final class Trace(val exception: Throwable) extends LogEvent -final class SetLevel(val newLevel: Level.Value) extends LogEvent -final class SetTrace(val level: Int) extends LogEvent -final class SetSuccess(val enabled: Boolean) extends LogEvent -final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent - -object ControlEvent extends Enumeration { - val Start, Header, Finish = Value -} \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/Logger.scala b/util/log/src/main/scala/sbt/Logger.scala deleted file mode 100644 index 848d45b3f..000000000 --- a/util/log/src/main/scala/sbt/Logger.scala +++ /dev/null @@ -1,133 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -import xsbti.{ Logger => xLogger, F0 } -import xsbti.{ Maybe, Position, Problem, Severity } - -import java.io.File - -abstract class AbstractLogger extends Logger { - def getLevel: Level.Value - def setLevel(newLevel: Level.Value): Unit - def setTrace(flag: Int): Unit - def getTrace: Int - final def traceEnabled: Boolean = getTrace >= 0 - def successEnabled: Boolean - def setSuccessEnabled(flag: Boolean): Unit - - def atLevel(level: Level.Value): Boolean = level.id >= getLevel.id - def control(event: ControlEvent.Value, message: => String): Unit - - def logAll(events: Seq[LogEvent]): Unit - /** Defined in terms of other methods in Logger and should not be called from them. */ - final def log(event: LogEvent): Unit = { - event match { - case s: Success => success(s.msg) - case l: Log => log(l.level, l.msg) - case t: Trace => trace(t.exception) - case setL: SetLevel => setLevel(setL.newLevel) - case setT: SetTrace => setTrace(setT.level) - case setS: SetSuccess => setSuccessEnabled(setS.enabled) - case c: ControlEvent => control(c.event, c.msg) - } - } -} - -object Logger { - def transferLevels(oldLog: AbstractLogger, newLog: AbstractLogger): Unit = { - newLog.setLevel(oldLog.getLevel) - newLog.setTrace(oldLog.getTrace) - } - - val Null: AbstractLogger = new AbstractLogger { - def getLevel: Level.Value = Level.Error - def setLevel(newLevel: Level.Value): Unit = () - def getTrace: Int = 0 - def setTrace(flag: Int): Unit = () - def successEnabled: Boolean = false - def setSuccessEnabled(flag: Boolean): Unit = () - def control(event: ControlEvent.Value, message: => String): Unit = () - def logAll(events: Seq[LogEvent]): Unit = () - def trace(t: => Throwable): Unit = () - def success(message: => String): Unit = () - def log(level: Level.Value, message: => String): Unit = () - } - - implicit def absLog2PLog(log: AbstractLogger): ProcessLogger = new BufferedLogger(log) with ProcessLogger - implicit def log2PLog(log: Logger): ProcessLogger = absLog2PLog(new FullLogger(log)) - implicit def xlog2Log(lg: xLogger): Logger = lg match { - case l: Logger => l - case _ => wrapXLogger(lg) - } - private[this] def wrapXLogger(lg: xLogger): Logger = new Logger { - override def debug(msg: F0[String]): Unit = lg.debug(msg) - override def warn(msg: F0[String]): Unit = lg.warn(msg) - override def info(msg: F0[String]): Unit = lg.info(msg) - override def error(msg: F0[String]): Unit = lg.error(msg) - override def trace(msg: F0[Throwable]): Unit = lg.trace(msg) - override def log(level: Level.Value, msg: F0[String]): Unit = lg.log(level, msg) - def trace(t: => Throwable): Unit = trace(f0(t)) - def success(s: => String): Unit = info(f0(s)) - def log(level: Level.Value, msg: => String): Unit = - { - val fmsg = f0(msg) - level match { - case Level.Debug => lg.debug(fmsg) - case Level.Info => lg.info(fmsg) - case Level.Warn => lg.warn(fmsg) - case Level.Error => lg.error(fmsg) - } - } - } - def f0[T](t: => T): F0[T] = new F0[T] { def apply = t } - - def m2o[S](m: Maybe[S]): Option[S] = if (m.isDefined) Some(m.get) else None - def o2m[S](o: Option[S]): Maybe[S] = o match { case Some(v) => Maybe.just(v); case None => Maybe.nothing() } - - def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer], pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position = - new Position { - val line = o2m(line0) - val lineContent = content - val offset = o2m(offset0) - val pointer = o2m(pointer0) - val pointerSpace = o2m(pointerSpace0) - val sourcePath = o2m(sourcePath0) - val sourceFile = o2m(sourceFile0) - } - - def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem = - new Problem { - val category = cat - val position = pos - val message = msg - val severity = sev - override def toString = s"[$severity] $pos: $message" - } -} - -/** - * This is intended to be the simplest logging interface for use by code that wants to log. - * It does not include configuring the logger. - */ -trait Logger extends xLogger { - final def verbose(message: => String): Unit = debug(message) - final def debug(message: => String): Unit = log(Level.Debug, message) - final def info(message: => String): Unit = log(Level.Info, message) - final def warn(message: => String): Unit = log(Level.Warn, message) - final def error(message: => String): Unit = log(Level.Error, message) - - def ansiCodesSupported: Boolean = false - - def trace(t: => Throwable): Unit - def success(message: => String): Unit - def log(level: Level.Value, message: => String): Unit - - def debug(msg: F0[String]): Unit = log(Level.Debug, msg) - def warn(msg: F0[String]): Unit = log(Level.Warn, msg) - def info(msg: F0[String]): Unit = log(Level.Info, msg) - def error(msg: F0[String]): Unit = log(Level.Error, msg) - def trace(msg: F0[Throwable]): Unit = trace(msg.apply) - def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply) -} diff --git a/util/log/src/main/scala/sbt/LoggerWriter.scala b/util/log/src/main/scala/sbt/LoggerWriter.scala deleted file mode 100644 index 9be8af409..000000000 --- a/util/log/src/main/scala/sbt/LoggerWriter.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -/** - * Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`. - * A line is delimited by `nl`, which is by default the platform line separator. - */ -class LoggerWriter(delegate: Logger, unbufferedLevel: Option[Level.Value], nl: String = System.getProperty("line.separator")) extends java.io.Writer { - def this(delegate: Logger, level: Level.Value) = this(delegate, Some(level)) - def this(delegate: Logger) = this(delegate, None) - - private[this] val buffer = new StringBuilder - private[this] val lines = new collection.mutable.ListBuffer[String] - - override def close() = flush() - override def flush(): Unit = - synchronized { - if (buffer.nonEmpty) { - log(buffer.toString) - buffer.clear() - } - } - def flushLines(level: Level.Value): Unit = - synchronized { - for (line <- lines) - delegate.log(level, line) - lines.clear() - } - override def write(content: Array[Char], offset: Int, length: Int): Unit = - synchronized { - buffer.appendAll(content, offset, length) - process() - } - - private[this] def process(): Unit = { - val i = buffer.indexOf(nl) - if (i >= 0) { - log(buffer.substring(0, i)) - buffer.delete(0, i + nl.length) - process() - } - } - private[this] def log(s: String): Unit = unbufferedLevel match { - case None => lines += s - case Some(level) => delegate.log(level, s) - } -} diff --git a/util/log/src/main/scala/sbt/MainLogging.scala b/util/log/src/main/scala/sbt/MainLogging.scala deleted file mode 100644 index 48015ad44..000000000 --- a/util/log/src/main/scala/sbt/MainLogging.scala +++ /dev/null @@ -1,51 +0,0 @@ -package sbt - -import java.io.PrintWriter - -object MainLogging { - def multiLogger(config: MultiLoggerConfig): Logger = - { - import config._ - val multi = new MultiLogger(console :: backed :: extra) - // sets multi to the most verbose for clients that inspect the current level - multi setLevel Level.unionAll(backingLevel :: screenLevel :: extra.map(_.getLevel)) - // set the specific levels - console setLevel screenLevel - backed setLevel backingLevel - console setTrace screenTrace - backed setTrace backingTrace - multi: Logger - } - - def globalDefault(console: ConsoleOut): (PrintWriter, GlobalLogBacking) => GlobalLogging = - { - lazy val f: (PrintWriter, GlobalLogBacking) => GlobalLogging = (writer, backing) => { - val backed = defaultBacked()(writer) - val full = multiLogger(defaultMultiConfig(console, backed)) - GlobalLogging(full, console, backed, backing, f) - } - f - } - - @deprecated("Explicitly specify the console output.", "0.13.0") - def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig = - defaultMultiConfig(ConsoleOut.systemOut, backing) - def defaultMultiConfig(console: ConsoleOut, backing: AbstractLogger): MultiLoggerConfig = - new MultiLoggerConfig(defaultScreen(console, ConsoleLogger.noSuppressedMessage), backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue) - - @deprecated("Explicitly specify the console output.", "0.13.0") - def defaultScreen(): AbstractLogger = ConsoleLogger() - - @deprecated("Explicitly specify the console output.", "0.13.0") - def defaultScreen(suppressedMessage: SuppressedTraceContext => Option[String]): AbstractLogger = ConsoleLogger(suppressedMessage = suppressedMessage) - - def defaultScreen(console: ConsoleOut): AbstractLogger = ConsoleLogger(console) - def defaultScreen(console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): AbstractLogger = - ConsoleLogger(console, suppressedMessage = suppressedMessage) - - def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger = - to => ConsoleLogger(ConsoleOut.printWriterOut(to), useColor = useColor) -} - -final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], - screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int) \ No newline at end of file diff --git a/util/log/src/main/scala/sbt/MultiLogger.scala b/util/log/src/main/scala/sbt/MultiLogger.scala deleted file mode 100644 index a6de160cd..000000000 --- a/util/log/src/main/scala/sbt/MultiLogger.scala +++ /dev/null @@ -1,50 +0,0 @@ - -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package sbt - -// note that setting the logging level on this logger has no effect on its behavior, only -// on the behavior of the delegates. -class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger { - override lazy val ansiCodesSupported = delegates exists supported - private[this] lazy val allSupportCodes = delegates forall supported - private[this] def supported = (_: AbstractLogger).ansiCodesSupported - - override def setLevel(newLevel: Level.Value): Unit = { - super.setLevel(newLevel) - dispatch(new SetLevel(newLevel)) - } - override def setTrace(level: Int): Unit = { - super.setTrace(level) - dispatch(new SetTrace(level)) - } - override def setSuccessEnabled(flag: Boolean): Unit = { - super.setSuccessEnabled(flag) - dispatch(new SetSuccess(flag)) - } - def trace(t: => Throwable): Unit = dispatch(new Trace(t)) - def log(level: Level.Value, message: => String): Unit = dispatch(new Log(level, message)) - def success(message: => String): Unit = dispatch(new Success(message)) - def logAll(events: Seq[LogEvent]): Unit = delegates.foreach(_.logAll(events)) - def control(event: ControlEvent.Value, message: => String): Unit = delegates.foreach(_.control(event, message)) - private[this] def dispatch(event: LogEvent): Unit = { - val plainEvent = if (allSupportCodes) event else removeEscapes(event) - for (d <- delegates) - if (d.ansiCodesSupported) - d.log(event) - else - d.log(plainEvent) - } - - private[this] def removeEscapes(event: LogEvent): LogEvent = - { - import ConsoleLogger.{ removeEscapeSequences => rm } - event match { - case s: Success => new Success(rm(s.msg)) - case l: Log => new Log(l.level, rm(l.msg)) - case ce: ControlEvent => new ControlEvent(ce.event, rm(ce.msg)) - case _: Trace | _: SetLevel | _: SetTrace | _: SetSuccess => event - } - } -} diff --git a/util/log/src/main/scala/sbt/StackTrace.scala b/util/log/src/main/scala/sbt/StackTrace.scala deleted file mode 100644 index 70554c5ec..000000000 --- a/util/log/src/main/scala/sbt/StackTrace.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Tony Sloane - */ -package sbt - -object StackTrace { - def isSbtClass(name: String) = name.startsWith("sbt") || name.startsWith("xsbt") - /** - * Return a printable representation of the stack trace associated - * with t. Information about t and its Throwable causes is included. - * The number of lines to be included for each Throwable is configured - * via d which should be greater than or equal to zero. If d is zero, - * then all elements are included up to (but not including) the first - * element that comes from sbt. If d is greater than zero, then up to - * that many lines are included, where the line for the Throwable is - * counted plus one line for each stack element. Less lines will be - * included if there are not enough stack elements. - */ - def trimmed(t: Throwable, d: Int): String = { - require(d >= 0) - val b = new StringBuilder() - - def appendStackTrace(t: Throwable, first: Boolean) { - - val include: StackTraceElement => Boolean = - if (d == 0) - element => !isSbtClass(element.getClassName) - else { - var count = d - 1 - (_ => { count -= 1; count >= 0 }) - } - - def appendElement(e: StackTraceElement) { - b.append("\tat ") - b.append(e) - b.append('\n') - } - - if (!first) - b.append("Caused by: ") - b.append(t) - b.append('\n') - - val els = t.getStackTrace() - var i = 0 - while ((i < els.size) && include(els(i))) { - appendElement(els(i)) - i += 1 - } - - } - - appendStackTrace(t, true) - var c = t - while (c.getCause() != null) { - c = c.getCause() - appendStackTrace(c, false) - } - b.toString() - - } -} \ No newline at end of file diff --git a/util/log/src/test/scala/Escapes.scala b/util/log/src/test/scala/Escapes.scala deleted file mode 100644 index 408ec5e23..000000000 --- a/util/log/src/test/scala/Escapes.scala +++ /dev/null @@ -1,113 +0,0 @@ -package sbt - -import org.scalacheck._ -import Prop._ -import Gen.{ listOf, oneOf } - -import ConsoleLogger.{ ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequences } - -object Escapes extends Properties("Escapes") { - property("genTerminator only generates terminators") = - forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) } - - property("genWithoutTerminator only generates terminators") = - forAllNoShrink(genWithoutTerminator) { (s: String) => - s.forall { c => !isEscapeTerminator(c) } - } - - property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => - !hasEscapeSequence(s) - } - - property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) => - hasEscapeSequence(s) - } - - property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => - val removed: String = removeEscapeSequences(s) - ("Escape sequence removed: '" + removed + "'") |: - (removed == s) - } - - property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) => - val removed: String = removeEscapeSequences(s) - ("Escape sequence removed: '" + removed + "'") |: - !hasEscapeSequence(removed) - } - - property("removeEscapeSequences returns string without escape sequences") = - forAllNoShrink(genWithoutEscape, genEscapePairs) { (start: String, escapes: List[EscapeAndNot]) => - val withEscapes: String = start + (escapes.map { ean => ean.escape.makeString + ean.notEscape }).mkString("") - val removed: String = removeEscapeSequences(withEscapes) - val original = start + escapes.map(_.notEscape).mkString("") - val diffCharString = diffIndex(original, removed) - ("Input string : '" + withEscapes + "'") |: - ("Expected : '" + original + "'") |: - ("Escapes removed : '" + removed + "'") |: - (diffCharString) |: - (original == removed) - } - - def diffIndex(expect: String, original: String): String = { - var i = 0; - while (i < expect.length && i < original.length) { - if (expect.charAt(i) != original.charAt(i)) return ("Differing character, idx: " + i + ", char: " + original.charAt(i) + ", expected: " + expect.charAt(i)) - i += 1 - } - if (expect.length != original.length) return s"Strings are different lengths!" - "No differences found" - } - - final case class EscapeAndNot(escape: EscapeSequence, notEscape: String) { - override def toString = s"EscapeAntNot(escape = [$escape], notEscape = [${notEscape.map(_.toInt)}])" - } - final case class EscapeSequence(content: String, terminator: Char) { - if (!content.isEmpty) { - assert(content.tail.forall(c => !isEscapeTerminator(c)), "Escape sequence content contains an escape terminator: '" + content + "'") - assert((content.head == '[') || !isEscapeTerminator(content.head), "Escape sequence content contains an escape terminator: '" + content.headOption + "'") - } - assert(isEscapeTerminator(terminator)) - def makeString: String = ESC + content + terminator - - override def toString = - if (content.isEmpty) s"ESC (${terminator.toInt})" - else s"ESC ($content) (${terminator.toInt})" - } - private[this] def noEscape(s: String): String = s.replace(ESC, ' ') - - lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genTwoCharacterSequence, genArbitraryEscapeSequence) - lazy val genEscapePair: Gen[EscapeAndNot] = for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not) - lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair) - - lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] = - for (content <- genWithoutTerminator if !content.isEmpty; term <- genTerminator) yield new EscapeSequence("[" + content, term) - - lazy val genKnownSequence: Gen[EscapeSequence] = - oneOf((misc ++ setGraphicsMode ++ setMode ++ resetMode).map(toEscapeSequence)) - - def toEscapeSequence(s: String): EscapeSequence = EscapeSequence(s.init, s.last) - - lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K") - - lazy val setGraphicsMode: Seq[String] = - for (txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m" - - lazy val resetMode = setModeLike('I') - lazy val setMode = setModeLike('h') - def setModeLike(term: Char): Seq[String] = (0 to 19).map(i => "=" + i.toString + term) - - lazy val genWithoutTerminator = - genRawString.map(_.filter { c => !isEscapeTerminator(c) && (c != '[') }) - - lazy val genTwoCharacterSequence = - // 91 == [ which is the CSI escape sequence. - oneOf((64 to 95)) filter (_ != 91) map (c => new EscapeSequence("", c.toChar)) - - lazy val genTerminator: Gen[Char] = Gen.choose('@', '~') - lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape) - - def genWithRandomEscapes: Gen[String] = - for (ls <- listOf(genRawString); end <- genRawString) yield ls.mkString("", ESC.toString, ESC.toString + end) - - private def genRawString = Arbitrary.arbString.arbitrary -} diff --git a/util/log/src/test/scala/LogWriterTest.scala b/util/log/src/test/scala/LogWriterTest.scala deleted file mode 100644 index ce96e9bc9..000000000 --- a/util/log/src/test/scala/LogWriterTest.scala +++ /dev/null @@ -1,150 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah */ - -package sbt - -import org.scalacheck._ -import Arbitrary.{ arbitrary => arb, _ } -import Gen.{ listOfN, oneOf } -import Prop._ - -import java.io.Writer - -object LogWriterTest extends Properties("Log Writer") { - final val MaxLines = 100 - final val MaxSegments = 10 - - /* Tests that content written through a LoggerWriter is properly passed to the underlying Logger. - * Each line, determined by the specified newline separator, must be logged at the correct logging level. */ - property("properly logged") = forAll { (output: Output, newLine: NewLine) => - import output.{ lines, level } - val log = new RecordingLogger - val writer = new LoggerWriter(log, Some(level), newLine.str) - logLines(writer, lines, newLine.str) - val events = log.getEvents - ("Recorded:\n" + events.map(show).mkString("\n")) |: - check(toLines(lines), events, level) - } - - /** - * Displays a LogEvent in a useful format for debugging. In particular, we are only interested in `Log` types - * and non-printable characters should be escaped - */ - def show(event: LogEvent): String = - event match { - case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")" - case _ => "Not Log" - } - /** - * Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are - * represented as separately written segments (ToLog instances). ToLog.`byCharacter` - * indicates whether to write the segment by character (true) or all at once (false) - */ - def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String): Unit = { - for (line <- lines; section <- line) { - val content = section.content - val normalized = Escape.newline(content, newLine) - if (section.byCharacter) - normalized.foreach { c => writer.write(c.toInt) } - else - writer.write(normalized) - } - writer.flush() - } - - /** Converts the given lines in segments to lines as Strings for checking the results of the test.*/ - def toLines(lines: List[List[ToLog]]): List[String] = - lines.map(_.map(_.contentOnly).mkString) - /** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/ - def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean = - (lines zip events) forall { - case (line, log: Log) => log.level == Lvl && line == log.msg - case _ => false - } - - /* The following are implicit generators to build up a write sequence. - * ToLog represents a written segment. NewLine represents one of the possible - * newline separators. A List[ToLog] represents a full line and always includes a - * final ToLog with a trailing '\n'. Newline characters are otherwise not present in - * the `content` of a ToLog instance.*/ - - implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput) - implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog) - implicit lazy val arbLine: Arbitrary[List[ToLog]] = Arbitrary(genLine) - implicit lazy val arbNewLine: Arbitrary[NewLine] = Arbitrary(genNewLine) - implicit lazy val arbLevel: Arbitrary[Level.Value] = Arbitrary(genLevel) - - implicit def genLine(implicit logG: Gen[ToLog]): Gen[List[ToLog]] = - for (l <- listOf[ToLog](MaxSegments); last <- logG) yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse - - implicit def genLog(implicit content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] = - for (c <- content.arbitrary; by <- byChar.arbitrary) yield { - assert(c != null) - new ToLog(removeNewlines(c), by) - } - - implicit lazy val genNewLine: Gen[NewLine] = - for (str <- oneOf("\n", "\r", "\r\n")) yield new NewLine(str) - - implicit lazy val genLevel: Gen[Level.Value] = - oneOf(Level.values.toSeq) - - implicit lazy val genOutput: Gen[Output] = - for (ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel) yield new Output(ls, lv) - - def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "") - def addNewline(l: ToLog): ToLog = - new ToLog(l.content + "\n", l.byCharacter) // \n will be replaced by a random line terminator for all lines - - def listOf[T](max: Int)(implicit content: Arbitrary[T]): Gen[List[T]] = - Gen.choose(0, max) flatMap { sz => listOfN(sz, content.arbitrary) } -} - -/* Helper classes*/ - -final class Output(val lines: List[List[ToLog]], val level: Level.Value) extends NotNull { - override def toString = - "Level: " + level + "\n" + lines.map(_.mkString).mkString("\n") -} -final class NewLine(val str: String) extends NotNull { - override def toString = Escape(str) -} -final class ToLog(val content: String, val byCharacter: Boolean) extends NotNull { - def contentOnly = Escape.newline(content, "") - override def toString = if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")" -} -/** Defines some utility methods for escaping unprintable characters.*/ -object Escape { - /** Escapes characters with code less than 20 by printing them as unicode escapes.*/ - def apply(s: String): String = - { - val builder = new StringBuilder(s.length) - for (c <- s) { - def escaped = pad(c.toInt.toHexString.toUpperCase, 4, '0') - if (c < 20) builder.append("\\u").append(escaped) else builder.append(c) - } - builder.toString - } - def pad(s: String, minLength: Int, extra: Char) = - { - val diff = minLength - s.length - if (diff <= 0) s else List.fill(diff)(extra).mkString("", "", s) - } - /** Replaces a \n character at the end of a string `s` with `nl`.*/ - def newline(s: String, nl: String): String = - if (s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s -} -/** Records logging events for later retrieval.*/ -final class RecordingLogger extends BasicLogger { - private var events: List[LogEvent] = Nil - - def getEvents = events.reverse - - override def ansiCodesSupported = true - def trace(t: => Throwable): Unit = { events ::= new Trace(t) } - def log(level: Level.Value, message: => String): Unit = { events ::= new Log(level, message) } - def success(message: => String): Unit = { events ::= new Success(message) } - def logAll(es: Seq[LogEvent]): Unit = { events :::= es.toList } - def control(event: ControlEvent.Value, message: => String): Unit = { events ::= new ControlEvent(event, message) } - -} diff --git a/util/log/src/test/scala/TestLogger.scala b/util/log/src/test/scala/TestLogger.scala deleted file mode 100644 index e7b6bee49..000000000 --- a/util/log/src/test/scala/TestLogger.scala +++ /dev/null @@ -1,10 +0,0 @@ -package sbt - -object TestLogger { - def apply[T](f: Logger => T): T = - { - val log = new BufferedLogger(ConsoleLogger()) - log.setLevel(Level.Debug) - log.bufferQuietly(f(log)) - } -} \ No newline at end of file diff --git a/util/logic/src/main/scala/sbt/logic/Logic.scala b/util/logic/src/main/scala/sbt/logic/Logic.scala deleted file mode 100644 index 856394251..000000000 --- a/util/logic/src/main/scala/sbt/logic/Logic.scala +++ /dev/null @@ -1,336 +0,0 @@ -package sbt -package logic - -import scala.annotation.tailrec -import Formula.{ And, True } - -/* -Defines a propositional logic with negation as failure and only allows stratified rule sets (negation must be acyclic) in order to have a unique minimal model. - -For example, this is not allowed: - + p :- not q - + q :- not p -but this is: - + p :- q - + q :- p -as is this: - + p :- q - + q := not r - - - Some useful links: - + https://en.wikipedia.org/wiki/Nonmonotonic_logic - + https://en.wikipedia.org/wiki/Negation_as_failure - + https://en.wikipedia.org/wiki/Propositional_logic - + https://en.wikipedia.org/wiki/Stable_model_semantics - + http://www.w3.org/2005/rules/wg/wiki/negation -*/ - -/** Disjunction (or) of the list of clauses. */ -final case class Clauses(clauses: List[Clause]) { - assert(clauses.nonEmpty, "At least one clause is required.") -} - -/** When the `body` Formula succeeds, atoms in `head` are true. */ -final case class Clause(body: Formula, head: Set[Atom]) - -/** A literal is an [[Atom]] or its [[negation|Negated]]. */ -sealed abstract class Literal extends Formula { - /** The underlying (positive) atom. */ - def atom: Atom - /** Negates this literal.*/ - def unary_! : Literal -} -/** A variable with name `label`. */ -final case class Atom(label: String) extends Literal { - def atom = this - def unary_! : Negated = Negated(this) -} -/** - * A negated atom, in the sense of negation as failure, not logical negation. - * That is, it is true if `atom` is not known/defined. - */ -final case class Negated(atom: Atom) extends Literal { - def unary_! : Atom = atom -} - -/** - * A formula consists of variables, negation, and conjunction (and). - * (Disjunction is not currently included- it is modeled at the level of a sequence of clauses. - * This is less convenient when defining clauses, but is not less powerful.) - */ -sealed abstract class Formula { - /** Constructs a clause that proves `atoms` when this formula is true. */ - def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet) - - /** Constructs a formula that is true iff this formula and `f` are both true.*/ - def &&(f: Formula): Formula = (this, f) match { - case (True, x) => x - case (x, True) => x - case (And(as), And(bs)) => And(as ++ bs) - case (And(as), b: Literal) => And(as + b) - case (a: Literal, And(bs)) => And(bs + a) - case (a: Literal, b: Literal) => And(Set(a, b)) - } -} - -object Formula { - /** A conjunction of literals. */ - final case class And(literals: Set[Literal]) extends Formula { - assert(literals.nonEmpty, "'And' requires at least one literal.") - } - final case object True extends Formula -} - -object Logic { - def reduceAll(clauses: List[Clause], initialFacts: Set[Literal]): Either[LogicException, Matched] = - reduce(Clauses(clauses), initialFacts) - - /** - * Computes the variables in the unique stable model for the program represented by `clauses` and `initialFacts`. - * `clause` may not have any negative feedback (that is, negation is acyclic) - * and `initialFacts` cannot be in the head of any clauses in `clause`. - * These restrictions ensure that the logic program has a unique minimal model. - */ - def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = - { - val (posSeq, negSeq) = separate(initialFacts.toSeq) - val (pos, neg) = (posSeq.toSet, negSeq.toSet) - - val problem = - checkContradictions(pos, neg) orElse - checkOverlap(clauses, pos) orElse - checkAcyclic(clauses) - - problem.toLeft( - reduce0(clauses, initialFacts, Matched.empty) - ) - } - - /** - * Verifies `initialFacts` are not in the head of any `clauses`. - * This avoids the situation where an atom is proved but no clauses prove it. - * This isn't necessarily a problem, but the main sbt use cases expects - * a proven atom to have at least one clause satisfied. - */ - private[this] def checkOverlap(clauses: Clauses, initialFacts: Set[Atom]): Option[InitialOverlap] = { - val as = atoms(clauses) - val initialOverlap = initialFacts.filter(as.inHead) - if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None - } - - private[this] def checkContradictions(pos: Set[Atom], neg: Set[Atom]): Option[InitialContradictions] = { - val contradictions = pos intersect neg - if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None - } - - private[this] def checkAcyclic(clauses: Clauses): Option[CyclicNegation] = { - val deps = dependencyMap(clauses) - val cycle = Dag.findNegativeCycle(graph(deps)) - if (cycle.nonEmpty) Some(new CyclicNegation(cycle)) else None - } - private[this] def graph(deps: Map[Atom, Set[Literal]]) = new Dag.DirectedSignedGraph[Atom] { - type Arrow = Literal - def nodes = deps.keys.toList - def dependencies(a: Atom) = deps.getOrElse(a, Set.empty).toList - def isNegative(b: Literal) = b match { - case Negated(_) => true - case Atom(_) => false - } - def head(b: Literal) = b.atom - } - - private[this] def dependencyMap(clauses: Clauses): Map[Atom, Set[Literal]] = - (Map.empty[Atom, Set[Literal]] /: clauses.clauses) { - case (m, Clause(formula, heads)) => - val deps = literals(formula) - (m /: heads) { (n, head) => n.updated(head, n.getOrElse(head, Set.empty) ++ deps) } - } - - sealed abstract class LogicException(override val toString: String) - final class InitialContradictions(val literals: Set[Atom]) extends LogicException("Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t")) - final class InitialOverlap(val literals: Set[Atom]) extends LogicException("Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t")) - final class CyclicNegation(val cycle: List[Literal]) extends LogicException("Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t")) - - /** Tracks proven atoms in the reverse order they were proved. */ - final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) { - def add(atoms: Set[Atom]): Matched = add(atoms.toList) - def add(atoms: List[Atom]): Matched = { - val newOnly = atoms.filterNot(provenSet) - new Matched(provenSet ++ newOnly, newOnly ::: reverseOrdered) - } - def ordered: List[Atom] = reverseOrdered.reverse - override def toString = ordered.map(_.label).mkString("Matched(", ",", ")") - } - object Matched { - val empty = new Matched(Set.empty, Nil) - } - - /** Separates a sequence of literals into `(pos, neg)` atom sequences. */ - private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = Util.separate(lits) { - case a: Atom => Left(a) - case Negated(n) => Right(n) - } - - /** - * Finds clauses that have no body and thus prove their head. - * Returns `(, )`. - */ - private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = - { - val (proven, unproven) = c.clauses.partition(_.body == True) - (proven.flatMap(_.head).toSet, unproven) - } - private[this] def keepPositive(lits: Set[Literal]): Set[Atom] = - lits.collect { case a: Atom => a }.toSet - - // precondition: factsToProcess contains no contradictions - @tailrec - private[this] def reduce0(clauses: Clauses, factsToProcess: Set[Literal], state: Matched): Matched = - applyAll(clauses, factsToProcess) match { - case None => // all of the remaining clauses failed on the new facts - state - case Some(applied) => - val (proven, unprovenClauses) = findProven(applied) - val processedFacts = state add keepPositive(factsToProcess) - val newlyProven = proven -- processedFacts.provenSet - val newState = processedFacts add newlyProven - if (unprovenClauses.isEmpty) - newState // no remaining clauses, done. - else { - val unproven = Clauses(unprovenClauses) - val nextFacts: Set[Literal] = if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven) - reduce0(unproven, nextFacts, newState) - } - } - - /** - * Finds negated atoms under the negation as failure rule and returns them. - * This should be called only after there are no more known atoms to be substituted. - */ - private[this] def inferFailure(clauses: Clauses): Set[Literal] = - { - /* At this point, there is at least one clause and one of the following is the case as the result of the acyclic negation rule: - i. there is at least one variable that occurs in a clause body but not in the head of a clause - ii. there is at least one variable that occurs in the head of a clause and does not transitively depend on a negated variable - In either case, each such variable x cannot be proven true and therefore proves 'not x' (negation as failure, !x in the code). - */ - val allAtoms = atoms(clauses) - val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse) - if (newFacts.nonEmpty) - newFacts - else { - val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty) - val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue) - if (newlyFalse.nonEmpty) - newlyFalse - else // should never happen due to the acyclic negation rule - sys.error(s"No progress:\n\tclauses: $clauses\n\tpossibly true: $possiblyTrue") - } - } - - private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => Negated(a)) - - /** - * Computes the set of atoms in `clauses` that directly or transitively take a negated atom as input. - * For example, for the following clauses, this method would return `List(a, d)` : - * a :- b, not c - * d :- a - */ - @tailrec - def hasNegatedDependency(clauses: Seq[Clause], posDeps: Relation[Atom, Atom], negDeps: Relation[Atom, Atom]): List[Atom] = - clauses match { - case Seq() => - // because cycles between positive literals are allowed, this isn't strictly a topological sort - Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse) - case Clause(formula, head) +: tail => - // collect direct positive and negative literals and track them in separate graphs - val (pos, neg) = directDeps(formula) - val (newPos, newNeg) = ((posDeps, negDeps) /: head) { - case ((pdeps, ndeps), d) => - (pdeps + (d, pos), ndeps + (d, neg)) - } - hasNegatedDependency(tail, newPos, newNeg) - } - - /** Computes the `(positive, negative)` literals in `formula`. */ - private[this] def directDeps(formula: Formula): (Seq[Atom], Seq[Atom]) = - Util.separate(literals(formula).toSeq) { - case Negated(a) => Right(a) - case a: Atom => Left(a) - } - private[this] def literals(formula: Formula): Set[Literal] = formula match { - case And(lits) => lits - case l: Literal => Set(l) - case True => Set.empty - } - - /** Computes the atoms in the heads and bodies of the clauses in `clause`. */ - def atoms(cs: Clauses): Atoms = cs.clauses.map(c => Atoms(c.head, atoms(c.body))).reduce(_ ++ _) - - /** Computes the set of all atoms in `formula`. */ - def atoms(formula: Formula): Set[Atom] = formula match { - case And(lits) => lits.map(_.atom) - case Negated(lit) => Set(lit) - case a: Atom => Set(a) - case True => Set() - } - - /** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */ - final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) { - /** Concatenates this with `as`. */ - def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula) - /** Atoms that cannot be true because they do not occur in a head. */ - def triviallyFalse: Set[Atom] = inFormula -- inHead - } - - /** - * Applies known facts to `clause`s, deriving a new, possibly empty list of clauses. - * 1. If a fact is in the body of a clause, the derived clause has that fact removed from the body. - * 2. If the negation of a fact is in a body of a clause, that clause fails and is removed. - * 3. If a fact or its negation is in the head of a clause, the derived clause has that fact (or its negation) removed from the head. - * 4. If a head is empty, the clause proves nothing and is removed. - * - * NOTE: empty bodies do not cause a clause to succeed yet. - * All known facts must be applied before this can be done in order to avoid inconsistencies. - * Precondition: no contradictions in `facts` - * Postcondition: no atom in `facts` is present in the result - * Postcondition: No clauses have an empty head - */ - def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = - { - val newClauses = - if (facts.isEmpty) - cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head - else - cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) - if (newClauses.isEmpty) None else Some(Clauses(newClauses)) - } - - def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = - { - val atoms = facts.map(_.atom) - val newHead = c.head -- atoms // 3. - if (newHead.isEmpty) // 4. empty head - None - else - substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 - } - - /** Derives the formula that results from substituting `facts` into `formula`. */ - @tailrec - def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match { - case And(lits) => - def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a) - if (lits.exists(negated(facts))) // 2. - None - else { - val newLits = lits -- facts - val newF = if (newLits.isEmpty) True else And(newLits) - Some(newF) // 1. - } - case True => Some(True) - case lit: Literal => // define in terms of And - substitute(And(Set(lit)), facts) - } -} diff --git a/util/logic/src/test/scala/sbt/logic/Test.scala b/util/logic/src/test/scala/sbt/logic/Test.scala deleted file mode 100644 index f62a9e767..000000000 --- a/util/logic/src/test/scala/sbt/logic/Test.scala +++ /dev/null @@ -1,115 +0,0 @@ -package sbt -package logic - -import org.scalacheck._ -import Prop.secure -import Logic.{ LogicException, Matched } - -object LogicTest extends Properties("Logic") { - import TestClauses._ - - property("Handles trivial resolution.") = secure(expect(trivial, Set(A))) - property("Handles less trivial resolution.") = secure(expect(lessTrivial, Set(B, A, D))) - property("Handles cycles without negation") = secure(expect(cycles, Set(F, A, B))) - property("Handles basic exclusion.") = secure(expect(excludedPos, Set())) - property("Handles exclusion of head proved by negation.") = secure(expect(excludedNeg, Set())) - // TODO: actually check ordering, probably as part of a check that dependencies are satisifed - property("Properly orders results.") = secure(expect(ordering, Set(B, A, C, E, F))) - property("Detects cyclic negation") = secure( - Logic.reduceAll(badClauses, Set()) match { - case Right(res) => false - case Left(err: Logic.CyclicNegation) => true - case Left(err) => sys.error(s"Expected cyclic error, got: $err") - } - ) - - def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { - case Left(err) => false - case Right(res) => - val actual = res.provenSet - (actual == expected) || sys.error(s"Expected to prove $expected, but actually proved $actual") - } -} - -object TestClauses { - - val A = Atom("A") - val B = Atom("B") - val C = Atom("C") - val D = Atom("D") - val E = Atom("E") - val F = Atom("F") - val G = Atom("G") - - val clauses = - A.proves(B) :: - A.proves(F) :: - B.proves(F) :: - F.proves(A) :: - (!C).proves(F) :: - D.proves(C) :: - C.proves(D) :: - Nil - - val cycles = Logic.reduceAll(clauses, Set()) - - val badClauses = - A.proves(D) :: - clauses - - val excludedNeg = { - val cs = - (!A).proves(B) :: - Nil - val init = - (!A) :: - (!B) :: - Nil - Logic.reduceAll(cs, init.toSet) - } - - val excludedPos = { - val cs = - A.proves(B) :: - Nil - val init = - A :: - (!B) :: - Nil - Logic.reduceAll(cs, init.toSet) - } - - val trivial = { - val cs = - Formula.True.proves(A) :: - Nil - Logic.reduceAll(cs, Set.empty) - } - - val lessTrivial = { - val cs = - Formula.True.proves(A) :: - Formula.True.proves(B) :: - (A && B && (!C)).proves(D) :: - Nil - Logic.reduceAll(cs, Set()) - } - - val ordering = { - val cs = - E.proves(F) :: - (C && !D).proves(E) :: - (A && B).proves(C) :: - Nil - Logic.reduceAll(cs, Set(A, B)) - } - - def all(): Unit = { - println(s"Cycles: $cycles") - println(s"xNeg: $excludedNeg") - println(s"xPos: $excludedPos") - println(s"trivial: $trivial") - println(s"lessTrivial: $lessTrivial") - println(s"ordering: $ordering") - } -} diff --git a/util/process/NOTICE b/util/process/NOTICE deleted file mode 100644 index 789c9ff1f..000000000 --- a/util/process/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Process Component -Copyright 2008, 2009, 2010 Mark Harrah, Vesa Vilhonen -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/process/src/main/scala/sbt/InheritInput.scala b/util/process/src/main/scala/sbt/InheritInput.scala deleted file mode 100755 index a9828b04d..000000000 --- a/util/process/src/main/scala/sbt/InheritInput.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2012 Eugene Vigdorchik - */ -package sbt - -import java.lang.{ ProcessBuilder => JProcessBuilder } - -/** On java 7, inherit System.in for a ProcessBuilder. */ -private[sbt] object InheritInput { - def apply(p: JProcessBuilder): Boolean = (redirectInput, inherit) match { - case (Some(m), Some(f)) => - m.invoke(p, f); true - case _ => false - } - - private[this] val pbClass = Class.forName("java.lang.ProcessBuilder") - private[this] val redirectClass = pbClass.getClasses find (_.getSimpleName == "Redirect") - - private[this] val redirectInput = redirectClass map (pbClass.getMethod("redirectInput", _)) - private[this] val inherit = redirectClass map (_ getField "INHERIT" get null) -} diff --git a/util/process/src/main/scala/sbt/Process.scala b/util/process/src/main/scala/sbt/Process.scala deleted file mode 100644 index 79435367d..000000000 --- a/util/process/src/main/scala/sbt/Process.scala +++ /dev/null @@ -1,221 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package sbt - -import java.lang.{ Process => JProcess, ProcessBuilder => JProcessBuilder } -import java.io.{ Closeable, File, IOException } -import java.io.{ BufferedReader, InputStream, InputStreamReader, OutputStream, PipedInputStream, PipedOutputStream } -import java.net.URL - -trait ProcessExtra { - import Process._ - implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder) - implicit def fileToProcess(file: File): FilePartialBuilder = apply(file) - implicit def urlToProcess(url: URL): URLPartialBuilder = apply(url) - @deprecated("Use string interpolation", "0.13.0") - implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command) - implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => SourcePartialBuilder): Seq[SourcePartialBuilder] = applySeq(builders) - - implicit def stringToProcess(command: String): ProcessBuilder = apply(command) - implicit def stringSeqToProcess(command: Seq[String]): ProcessBuilder = apply(command) -} - -/** Methods for constructing simple commands that can then be combined. */ -object Process extends ProcessExtra { - def apply(command: String): ProcessBuilder = apply(command, None) - - def apply(command: Seq[String]): ProcessBuilder = apply(command.toArray, None) - - def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command :: arguments.toList, None) - /** create ProcessBuilder with working dir set to File and extra environment variables */ - def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder = - apply(command, Some(cwd), extraEnv: _*) - /** create ProcessBuilder with working dir set to File and extra environment variables */ - def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder = - apply(command, Some(cwd), extraEnv: _*) - /** create ProcessBuilder with working dir optionally set to File and extra environment variables */ - def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { - apply(command.split("""\s+"""), cwd, extraEnv: _*) - // not smart to use this on windows, because CommandParser uses \ to escape ". - /*CommandParser.parse(command) match { - case Left(errorMsg) => error(errorMsg) - case Right((cmd, args)) => apply(cmd :: args, cwd, extraEnv : _*) - }*/ - } - /** create ProcessBuilder with working dir optionally set to File and extra environment variables */ - def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = { - val jpb = new JProcessBuilder(command.toArray: _*) - cwd.foreach(jpb directory _) - extraEnv.foreach { case (k, v) => jpb.environment.put(k, v) } - apply(jpb) - } - def apply(builder: JProcessBuilder): ProcessBuilder = new SimpleProcessBuilder(builder) - def apply(file: File): FilePartialBuilder = new FileBuilder(file) - def apply(url: URL): URLPartialBuilder = new URLBuilder(url) - @deprecated("Use string interpolation", "0.13.0") - def apply(command: scala.xml.Elem): ProcessBuilder = apply(command.text.trim) - def applySeq[T](builders: Seq[T])(implicit convert: T => SourcePartialBuilder): Seq[SourcePartialBuilder] = builders.map(convert) - - def apply(value: Boolean): ProcessBuilder = apply(value.toString, if (value) 0 else 1) - def apply(name: String, exitValue: => Int): ProcessBuilder = new DummyProcessBuilder(name, exitValue) - - def cat(file: SourcePartialBuilder, files: SourcePartialBuilder*): ProcessBuilder = cat(file :: files.toList) - def cat(files: Seq[SourcePartialBuilder]): ProcessBuilder = - { - require(files.nonEmpty) - files.map(_.cat).reduceLeft(_ #&& _) - } -} - -trait SourcePartialBuilder extends NotNull { - /** Writes the output stream of this process to the given file. */ - def #>(f: File): ProcessBuilder = toFile(f, false) - /** Appends the output stream of this process to the given file. */ - def #>>(f: File): ProcessBuilder = toFile(f, true) - /** - * Writes the output stream of this process to the given OutputStream. The - * argument is call-by-name, so the stream is recreated, written, and closed each - * time this process is executed. - */ - def #>(out: => OutputStream): ProcessBuilder = #>(new OutputStreamBuilder(out)) - def #>(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(toSource, b, false, ExitCodes.firstIfNonzero) - private def toFile(f: File, append: Boolean) = #>(new FileOutput(f, append)) - def cat = toSource - protected def toSource: ProcessBuilder -} -trait SinkPartialBuilder extends NotNull { - /** Reads the given file into the input stream of this process. */ - def #<(f: File): ProcessBuilder = #<(new FileInput(f)) - /** Reads the given URL into the input stream of this process. */ - def #<(f: URL): ProcessBuilder = #<(new URLInput(f)) - /** - * Reads the given InputStream into the input stream of this process. The - * argument is call-by-name, so the stream is recreated, read, and closed each - * time this process is executed. - */ - def #<(in: => InputStream): ProcessBuilder = #<(new InputStreamBuilder(in)) - def #<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, toSink, false, ExitCodes.firstIfNonzero) - protected def toSink: ProcessBuilder -} - -trait URLPartialBuilder extends SourcePartialBuilder -trait FilePartialBuilder extends SinkPartialBuilder with SourcePartialBuilder { - def #<<(f: File): ProcessBuilder - def #<<(u: URL): ProcessBuilder - def #<<(i: => InputStream): ProcessBuilder - def #<<(p: ProcessBuilder): ProcessBuilder -} - -/** - * Represents a process that is running or has finished running. - * It may be a compound process with several underlying native processes (such as 'a #&& b`). - */ -trait Process extends NotNull { - /** Blocks until this process exits and returns the exit code.*/ - def exitValue(): Int - /** Destroys this process. */ - def destroy(): Unit -} -/** Represents a runnable process. */ -trait ProcessBuilder extends SourcePartialBuilder with SinkPartialBuilder { - /** - * Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is - * sent to the console. If the exit code is non-zero, an exception is thrown. - */ - def !! : String - /** - * Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is - * sent to the provided ProcessLogger. If the exit code is non-zero, an exception is thrown. - */ - def !!(log: ProcessLogger): String - /** - * Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination and then throw an exception. - */ - def lines: Stream[String] - /** - * Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. - */ - def lines(log: ProcessLogger): Stream[String] - /** - * Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. - */ - def lines_! : Stream[String] - /** - * Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available - * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value, - * the Stream will provide all lines up to termination but will not throw an exception. - */ - def lines_!(log: ProcessLogger): Stream[String] - /** - * Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the console. - */ - def ! : Int - /** - * Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger. - */ - def !(log: ProcessLogger): Int - /** - * Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the console. The newly started process reads from standard input of the current process. - */ - def !< : Int - /** - * Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger. The newly started process reads from standard input of the current process. - */ - def !<(log: ProcessLogger): Int - /** Starts the process represented by this builder. Standard output and error are sent to the console.*/ - def run(): Process - /** Starts the process represented by this builder. Standard output and error are sent to the given ProcessLogger.*/ - def run(log: ProcessLogger): Process - /** Starts the process represented by this builder. I/O is handled by the given ProcessIO instance.*/ - def run(io: ProcessIO): Process - /** - * Starts the process represented by this builder. Standard output and error are sent to the console. - * The newly started process reads from standard input of the current process if `connectInput` is true. - */ - def run(connectInput: Boolean): Process - /** - * Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are - * sent to the given ProcessLogger. - * The newly started process reads from standard input of the current process if `connectInput` is true. - */ - def run(log: ProcessLogger, connectInput: Boolean): Process - - def runBuffered(log: ProcessLogger, connectInput: Boolean): Process - - /** Constructs a command that runs this command first and then `other` if this command succeeds.*/ - def #&&(other: ProcessBuilder): ProcessBuilder - /** Constructs a command that runs this command first and then `other` if this command does not succeed.*/ - def #||(other: ProcessBuilder): ProcessBuilder - /** - * Constructs a command that will run this command and pipes the output to `other`. - * `other` must be a simple command. - * The exit code will be that of `other` regardless of whether this command succeeds. - */ - def #|(other: ProcessBuilder): ProcessBuilder - /** Constructs a command that will run this command and then `other`. The exit code will be the exit code of `other`.*/ - def ###(other: ProcessBuilder): ProcessBuilder - - def canPipeTo: Boolean -} -/** Each method will be called in a separate thread.*/ -final class ProcessIO(val writeInput: OutputStream => Unit, val processOutput: InputStream => Unit, val processError: InputStream => Unit, val inheritInput: JProcessBuilder => Boolean) extends NotNull { - def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError, inheritInput) - def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process, inheritInput) - def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError, inheritInput) -} -trait ProcessLogger { - def info(s: => String): Unit - def error(s: => String): Unit - def buffer[T](f: => T): T -} diff --git a/util/process/src/main/scala/sbt/ProcessImpl.scala b/util/process/src/main/scala/sbt/ProcessImpl.scala deleted file mode 100644 index abef81b33..000000000 --- a/util/process/src/main/scala/sbt/ProcessImpl.scala +++ /dev/null @@ -1,436 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah, Vesa Vilhonen - */ -package sbt - -import java.lang.{ Process => JProcess, ProcessBuilder => JProcessBuilder } -import java.io.{ BufferedReader, Closeable, InputStream, InputStreamReader, IOException, OutputStream, PrintStream } -import java.io.{ FilterInputStream, FilterOutputStream, PipedInputStream, PipedOutputStream } -import java.io.{ File, FileInputStream, FileOutputStream } -import java.net.URL - -/** Runs provided code in a new Thread and returns the Thread instance. */ -private object Spawn { - def apply(f: => Unit): Thread = apply(f, false) - def apply(f: => Unit, daemon: Boolean): Thread = - { - val thread = new Thread() { override def run() = { f } } - thread.setDaemon(daemon) - thread.start() - thread - } -} -private object Future { - def apply[T](f: => T): () => T = - { - val result = new SyncVar[Either[Throwable, T]] - def run(): Unit = - try { result.set(Right(f)) } - catch { case e: Exception => result.set(Left(e)) } - Spawn(run) - () => - result.get match { - case Right(value) => value - case Left(exception) => throw exception - } - } -} - -object BasicIO { - def apply(buffer: StringBuffer, log: Option[ProcessLogger], withIn: Boolean) = new ProcessIO(input(withIn), processFully(buffer), getErr(log), inheritInput(withIn)) - def apply(log: ProcessLogger, withIn: Boolean) = new ProcessIO(input(withIn), processInfoFully(log), processErrFully(log), inheritInput(withIn)) - - def getErr(log: Option[ProcessLogger]) = log match { case Some(lg) => processErrFully(lg); case None => toStdErr } - - private def processErrFully(log: ProcessLogger) = processFully(s => log.error(s)) - private def processInfoFully(log: ProcessLogger) = processFully(s => log.info(s)) - - def closeOut = (_: OutputStream).close() - final val BufferSize = 8192 - final val Newline = System.getProperty("line.separator") - - def close(c: java.io.Closeable) = try { c.close() } catch { case _: java.io.IOException => () } - def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer)) - def processFully(processLine: String => Unit): InputStream => Unit = - in => - { - val reader = new BufferedReader(new InputStreamReader(in)) - processLinesFully(processLine)(reader.readLine) - reader.close() - } - def processLinesFully(processLine: String => Unit)(readLine: () => String): Unit = { - def readFully(): Unit = { - val line = readLine() - if (line != null) { - processLine(line) - readFully() - } - } - readFully() - } - def connectToIn(o: OutputStream): Unit = transferFully(Uncloseable protect System.in, o) - def input(connect: Boolean): OutputStream => Unit = if (connect) connectToIn else closeOut - def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput), inheritInput(connectInput)) - def standard(in: OutputStream => Unit, inheritIn: JProcessBuilder => Boolean): ProcessIO = new ProcessIO(in, toStdOut, toStdErr, inheritIn) - - def toStdErr = (in: InputStream) => transferFully(in, System.err) - def toStdOut = (in: InputStream) => transferFully(in, System.out) - - def transferFully(in: InputStream, out: OutputStream): Unit = - try { transferFullyImpl(in, out) } - catch { case _: InterruptedException => () } - - private[this] def appendLine(buffer: Appendable): String => Unit = - line => - { - buffer.append(line) - buffer.append(Newline) - } - - private[this] def transferFullyImpl(in: InputStream, out: OutputStream): Unit = { - val continueCount = 1 //if(in.isInstanceOf[PipedInputStream]) 1 else 0 - val buffer = new Array[Byte](BufferSize) - def read(): Unit = { - val byteCount = in.read(buffer) - if (byteCount >= continueCount) { - out.write(buffer, 0, byteCount) - out.flush() - read - } - } - read - in.close() - } - - def inheritInput(connect: Boolean) = { p: JProcessBuilder => if (connect) InheritInput(p) else false } -} -private[sbt] object ExitCodes { - def ignoreFirst: (Int, Int) => Int = (a, b) => b - def firstIfNonzero: (Int, Int) => Int = (a, b) => if (a != 0) a else b -} - -private abstract class AbstractProcessBuilder extends ProcessBuilder with SinkPartialBuilder with SourcePartialBuilder { - def #&&(other: ProcessBuilder): ProcessBuilder = new AndProcessBuilder(this, other) - def #||(other: ProcessBuilder): ProcessBuilder = new OrProcessBuilder(this, other) - def #|(other: ProcessBuilder): ProcessBuilder = - { - require(other.canPipeTo, "Piping to multiple processes is not supported.") - new PipedProcessBuilder(this, other, false, exitCode = ExitCodes.ignoreFirst) - } - def ###(other: ProcessBuilder): ProcessBuilder = new SequenceProcessBuilder(this, other) - - protected def toSource = this - protected def toSink = this - - def run(): Process = run(false) - def run(connectInput: Boolean): Process = run(BasicIO.standard(connectInput)) - def run(log: ProcessLogger): Process = run(log, false) - def run(log: ProcessLogger, connectInput: Boolean): Process = run(BasicIO(log, connectInput)) - - private[this] def getString(log: Option[ProcessLogger], withIn: Boolean): String = - { - val buffer = new StringBuffer - val code = this ! BasicIO(buffer, log, withIn) - if (code == 0) buffer.toString else sys.error("Nonzero exit value: " + code) - } - def !! = getString(None, false) - def !!(log: ProcessLogger) = getString(Some(log), false) - def !!< = getString(None, true) - def !!<(log: ProcessLogger) = getString(Some(log), true) - - def lines: Stream[String] = lines(false, true, None) - def lines(log: ProcessLogger): Stream[String] = lines(false, true, Some(log)) - def lines_! : Stream[String] = lines(false, false, None) - def lines_!(log: ProcessLogger): Stream[String] = lines(false, false, Some(log)) - - private[this] def lines(withInput: Boolean, nonZeroException: Boolean, log: Option[ProcessLogger]): Stream[String] = - { - val streamed = Streamed[String](nonZeroException) - val process = run(new ProcessIO(BasicIO.input(withInput), BasicIO.processFully(streamed.process), BasicIO.getErr(log), BasicIO.inheritInput(withInput))) - Spawn { streamed.done(process.exitValue()) } - streamed.stream() - } - - def ! = run(false).exitValue() - def !< = run(true).exitValue() - def !(log: ProcessLogger) = runBuffered(log, false).exitValue() - def !<(log: ProcessLogger) = runBuffered(log, true).exitValue() - def runBuffered(log: ProcessLogger, connectInput: Boolean) = - log.buffer { run(log, connectInput) } - def !(io: ProcessIO) = run(io).exitValue() - - def canPipeTo = false -} - -private[sbt] class URLBuilder(url: URL) extends URLPartialBuilder with SourcePartialBuilder { - protected def toSource = new URLInput(url) -} -private[sbt] class FileBuilder(base: File) extends FilePartialBuilder with SinkPartialBuilder with SourcePartialBuilder { - protected def toSource = new FileInput(base) - protected def toSink = new FileOutput(base, false) - def #<<(f: File): ProcessBuilder = #<<(new FileInput(f)) - def #<<(u: URL): ProcessBuilder = #<<(new URLInput(u)) - def #<<(s: => InputStream): ProcessBuilder = #<<(new InputStreamBuilder(s)) - def #<<(b: ProcessBuilder): ProcessBuilder = new PipedProcessBuilder(b, new FileOutput(base, true), false, ExitCodes.firstIfNonzero) -} - -private abstract class BasicBuilder extends AbstractProcessBuilder { - protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.") - final def run(io: ProcessIO): Process = - { - val p = createProcess(io) - p.start() - p - } - protected[this] def createProcess(io: ProcessIO): BasicProcess -} -private abstract class BasicProcess extends Process { - def start(): Unit -} - -private abstract class CompoundProcess extends BasicProcess { - def destroy(): Unit = destroyer() - def exitValue() = getExitValue().getOrElse(sys.error("No exit code: process destroyed.")) - - def start() = getExitValue - - protected lazy val (getExitValue, destroyer) = - { - val code = new SyncVar[Option[Int]]() - code.set(None) - val thread = Spawn(code.set(runAndExitValue())) - - ( - Future { thread.join(); code.get }, - () => thread.interrupt() - ) - } - - /** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/ - protected[this] def runAndExitValue(): Option[Int] - - protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] = - { - try { Some(action) } - catch { case _: InterruptedException => destroyImpl; None } - } -} - -private abstract class SequentialProcessBuilder(a: ProcessBuilder, b: ProcessBuilder, operatorString: String) extends BasicBuilder { - checkNotThis(a) - checkNotThis(b) - override def toString = " ( " + a + " " + operatorString + " " + b + " ) " -} -private class PipedProcessBuilder(first: ProcessBuilder, second: ProcessBuilder, toError: Boolean, exitCode: (Int, Int) => Int) extends SequentialProcessBuilder(first, second, if (toError) "#|!" else "#|") { - override def createProcess(io: ProcessIO) = new PipedProcesses(first, second, io, toError, exitCode) -} -private class AndProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "#&&") { - override def createProcess(io: ProcessIO) = new AndProcess(first, second, io) -} -private class OrProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "#||") { - override def createProcess(io: ProcessIO) = new OrProcess(first, second, io) -} -private class SequenceProcessBuilder(first: ProcessBuilder, second: ProcessBuilder) extends SequentialProcessBuilder(first, second, "###") { - override def createProcess(io: ProcessIO) = new ProcessSequence(first, second, io) -} - -private class SequentialProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO, evaluateSecondProcess: Int => Boolean) extends CompoundProcess { - protected[this] override def runAndExitValue() = - { - val first = a.run(io) - runInterruptible(first.exitValue)(first.destroy()) flatMap - { codeA => - if (evaluateSecondProcess(codeA)) { - val second = b.run(io) - runInterruptible(second.exitValue)(second.destroy()) - } else - Some(codeA) - } - } -} -private class AndProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, _ == 0) -private class OrProcess(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, _ != 0) -private class ProcessSequence(a: ProcessBuilder, b: ProcessBuilder, io: ProcessIO) extends SequentialProcess(a, b, io, ignore => true) - -private class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean, exitCode: (Int, Int) => Int) extends CompoundProcess { - protected[this] override def runAndExitValue() = - { - val currentSource = new SyncVar[Option[InputStream]] - val pipeOut = new PipedOutputStream - val source = new PipeSource(currentSource, pipeOut, a.toString) - source.start() - - val pipeIn = new PipedInputStream(pipeOut) - val currentSink = new SyncVar[Option[OutputStream]] - val sink = new PipeSink(pipeIn, currentSink, b.toString) - sink.start() - - def handleOutOrError(fromOutput: InputStream) = currentSource.put(Some(fromOutput)) - - val firstIO = - if (toError) - defaultIO.withError(handleOutOrError) - else - defaultIO.withOutput(handleOutOrError) - val secondIO = defaultIO.withInput(toInput => currentSink.put(Some(toInput))) - - val second = b.run(secondIO) - val first = a.run(firstIO) - try { - runInterruptible { - val firstResult = first.exitValue - currentSource.put(None) - currentSink.put(None) - val secondResult = second.exitValue - exitCode(firstResult, secondResult) - } { - first.destroy() - second.destroy() - } - } finally { - BasicIO.close(pipeIn) - BasicIO.close(pipeOut) - } - } -} -private class PipeSource(currentSource: SyncVar[Option[InputStream]], pipe: PipedOutputStream, label: => String) extends Thread { - final override def run(): Unit = { - currentSource.get match { - case Some(source) => - try { BasicIO.transferFully(source, pipe) } - catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() } - finally { - BasicIO.close(source) - currentSource.unset() - } - run() - case None => - currentSource.unset() - BasicIO.close(pipe) - } - } -} -private class PipeSink(pipe: PipedInputStream, currentSink: SyncVar[Option[OutputStream]], label: => String) extends Thread { - final override def run(): Unit = { - currentSink.get match { - case Some(sink) => - try { BasicIO.transferFully(pipe, sink) } - catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() } - finally { - BasicIO.close(sink) - currentSink.unset() - } - run() - case None => - currentSink.unset() - } - } -} - -private[sbt] class DummyProcessBuilder(override val toString: String, exitValue: => Int) extends AbstractProcessBuilder { - override def run(io: ProcessIO): Process = new DummyProcess(exitValue) - override def canPipeTo = true -} -/** - * A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O. - * The implementation of `exitValue` waits until these threads die before returning. - */ -private class DummyProcess(action: => Int) extends Process { - private[this] val exitCode = Future(action) - override def exitValue() = exitCode() - override def destroy(): Unit = () -} -/** Represents a simple command without any redirection or combination. */ -private[sbt] class SimpleProcessBuilder(p: JProcessBuilder) extends AbstractProcessBuilder { - override def run(io: ProcessIO): Process = - { - import io._ - val inherited = inheritInput(p) - val process = p.start() - - // spawn threads that process the output and error streams, and also write input if not inherited. - if (!inherited) - Spawn(writeInput(process.getOutputStream)) - val outThread = Spawn(processOutput(process.getInputStream)) - val errorThread = - if (!p.redirectErrorStream) - Spawn(processError(process.getErrorStream)) :: Nil - else - Nil - new SimpleProcess(process, outThread :: errorThread) - } - override def toString = p.command.toString - override def canPipeTo = true -} - -/** - * A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the - * output and error streams of the process. - * The implementation of `exitValue` wait for the process to finish and then waits until the threads reading output and error streams die before - * returning. Note that the thread that reads the input stream cannot be interrupted, see https://github.com/sbt/sbt/issues/327 and - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4514257 - */ -private class SimpleProcess(p: JProcess, outputThreads: List[Thread]) extends Process { - override def exitValue() = - { - try { - p.waitFor() - } catch { - case _: InterruptedException => p.destroy() - } - outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this) - p.exitValue() - } - override def destroy() = p.destroy() -} - -private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath) -private class URLInput(url: URL) extends InputStreamBuilder(url.openStream, url.toString) -private class FileInput(file: File) extends InputStreamBuilder(new FileInputStream(file), file.getAbsolutePath) - -import Uncloseable.protect -private class OutputStreamBuilder(stream: => OutputStream, label: String) extends ThreadProcessBuilder(label, _.writeInput(protect(stream))) { - def this(stream: => OutputStream) = this(stream, "") -} -private class InputStreamBuilder(stream: => InputStream, label: String) extends ThreadProcessBuilder(label, _.processOutput(protect(stream))) { - def this(stream: => InputStream) = this(stream, "") -} - -private abstract class ThreadProcessBuilder(override val toString: String, runImpl: ProcessIO => Unit) extends AbstractProcessBuilder { - override def run(io: ProcessIO): Process = - { - val success = new SyncVar[Boolean] - success.put(false) - new ThreadProcess(Spawn { runImpl(io); success.set(true) }, success) - } -} -private final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process { - override def exitValue() = - { - thread.join() - if (success.get) 0 else 1 - } - override def destroy(): Unit = thread.interrupt() -} - -object Uncloseable { - def apply(in: InputStream): InputStream = new FilterInputStream(in) { override def close(): Unit = () } - def apply(out: OutputStream): OutputStream = new FilterOutputStream(out) { override def close(): Unit = () } - def protect(in: InputStream): InputStream = if (in eq System.in) Uncloseable(in) else in - def protect(out: OutputStream): OutputStream = if ((out eq System.out) || (out eq System.err)) Uncloseable(out) else out -} -private object Streamed { - def apply[T](nonzeroException: Boolean): Streamed[T] = - { - val q = new java.util.concurrent.LinkedBlockingQueue[Either[Int, T]] - def next(): Stream[T] = - q.take match { - case Left(0) => Stream.empty - case Left(code) => if (nonzeroException) sys.error("Nonzero exit code: " + code) else Stream.empty - case Right(s) => Stream.cons(s, next) - } - new Streamed((s: T) => q.put(Right(s)), code => q.put(Left(code)), () => next()) - } -} - -private final class Streamed[T](val process: T => Unit, val done: Int => Unit, val stream: () => Stream[T]) extends NotNull diff --git a/util/process/src/main/scala/sbt/SyncVar.scala b/util/process/src/main/scala/sbt/SyncVar.scala deleted file mode 100644 index c268aac3d..000000000 --- a/util/process/src/main/scala/sbt/SyncVar.scala +++ /dev/null @@ -1,39 +0,0 @@ -package sbt - -// minimal copy of scala.concurrent.SyncVar since that version deprecated put and unset -private[sbt] final class SyncVar[A] { - private[this] var isDefined: Boolean = false - private[this] var value: Option[A] = None - - /** Waits until a value is set and then gets it. Does not clear the value */ - def get: A = synchronized { - while (!isDefined) wait() - value.get - } - - /** Waits until a value is set, gets it, and finally clears the value. */ - def take(): A = synchronized { - try get finally unset() - } - - /** Sets the value, whether or not it is currently defined. */ - def set(x: A): Unit = synchronized { - isDefined = true - value = Some(x) - notifyAll() - } - - /** Sets the value, first waiting until it is undefined if it is currently defined. */ - def put(x: A): Unit = synchronized { - while (isDefined) wait() - set(x) - } - - /** Clears the value, whether or not it is current defined. */ - def unset(): Unit = synchronized { - isDefined = false - value = None - notifyAll() - } -} - diff --git a/util/process/src/test/scala/ProcessSpecification.scala b/util/process/src/test/scala/ProcessSpecification.scala deleted file mode 100644 index 67bd5e625..000000000 --- a/util/process/src/test/scala/ProcessSpecification.scala +++ /dev/null @@ -1,131 +0,0 @@ -package sbt - -import java.io.File -import org.scalacheck.{ Arbitrary, Gen, Prop, Properties } -import Prop._ - -import Process._ - -object ProcessSpecification extends Properties("Process I/O") { - implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary( - for ( - size <- Gen.choose(0, 10); - l <- Gen.listOfN[Byte](size, Arbitrary.arbByte.arbitrary) - ) yield l.toArray - ) - - /*property("Correct exit code") = forAll( (exitCode: Byte) => checkExit(exitCode)) - property("#&& correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #&& _)(_ && _)) - property("#|| correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _)) - property("### correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))*/ - property("Pipe to output file") = forAll((data: Array[Byte]) => checkFileOut(data)) - property("Pipe from input file") = forAll((data: Array[Byte]) => checkFileIn(data)) - property("Pipe to process") = forAll((data: Array[Byte]) => checkPipe(data)) - property("Pipe to process ignores input exit code") = forAll((data: Array[Byte], code: Byte) => checkPipeExit(data, code)) - property("Pipe from input file to bad process preserves correct exit code.") = forAll((data: Array[Byte], code: Byte) => checkFileInExit(data, code)) - property("Pipe to output file from bad process preserves correct exit code.") = forAll((data: Array[Byte], code: Byte) => checkFileOutExit(data, code)) - - private def checkBinary(codes: Array[Byte])(reduceProcesses: (ProcessBuilder, ProcessBuilder) => ProcessBuilder)(reduceExit: (Boolean, Boolean) => Boolean) = - { - (codes.length > 1) ==> - { - val unsignedCodes = codes.map(unsigned) - val exitCode = unsignedCodes.map(code => Process(process("sbt.exit " + code))).reduceLeft(reduceProcesses) ! - val expectedExitCode = unsignedCodes.map(toBoolean).reduceLeft(reduceExit) - toBoolean(exitCode) == expectedExitCode - } - } - private def toBoolean(exitCode: Int) = exitCode == 0 - private def checkExit(code: Byte) = - { - val exitCode = unsigned(code) - (process("sbt.exit " + exitCode) !) == exitCode - } - private def checkFileOut(data: Array[Byte]) = - { - withData(data) { (temporaryFile, temporaryFile2) => - val catCommand = process("sbt.cat " + temporaryFile.getAbsolutePath) - catCommand #> temporaryFile2 - } - } - private def checkFileIn(data: Array[Byte]) = - { - withData(data) { (temporaryFile, temporaryFile2) => - val catCommand = process("sbt.cat") - temporaryFile #> catCommand #> temporaryFile2 - } - } - private def checkPipe(data: Array[Byte]) = - { - withData(data) { (temporaryFile, temporaryFile2) => - val catCommand = process("sbt.cat") - temporaryFile #> catCommand #| catCommand #> temporaryFile2 - } - } - private def checkPipeExit(data: Array[Byte], code: Byte) = - withTempFiles { (a, b) => - IO.write(a, data) - val catCommand = process("sbt.cat") - val exitCommand = process(s"sbt.exit $code") - val exit = (a #> exitCommand #| catCommand #> b).! - (s"Exit code: $exit") |: - (s"Output file length: ${b.length}") |: - (exit == 0) && - (b.length == 0) - } - - private def checkFileOutExit(data: Array[Byte], exitCode: Byte) = - withTempFiles { (a, b) => - IO.write(a, data) - val code = unsigned(exitCode) - val command = process(s"sbt.exit $code") - val exit = (a #> command #> b).! - (s"Exit code: $exit, expected: $code") |: - (s"Output file length: ${b.length}") |: - (exit == code) && - (b.length == 0) - } - - private def checkFileInExit(data: Array[Byte], exitCode: Byte) = - withTempFiles { (a, b) => - IO.write(a, data) - val code = unsigned(exitCode) - val command = process(s"sbt.exit $code") - val exit = (a #> command).! - (s"Exit code: $exit, expected: $code") |: - (exit == code) - } - - private def temp() = File.createTempFile("sbt", "") - private def withData(data: Array[Byte])(f: (File, File) => ProcessBuilder) = - withTempFiles { (a, b) => - IO.write(a, data) - val process = f(a, b) - (process !) == 0 && sameFiles(a, b) - } - private def sameFiles(a: File, b: File) = - IO.readBytes(a) sameElements IO.readBytes(b) - - private def withTempFiles[T](f: (File, File) => T): T = - { - val temporaryFile1 = temp() - val temporaryFile2 = temp() - try f(temporaryFile1, temporaryFile2) - finally { - temporaryFile1.delete() - temporaryFile2.delete() - } - } - private def unsigned(b: Int): Int = ((b: Int) + 256) % 256 - private def unsigned(b: Byte): Int = unsigned(b: Int) - private def process(command: String) = - { - val ignore = echo // just for the compile dependency so that this test is rerun when TestedProcess.scala changes, not used otherwise - - val thisClasspath = List(getSource[Product], getSource[IO.type], getSource[SourceTag]).mkString(File.pathSeparator) - "java -cp " + thisClasspath + " " + command - } - private def getSource[T: Manifest]: String = - IO.classLocationFile[T].getAbsolutePath -} -private trait SourceTag diff --git a/util/process/src/test/scala/TestedProcess.scala b/util/process/src/test/scala/TestedProcess.scala deleted file mode 100644 index 8dabcf381..000000000 --- a/util/process/src/test/scala/TestedProcess.scala +++ /dev/null @@ -1,46 +0,0 @@ -package sbt - -import java.io.{ File, FileNotFoundException, IOException } - -object exit { - def main(args: Array[String]): Unit = { - System.exit(java.lang.Integer.parseInt(args(0))) - } -} -object cat { - def main(args: Array[String]): Unit = { - try { - if (args.length == 0) - IO.transfer(System.in, System.out) - else - catFiles(args.toList) - System.exit(0) - } catch { - case e: Throwable => - e.printStackTrace() - System.err.println("Error: " + e.toString) - System.exit(1) - } - } - private def catFiles(filenames: List[String]): Option[String] = - { - filenames match { - case head :: tail => - val file = new File(head) - if (file.isDirectory) - throw new IOException("Is directory: " + file) - else if (file.exists) { - Using.fileInputStream(file) { stream => - IO.transfer(stream, System.out) - } - catFiles(tail) - } else - throw new FileNotFoundException("No such file or directory: " + file) - case Nil => None - } - } -} -object echo { - def main(args: Array[String]): Unit = - System.out.println(args.mkString(" ")) -} diff --git a/util/relation/src/main/scala/sbt/Relation.scala b/util/relation/src/main/scala/sbt/Relation.scala deleted file mode 100644 index 9a648ad64..000000000 --- a/util/relation/src/main/scala/sbt/Relation.scala +++ /dev/null @@ -1,172 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Relation._ - -object Relation { - /** Constructs a new immutable, finite relation that is initially empty. */ - def empty[A, B]: Relation[A, B] = make(Map.empty, Map.empty) - - /** - * Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without checking that they are consistent. - * This is a low-level constructor and the alternatives [[empty]] and [[reconstruct]] should be preferred. - */ - def make[A, B](forward: Map[A, Set[B]], reverse: Map[B, Set[A]]): Relation[A, B] = new MRelation(forward, reverse) - - /** Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in `_2s`, `(_1, _2)` is in the relation. */ - def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = - { - val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a) - val reverse = (Map.empty[B, Set[A]] /: reversePairs) { case (m, (b, a)) => add(m, b, a :: Nil) } - make(forward filter { case (a, bs) => bs.nonEmpty }, reverse) - } - - def merge[A, B](rels: Traversable[Relation[A, B]]): Relation[A, B] = (Relation.empty[A, B] /: rels)(_ ++ _) - - private[sbt] def remove[X, Y](map: M[X, Y], from: X, to: Y): M[X, Y] = - map.get(from) match { - case Some(tos) => - val newSet = tos - to - if (newSet.isEmpty) map - from else map.updated(from, newSet) - case None => map - } - - private[sbt] def combine[X, Y](a: M[X, Y], b: M[X, Y]): M[X, Y] = - (a /: b) { (map, mapping) => add(map, mapping._1, mapping._2) } - - private[sbt] def add[X, Y](map: M[X, Y], from: X, to: Traversable[Y]): M[X, Y] = - map.updated(from, get(map, from) ++ to) - - private[sbt] def get[X, Y](map: M[X, Y], t: X): Set[Y] = map.getOrElse(t, Set.empty[Y]) - - private[sbt]type M[X, Y] = Map[X, Set[Y]] -} - -/** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */ -trait Relation[A, B] { - /** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */ - def forward(_1: A): Set[B] - /** Returns the set of all `_1`s such that `(_1, _2)` is in this relation. */ - def reverse(_2: B): Set[A] - /** Includes `pair` in the relation. */ - def +(pair: (A, B)): Relation[A, B] - /** Includes `(a, b)` in the relation. */ - def +(a: A, b: B): Relation[A, B] - /** Includes in the relation `(a, b)` for all `b` in `bs`. */ - def +(a: A, bs: Traversable[B]): Relation[A, B] - /** Returns the union of the relation `r` with this relation. */ - def ++(r: Relation[A, B]): Relation[A, B] - /** Includes the given pairs in this relation. */ - def ++(rs: Traversable[(A, B)]): Relation[A, B] - /** Removes all elements `(_1, _2)` for all `_1` in `_1s` from this relation. */ - def --(_1s: Traversable[A]): Relation[A, B] - /** Removes all `pairs` from this relation. */ - def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] - /** Removes all `relations` from this relation. */ - def --(relations: Relation[A, B]): Relation[A, B] - /** Removes all pairs `(_1, _2)` from this relation. */ - def -(_1: A): Relation[A, B] - /** Removes `pair` from this relation. */ - def -(pair: (A, B)): Relation[A, B] - /** Returns the set of all `_1`s such that `(_1, _2)` is in this relation. */ - def _1s: collection.Set[A] - /** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */ - def _2s: collection.Set[B] - /** Returns the number of pairs in this relation */ - def size: Int - - /** Returns true iff `(a,b)` is in this relation*/ - def contains(a: A, b: B): Boolean - - /** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true.*/ - def filter(f: (A, B) => Boolean): Relation[A, B] - - /** - * Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true and - * the other only pairs `(a,b)` for which `f(a,b)` is false. - */ - def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B]) - - /** Partitions this relation into a map of relations according to some discriminator function. */ - def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]] - - /** Returns all pairs in this relation.*/ - def all: Traversable[(A, B)] - - /** - * Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in this relation. - * - * Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for some `_2`. - * The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)` is in this relation. - */ - def forwardMap: Map[A, Set[B]] - - /** - * Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in this relation. - * - * Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for some `_1`. - * The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)` is in this relation. - */ - def reverseMap: Map[B, Set[A]] -} - -// Note that we assume without checking that fwd and rev are consistent. -private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) extends Relation[A, B] { - def forwardMap = fwd - def reverseMap = rev - - def forward(t: A) = get(fwd, t) - def reverse(t: B) = get(rev, t) - - def _1s = fwd.keySet - def _2s = rev.keySet - - def size = (fwd.valuesIterator map (_.size)).sum - - def all: Traversable[(A, B)] = fwd.iterator.flatMap { case (a, bs) => bs.iterator.map(b => (a, b)) }.toTraversable - - def +(pair: (A, B)) = this + (pair._1, Set(pair._2)) - def +(from: A, to: B) = this + (from, to :: Nil) - def +(from: A, to: Traversable[B]) = if (to.isEmpty) this else - new MRelation(add(fwd, from, to), (rev /: to) { (map, t) => add(map, t, from :: Nil) }) - - def ++(rs: Traversable[(A, B)]) = ((this: Relation[A, B]) /: rs) { _ + _ } - def ++(other: Relation[A, B]) = new MRelation[A, B](combine(fwd, other.forwardMap), combine(rev, other.reverseMap)) - - def --(ts: Traversable[A]): Relation[A, B] = ((this: Relation[A, B]) /: ts) { _ - _ } - def --(pairs: TraversableOnce[(A, B)]): Relation[A, B] = ((this: Relation[A, B]) /: pairs) { _ - _ } - def --(relations: Relation[A, B]): Relation[A, B] = --(relations.all) - def -(pair: (A, B)): Relation[A, B] = - new MRelation(remove(fwd, pair._1, pair._2), remove(rev, pair._2, pair._1)) - def -(t: A): Relation[A, B] = - fwd.get(t) match { - case Some(rs) => - val upRev = (rev /: rs) { (map, r) => remove(map, r, t) } - new MRelation(fwd - t, upRev) - case None => this - } - - def filter(f: (A, B) => Boolean): Relation[A, B] = Relation.empty[A, B] ++ all.filter(f.tupled) - - def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B]) = { - val (y, n) = all.partition(f.tupled) - (Relation.empty[A, B] ++ y, Relation.empty[A, B] ++ n) - } - - def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]] = all.groupBy(discriminator) mapValues { Relation.empty[A, B] ++ _ } - - def contains(a: A, b: B): Boolean = forward(a)(b) - - override def equals(other: Any) = other match { - // We assume that the forward and reverse maps are consistent, so we only use the forward map - // for equality. Note that key -> Empty is semantically the same as key not existing. - case o: MRelation[A, B] => forwardMap.filterNot(_._2.isEmpty) == o.forwardMap.filterNot(_._2.isEmpty) - case _ => false - } - - override def hashCode = fwd.filterNot(_._2.isEmpty).hashCode() - - override def toString = all.map { case (a, b) => a + " -> " + b }.mkString("Relation [", ", ", "]") -} diff --git a/util/relation/src/test/scala/RelationTest.scala b/util/relation/src/test/scala/RelationTest.scala deleted file mode 100644 index 558935bdb..000000000 --- a/util/relation/src/test/scala/RelationTest.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import org.scalacheck._ -import Prop._ - -object RelationTest extends Properties("Relation") { - property("Added entry check") = forAll { (pairs: List[(Int, Double)]) => - val r = Relation.empty[Int, Double] ++ pairs - check(r, pairs) - } - def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = - { - val _1s = pairs.map(_._1).toSet - val _2s = pairs.map(_._2).toSet - - r._1s == _1s && r.forwardMap.keySet == _1s && - r._2s == _2s && r.reverseMap.keySet == _2s && - pairs.forall { - case (a, b) => - (r.forward(a) contains b) && - (r.reverse(b) contains a) && - (r.forwardMap(a) contains b) && - (r.reverseMap(b) contains a) - } - } - - property("Does not contain removed entries") = forAll { (pairs: List[(Int, Double, Boolean)]) => - val add = pairs.map { case (a, b, c) => (a, b) } - val added = Relation.empty[Int, Double] ++ add - - val removeFine = pairs.collect { case (a, b, true) => (a, b) } - val removeCoarse = removeFine.map(_._1) - val r = added -- removeCoarse - - def notIn[X, Y](map: Map[X, Set[Y]], a: X, b: Y) = map.get(a).forall(set => !(set contains b)) - - all(removeCoarse) { rem => - ("_1s does not contain removed" |: (!r._1s.contains(rem))) && - ("Forward does not contain removed" |: r.forward(rem).isEmpty) && - ("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) && - ("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem)) - } && - all(removeFine) { - case (a, b) => - ("Forward does not contain removed" |: (!r.forward(a).contains(b))) && - ("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) && - ("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) && - ("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a))) - } - } - - property("Groups correctly") = forAll { (entries: List[(Int, Double)], randomInt: Int) => - val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups. - val rel = Relation.empty[Int, Double] ++ entries - val grouped = rel groupBy (_._1 % splitInto) - all(grouped.toSeq) { - case (k, rel_k) => rel_k._1s forall { _ % splitInto == k } - } - } - - property("Computes size correctly") = forAll { (entries: List[(Int, Double)]) => - val rel = Relation.empty[Int, Double] ++ entries - val expected = rel.all.size // Note: not entries.length, as entries may have duplicates. - val computed = rel.size - "Expected size: %d. Computed size: %d.".format(expected, computed) |: expected == computed - } - - def all[T](s: Seq[T])(p: T => Prop): Prop = - if (s.isEmpty) true else s.map(p).reduceLeft(_ && _) -} - -object EmptyRelationTest extends Properties("Empty relation") { - lazy val e = Relation.empty[Int, Double] - - property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty } - property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty } - property("Forward map empty") = e.forwardMap.isEmpty - property("Reverse map empty") = e.reverseMap.isEmpty - property("_1 empty") = e._1s.isEmpty - property("_2 empty") = e._2s.isEmpty -} \ No newline at end of file