mirror of https://github.com/sbt/sbt.git
Merge branch '0.12' of git://github.com/harrah/xsbt into 0.12
This commit is contained in:
commit
1f0a45e950
|
|
@ -61,7 +61,7 @@ class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentPr
|
|||
/** 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("org.scala-tools.sbt", id, ComponentManager.stampedVersion)
|
||||
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) }
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ package sbt
|
|||
final case class ConflictWarning(label: String, filter: ModuleFilter, group: ModuleID => String, level: Level.Value, failOnConflict: Boolean)
|
||||
object ConflictWarning
|
||||
{
|
||||
def default(label: String): ConflictWarning = ConflictWarning(label, moduleFilter(organization = GlobFilter("org.scala-tools.sbt") | GlobFilter("org.scala-lang")), (_: ModuleID).organization, Level.Warn, false)
|
||||
def default(label: String): ConflictWarning = ConflictWarning(label, moduleFilter(organization = GlobFilter(SbtArtifacts.Organization) | GlobFilter(ScalaArtifacts.Organization)), (_: ModuleID).organization, Level.Warn, false)
|
||||
|
||||
def apply(config: ConflictWarning, report: UpdateReport, log: Logger)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
package sbt
|
||||
|
||||
final case class ScalaVersion(full: String, binary: String)
|
||||
|
||||
sealed trait CrossVersion
|
||||
object CrossVersion
|
||||
{
|
||||
val TransitionScalaVersion = "2.10"
|
||||
val TransitionSbtVersion = "0.12"
|
||||
|
||||
object Disabled extends CrossVersion { override def toString = "disabled" }
|
||||
final class Binary(val remapVersion: String => String) extends CrossVersion {
|
||||
override def toString = "Binary"
|
||||
}
|
||||
final class Full(val remapVersion: String => String) extends CrossVersion {
|
||||
override def toString = "Full"
|
||||
}
|
||||
|
||||
def full: CrossVersion = new Full(idFun)
|
||||
def fullMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion)
|
||||
|
||||
def binary: CrossVersion = new Binary(idFun)
|
||||
def binaryMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion)
|
||||
|
||||
private[this] def idFun[T]: T => T = x => x
|
||||
def append(s: String): Option[String => String] = Some(x => crossName(x, s))
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
def apply(module: ModuleID, is: IvyScala): Option[String => String] =
|
||||
CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion)
|
||||
|
||||
def apply(module: ModuleID, is: Option[IvyScala]): Option[String => String] =
|
||||
is flatMap { i => apply(module, i) }
|
||||
|
||||
def substituteCross(artifacts: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
|
||||
cross match {
|
||||
case None => artifacts
|
||||
case Some(is) => substituteCrossA(artifacts, cross)
|
||||
}
|
||||
|
||||
def applyCross(s: String, fopt: Option[String => String]): String =
|
||||
fopt match {
|
||||
case None => s
|
||||
case Some(fopt) => fopt(s)
|
||||
}
|
||||
|
||||
def crossName(name: String, cross: String): String =
|
||||
name + "_" + cross
|
||||
def substituteCross(a: Artifact, cross: Option[String => String]): Artifact =
|
||||
a.copy(name = applyCross(a.name, cross))
|
||||
def substituteCrossA(as: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
|
||||
as.map(art => substituteCross(art, cross))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
def isStable(v: String): Boolean = !v.contains("-")
|
||||
def selectVersion(full: String, binary: String): String = if(isStable(full)) binary else full
|
||||
|
||||
val PartialVersion = """(\d+)\.(\d+)(?:\..+)?""".r
|
||||
def partialVersion(s: String): Option[(Int,Int)] =
|
||||
s match {
|
||||
case PartialVersion(major, minor) => Some(major.toInt, minor.toInt)
|
||||
case _ => None
|
||||
}
|
||||
private[this] def isNewer(major: Int, minor: Int, minMajor: Int, minMinor: Int): Boolean =
|
||||
major > minMajor || (major == minMajor && minor >= minMinor)
|
||||
|
||||
def binaryScalaVersion(full: String): String = binaryVersion(full, TransitionScalaVersion)
|
||||
def binarySbtVersion(full: String): String = binaryVersion(full, TransitionSbtVersion)
|
||||
def binaryVersion(full: String, cutoff: String): String =
|
||||
{
|
||||
def sub(major: Int, minor: Int) = major + "." + minor
|
||||
(partialVersion(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,21 +27,28 @@ trait DependencyBuilders
|
|||
|
||||
final class GroupID private[sbt] (groupID: String)
|
||||
{
|
||||
def % (artifactID: String) = groupArtifact(artifactID, None)
|
||||
def %% (artifactID: String, crossVersion: String => String = identity) = groupArtifact(artifactID, Some(crossVersion))
|
||||
def %% (artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, Some(Map(alternatives: _*) orElse { case s => s }))
|
||||
private def groupArtifact(artifactID: String, cross: Option[String => 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] (groupID: String, artifactID: String, crossVersion: Option[String => String])
|
||||
final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: CrossVersion)
|
||||
{
|
||||
def % (revision: String): ModuleID =
|
||||
{
|
||||
nonEmpty(revision, "Revision")
|
||||
ModuleID(groupID, artifactID, revision).cross(!crossVersion.isEmpty, crossVersion.getOrElse(identity))
|
||||
ModuleID(groupID, artifactID, revision).cross(crossVersion)
|
||||
}
|
||||
}
|
||||
final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID)
|
||||
|
|
|
|||
|
|
@ -299,24 +299,19 @@ private object IvySbt
|
|||
}
|
||||
|
||||
private def substituteCross(m: ModuleSettings): ModuleSettings =
|
||||
m.ivyScala match { case None => m; case Some(is) => substituteCross(m, is.substituteCross) }
|
||||
private def substituteCross(m: ModuleSettings, sub: ModuleID => ModuleID): 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)
|
||||
case _ => m
|
||||
}
|
||||
def crossName(name: String, cross: String): String =
|
||||
name + "_" + cross
|
||||
def substituteCross(a: Artifact, cross: String): Artifact =
|
||||
a.copy(name = crossName(a.name, cross))
|
||||
def substituteCrossA(as: Seq[Artifact], cross: String): Seq[Artifact] =
|
||||
as.map(art => substituteCross(art, cross))
|
||||
def substituteCross(m: ModuleID, cross: String): ModuleID =
|
||||
if(m.crossVersion)
|
||||
m.copy(name = crossName(m.name, m.crossVersionRemap(cross)), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross))
|
||||
else
|
||||
m
|
||||
}
|
||||
|
||||
private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, configurations: Iterable[String]): MDArtifact =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ object IvyActions
|
|||
val resolver = ivy.getSettings.getResolver(resolverName)
|
||||
if(resolver eq null) error("Undefined resolver '" + resolverName + "'")
|
||||
val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) }
|
||||
val is = crossIvyScala(module.moduleSettings)
|
||||
val as = mapArtifacts(md, is, artifacts) ++ ivyArtifact.toList
|
||||
val cross = crossVersionMap(module.moduleSettings)
|
||||
val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toList
|
||||
withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = true) }
|
||||
}
|
||||
}
|
||||
|
|
@ -102,18 +102,16 @@ object IvyActions
|
|||
try { act }
|
||||
finally { resolver.setChecksums(previous mkString ",") }
|
||||
}
|
||||
private def crossIvyScala(moduleSettings: ModuleSettings): Option[IvyScala] =
|
||||
private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] =
|
||||
moduleSettings match {
|
||||
case i: InlineConfiguration if i.module.crossVersion => i.ivyScala
|
||||
case e: EmptyConfiguration if e.module.crossVersion => e.ivyScala
|
||||
case i: InlineConfiguration => CrossVersion(i.module, i.ivyScala)
|
||||
case e: EmptyConfiguration => CrossVersion(e.module, e.ivyScala)
|
||||
case _ => None
|
||||
}
|
||||
def substituteCross(ivyScala: Option[IvyScala], artifacts: Seq[Artifact]): Seq[Artifact] =
|
||||
ivyScala match { case None => artifacts; case Some(is) => IvySbt.substituteCrossA(artifacts, is.scalaVersion) }
|
||||
def mapArtifacts(module: ModuleDescriptor, ivyScala: Option[IvyScala], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] =
|
||||
def mapArtifacts(module: ModuleDescriptor, cross: Option[String => String], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] =
|
||||
{
|
||||
val rawa = artifacts.keys.toSeq
|
||||
val seqa = substituteCross(ivyScala, rawa)
|
||||
val seqa = CrossVersion.substituteCross(rawa, cross)
|
||||
val zipped = rawa zip IvySbt.mapArtifacts(module, seqa)
|
||||
zipped map { case (a, ivyA) => (ivyA, artifacts(a)) }
|
||||
}
|
||||
|
|
@ -199,7 +197,7 @@ object IvyActions
|
|||
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, crossVersionRemap = m.crossVersionRemap, extraAttributes = m.extraAttributes, configurations = if(confs) m.configurations else None)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,14 +9,21 @@ import scala.xml.NodeSeq
|
|||
import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver}
|
||||
import org.apache.ivy.util.url.CredentialsStore
|
||||
|
||||
final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String,String] = Map.empty, crossVersion: Boolean = false, crossVersionRemap: String => String = identity)
|
||||
final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String,String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled)
|
||||
{
|
||||
override def toString =
|
||||
organization + ":" + name + ":" + revision +
|
||||
(configurations match { case Some(s) => ":" + s; case None => "" }) +
|
||||
(if(extraAttributes.isEmpty) "" else " " + extraString)
|
||||
def extraString = extraAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")")
|
||||
def cross(v: Boolean, verRemap: String => String = identity) = copy(crossVersion = v, crossVersionRemap = verRemap)
|
||||
|
||||
@deprecated("Use the variant accepting a CrossVersion value constructed by a member of the CrossVersion object.", "0.12.0")
|
||||
def cross(v: Boolean): ModuleID = cross(if(v) CrossVersion.binary else CrossVersion.Disabled)
|
||||
@deprecated("Use the variant accepting a CrossVersion value constructed by a member of the CrossVersion object.", "0.12.0")
|
||||
def cross(v: Boolean, verRemap: String => String): ModuleID = cross(if(v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled)
|
||||
|
||||
def cross(v: CrossVersion): ModuleID = copy(crossVersion = v)
|
||||
|
||||
// () required for chaining
|
||||
def notTransitive() = intransitive()
|
||||
def intransitive() = copy(isTransitive = false)
|
||||
|
|
@ -199,8 +206,7 @@ object Resolver
|
|||
def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean, scalaTools: Boolean): Seq[Resolver] =
|
||||
Seq(Resolver.defaultLocal) ++
|
||||
userResolvers ++
|
||||
single(DefaultMavenRepository, mavenCentral)++
|
||||
single(ScalaToolsReleases, scalaTools)
|
||||
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. */
|
||||
|
|
@ -419,14 +425,14 @@ object Artifact
|
|||
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: String, module: ModuleID, artifact: Artifact): String =
|
||||
def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String =
|
||||
{
|
||||
import artifact._
|
||||
val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c }
|
||||
val base = if(module.crossVersion) IvySbt.crossName(artifact.name, module.crossVersionRemap(scalaVersion)) else artifact.name
|
||||
val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary)
|
||||
val base = CrossVersion.applyCross(artifact.name, cross)
|
||||
base + "-" + module.revision + classifierStr + "." + artifact.extension
|
||||
}
|
||||
def cross(enable: Boolean, scalaVersion: String): String = if(enable) "_" + scalaVersion else ""
|
||||
|
||||
val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs)
|
||||
val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType)
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@ object ScalaArtifacts
|
|||
val CompilerID = "scala-compiler"
|
||||
def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version)
|
||||
}
|
||||
object SbtArtifacts
|
||||
{
|
||||
val Organization = "org.scala-sbt"
|
||||
}
|
||||
|
||||
import ScalaArtifacts._
|
||||
|
||||
final case class IvyScala(scalaVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean, substituteCross: ModuleID => ModuleID)
|
||||
final case class IvyScala(scalaFullVersion: String, scalaBinaryVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean)
|
||||
|
||||
private object IvyScala
|
||||
{
|
||||
|
|
@ -30,11 +34,11 @@ private object IvyScala
|
|||
def checkModule(module: DefaultModuleDescriptor, conf: String)(check: IvyScala)
|
||||
{
|
||||
if(check.checkExplicit)
|
||||
checkDependencies(module, check.scalaVersion, check.configurations)
|
||||
checkDependencies(module, check.scalaBinaryVersion, check.configurations)
|
||||
if(check.filterImplicit)
|
||||
excludeScalaJars(module, check.configurations)
|
||||
if(check.overrideScalaVersion)
|
||||
overrideScalaVersion(module, check.scalaVersion)
|
||||
overrideScalaVersion(module, check.scalaFullVersion)
|
||||
}
|
||||
def overrideScalaVersion(module: DefaultModuleDescriptor, version: String)
|
||||
{
|
||||
|
|
@ -50,14 +54,15 @@ private object IvyScala
|
|||
|
||||
/** 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, scalaVersion: String, configurations: Iterable[Configuration])
|
||||
private def checkDependencies(module: ModuleDescriptor, scalaBinaryVersion: String, configurations: Iterable[Configuration])
|
||||
{
|
||||
val configSet = if(configurations.isEmpty) (c: String) => true else configurationSet(configurations)
|
||||
for(dep <- module.getDependencies.toList)
|
||||
{
|
||||
val id = dep.getDependencyRevisionId
|
||||
if(id.getOrganisation == Organization && id.getRevision != scalaVersion && dep.getModuleConfigurations.exists(configSet))
|
||||
error("Version specified for dependency " + id + " differs from Scala version in project (" + scalaVersion + ").")
|
||||
val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision)
|
||||
if(id.getOrganisation == Organization && depBinaryVersion != scalaBinaryVersion && dep.getModuleConfigurations.exists(configSet))
|
||||
error("Binary version for dependency " + id + " (" + depBinaryVersion + ") differs from Scala binary version in project (" + scalaBinaryVersion + ").")
|
||||
}
|
||||
}
|
||||
private def configurationSet(configurations: Iterable[Configuration]) = configurations.map(_.toString).toSet
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ private object BootConfiguration
|
|||
|
||||
val JUnitName = "junit"
|
||||
|
||||
val SbtOrg = "org.scala-tools.sbt"
|
||||
val SbtOrg = "org.scala-sbt"
|
||||
|
||||
/** The Ivy conflict manager to use for updating.*/
|
||||
val ConflictManagerName = "latest-revision"
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class ConfigurationParser
|
|||
|
||||
def getApplication(m: LabelMap): (Application, Value[List[String]]) =
|
||||
{
|
||||
val (org, m1) = id(m, "org", "org.scala-tools.sbt")
|
||||
val (org, m1) = id(m, "org", BootConfiguration.SbtOrg)
|
||||
val (name, m2) = id(m1, "name", "sbt")
|
||||
val (rev, m3) = getVersion(m2, name + " version", name + ".version")
|
||||
val (main, m4) = id(m3, "class", "xsbt.Main")
|
||||
|
|
@ -171,7 +171,7 @@ class ConfigurationParser
|
|||
m.toList.map {
|
||||
case (key, None) => Predefined(key)
|
||||
case (key, Some(value)) =>
|
||||
val r = trim(value.split(",",3))
|
||||
val r = trim(substituteVariables(value).split(",",3))
|
||||
val url = try { new URL(r(0)) } catch { case e: MalformedURLException => error("Invalid URL specified for '" + key + "': " + e.getMessage) }
|
||||
if(r.length == 3) Ivy(key, url, r(1), r(2)) else if(r.length == 2) Ivy(key, url, r(1), r(1)) else Maven(key, url)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
local
|
||||
${{repositories}}
|
||||
maven-central
|
||||
scala-tools-releases
|
||||
scala-tools-snapshots
|
||||
|
||||
[boot]
|
||||
directory: ${sbt.boot.directory-${sbt.global.base-${user.home}/.sbt}/boot/}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ object ScalaProviderTest extends Specification
|
|||
object LaunchTest
|
||||
{
|
||||
def testApp(main: String): Application = testApp(main, Array[File]())
|
||||
def testApp(main: String, extra: Array[File]): Application = Application("org.scala-tools.sbt", "launch-test", new Explicit(AppVersion), main, Nil, false, extra)
|
||||
def testApp(main: String, extra: Array[File]): Application = Application("org.scala-sbt", "launch-test", new Explicit(AppVersion), main, Nil, false, extra)
|
||||
import Predefined._
|
||||
def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply)
|
||||
def withLauncher[T](f: xsbti.Launcher => T): T =
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ package sbt
|
|||
import DefaultParsers._
|
||||
import Types.idFun
|
||||
import java.net.URI
|
||||
import CommandSupport.ShowCommand
|
||||
import CommandStrings.ShowCommand
|
||||
|
||||
final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)
|
||||
object Act
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ final object Aggregation
|
|||
Dag.topologicalSort(key) { k =>
|
||||
if(reverse)
|
||||
reverseAggregatedKeys(k, extra, mask)
|
||||
else if(aggregationEnabled(key, extra.data))
|
||||
else if(aggregationEnabled(k, extra.data))
|
||||
aggregatedKeys(k, extra, mask)
|
||||
else
|
||||
Nil
|
||||
|
|
|
|||
|
|
@ -38,4 +38,12 @@ object Append
|
|||
def appendValues(a: Classpath, b: Seq[File]): Classpath = a ++ Attributed.blankSeq(b)
|
||||
def appendValue(a: Classpath, b: File): Classpath = a :+ Attributed.blank(b)
|
||||
}
|
||||
implicit def appendSet[T, V <: T]: Sequence[Set[T], Set[V], V] = new Sequence[Set[T], Set[V], V] {
|
||||
def appendValues(a: Set[T], b: Set[V]): Set[T] = a ++ b
|
||||
def appendValue(a: Set[T], b: V): Set[T] = a + b
|
||||
}
|
||||
implicit def appendMap[A,B, X <: A, Y <: B]: Sequence[Map[A,B], Map[X,Y], (X,Y)] = new Sequence[Map[A,B], Map[X,Y], (X,Y)] {
|
||||
def appendValues(a: Map[A,B], b: Map[X,Y]): Map[A,B] = a ++ b
|
||||
def appendValue(a: Map[A,B], b: (X,Y)): Map[A,B] = a + b
|
||||
}
|
||||
}
|
||||
|
|
@ -3,37 +3,15 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import complete.HistoryCommands
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import java.io.File
|
||||
import Path._
|
||||
|
||||
object CommandSupport
|
||||
object CommandStrings
|
||||
{
|
||||
def logger(s: State) = globalLogging(s).full
|
||||
def globalLogging(s: State) = s get Keys.globalLogging getOrElse error("Global logging misconfigured")
|
||||
@deprecated("Use the `log` member of a State instance directly.", "0.12.0")
|
||||
def logger(s: State) = s.log
|
||||
|
||||
// slightly better fallback in case of older launcher
|
||||
def bootDirectory(state: State): File =
|
||||
try { state.configuration.provider.scalaProvider.launcher.bootDirectory }
|
||||
catch { case e: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||
|
||||
private def canRead = (_: File).canRead
|
||||
def notReadable(files: Seq[File]): Seq[File] = files filterNot canRead
|
||||
def readable(files: Seq[File]): Seq[File] = files filter canRead
|
||||
def sbtRCs(s: State): Seq[File] =
|
||||
(Path.userHome / sbtrc) ::
|
||||
(s.baseDir / sbtrc asFile) ::
|
||||
Nil
|
||||
|
||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (line => IO.readLines(line)) flatMap processLine
|
||||
def processLine(s: String) = { val trimmed = s.trim; if(ignoreLine(trimmed)) None else Some(trimmed) }
|
||||
def ignoreLine(s: String) = s.isEmpty || s.startsWith("#")
|
||||
@deprecated("Use the `globalLogging` member of a State instance directly.", "0.12.0")
|
||||
def globalLogging(s: State) = s.globalLogging
|
||||
|
||||
/** The prefix used to identify a request to execute the remaining input on source changes.*/
|
||||
val ContinuousExecutePrefix = "~"
|
||||
val HelpCommand = "help"
|
||||
val AboutCommand = "about"
|
||||
val TasksCommand = "tasks"
|
||||
val ProjectCommand = "project"
|
||||
|
|
@ -41,8 +19,14 @@ object CommandSupport
|
|||
val ShowCommand = "show"
|
||||
val BootCommand = "boot"
|
||||
|
||||
val Exit = "exit"
|
||||
val Quit = "quit"
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
val ContinuousExecutePrefix = BasicCommandStrings.ContinuousExecutePrefix
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
val Exit = BasicCommandStrings.Exit
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
val Quit = BasicCommandStrings.Quit
|
||||
|
||||
val EvalCommand = "eval"
|
||||
val evalBrief = (EvalCommand + " <expression>", "Evaluates the given Scala expression and prints the result and type.")
|
||||
|
|
@ -128,9 +112,8 @@ SetCommand + """ <setting-expression>
|
|||
def sessionBrief = (SessionCommand + " <session-command>", "Manipulates session settings. For details, run 'help " + SessionCommand + "'.")
|
||||
|
||||
/** The command name to terminate the program.*/
|
||||
val TerminateAction: String = Exit
|
||||
|
||||
def continuousBriefHelp = (ContinuousExecutePrefix + " <command>", "Executes the specified command whenever source files change.")
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
val TerminateAction: String = BasicCommandStrings.TerminateAction
|
||||
|
||||
def tasksPreamble = """
|
||||
This is a list of tasks defined for the current project.
|
||||
|
|
@ -140,11 +123,6 @@ Tasks produce values. Use the 'show' command to run the task and print the resu
|
|||
def tasksBrief = "Displays the tasks defined for the current project."
|
||||
def tasksDetailed = "Displays the tasks defined directly or indirectly for the current project."
|
||||
|
||||
def helpBrief = (HelpCommand + " [command]*", "Displays this help message or prints detailed help on requested commands.")
|
||||
def helpDetailed = """
|
||||
If an argument is provided, this prints detailed help for that command.
|
||||
Otherwise, this prints a help summary."""
|
||||
|
||||
def aboutBrief = "Displays basic information about sbt and the build."
|
||||
def aboutDetailed = aboutBrief
|
||||
|
||||
|
|
@ -175,126 +153,23 @@ ProjectCommand +
|
|||
def projectsBrief = projectsDetailed
|
||||
def projectsDetailed = "Displays the names of available projects."
|
||||
|
||||
def historyHelp = Help.briefDetail(HistoryCommands.descriptions)
|
||||
|
||||
def exitBrief = "Terminates the build."
|
||||
|
||||
def sbtrc = ".sbtrc"
|
||||
|
||||
def ReadCommand = "<"
|
||||
def ReadFiles = " file1 file2 ..."
|
||||
def ReadBrief = (ReadCommand + " <file>*", "Reads command lines from the provided files.")
|
||||
def ReadDetailed =
|
||||
ReadCommand + ReadFiles + """
|
||||
|
||||
Reads the lines from the given files and inserts them as commands.
|
||||
All empty lines and lines that start with '#' are ignored.
|
||||
If a file does not exist or is not readable, this command fails.
|
||||
|
||||
All the lines from all the files are read before any of the commands
|
||||
are executed. Thus, if any file is not readable, none of commands
|
||||
from any of the files (even the existing ones) will be run.
|
||||
|
||||
You probably need to escape this command if entering it at your shell."""
|
||||
|
||||
def ApplyCommand = "apply"
|
||||
def ApplyBrief = (ApplyCommand + " <module-name>*", ApplyDetailed)
|
||||
def ApplyDetailed = "Transforms the current State by calling <module-name>.apply(currentState) for each listed."
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def ReadCommand = BasicCommandStrings.ReadCommand
|
||||
|
||||
def DefaultsCommand = "add-default-commands"
|
||||
def DefaultsBrief = (DefaultsCommand, DefaultsDetailed)
|
||||
def DefaultsDetailed = "Registers default built-in commands"
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def RebootCommand = "reboot"
|
||||
def RebootSummary = RebootCommand + " [full]"
|
||||
def RebootBrief = (RebootSummary, "Reboots sbt and then executes the remaining commands.")
|
||||
def RebootDetailed =
|
||||
RebootSummary + """
|
||||
|
||||
This command is equivalent to exiting sbt, restarting, and running the
|
||||
remaining commands with the exception that the JVM is not shut down.
|
||||
|
||||
If 'full' is specified, the boot directory (`~/.sbt/boot` by default)
|
||||
is deleted before restarting. This forces an update of sbt and Scala
|
||||
and is useful when working with development versions of sbt or Scala."""
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def Multi = ";"
|
||||
def MultiBrief = (Multi + " <command> (" + Multi + " <command>)*", "Runs the provided semicolon-separated commands.")
|
||||
def MultiDetailed =
|
||||
Multi + " command1 " + Multi + """ command2 ...
|
||||
|
||||
Runs the specified commands."""
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def AppendCommand = "append"
|
||||
def AppendLastBrief = (AppendCommand + " <command>", AppendLastDetailed)
|
||||
def AppendLastDetailed = "Appends 'command' to list of commands to run."
|
||||
|
||||
val AliasCommand = "alias"
|
||||
def AliasBrief = (AliasCommand, "Adds, removes, or prints command aliases.")
|
||||
def AliasDetailed =
|
||||
AliasCommand + """
|
||||
|
||||
Prints a list of defined aliases.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name
|
||||
|
||||
Prints the alias defined for `name`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=value
|
||||
|
||||
Sets the alias `name` to `value`, replacing any existing alias with that name.
|
||||
Whenever `name` is entered, the corresponding `value` is run.
|
||||
If any argument is provided to `name`, it is appended as argument to `value`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=
|
||||
|
||||
Removes the alias for `name`."""
|
||||
|
||||
def Discover = "discover"
|
||||
def DiscoverBrief = (DiscoverSyntax, "Finds annotated classes and subclasses.")
|
||||
def DiscoverSyntax = Discover + " [-module true|false] [-sub <names>] [-annot <names>]"
|
||||
def DiscoverDetailed =
|
||||
DiscoverSyntax + """
|
||||
|
||||
Looks for public, concrete classes that match the requested query using the current sbt.inc.Analysis instance.
|
||||
|
||||
-module
|
||||
Specifies whether modules (true) or classes (false) are found.
|
||||
The default is classes/traits (false).
|
||||
|
||||
-sub
|
||||
Specifies comma-separated class names.
|
||||
Classes that have one or more of these classes as an ancestor are included in the resulting list.
|
||||
|
||||
-annot
|
||||
Specifies comma-separated annotation names.
|
||||
Classes with one or more of these annotations on the class or one of its non-private methods are included in the resulting list.
|
||||
"""
|
||||
|
||||
def CompileName = "direct-compile"
|
||||
def CompileBrief = (CompileSyntax, "Incrementally compiles the provided sources.")
|
||||
def CompileSyntax = CompileName + " -src <paths> [-cp <paths>] [-d <path>]"
|
||||
def CompileDetailed =
|
||||
CompileSyntax + """
|
||||
|
||||
Incrementally compiles Scala and Java sources.
|
||||
|
||||
<paths> are explicit paths separated by the platform path separator.
|
||||
|
||||
The specified output path will contain the following directory structure:
|
||||
|
||||
scala_<version>/
|
||||
classes/
|
||||
cache/
|
||||
|
||||
Compiled classes will be written to the 'classes' directory.
|
||||
Cached information about the compilation will be written to 'cache'.
|
||||
"""
|
||||
|
||||
val FailureWall = "---"
|
||||
|
||||
def Load = "load"
|
||||
def LoadLabel = "a project"
|
||||
|
|
@ -308,31 +183,20 @@ CompileSyntax + """
|
|||
def LoadProjectBrief = (LoadProject, LoadProjectDetailed)
|
||||
def LoadProjectDetailed = "Loads the project in the current directory"
|
||||
|
||||
def Shell = "shell"
|
||||
def ShellBrief = ShellDetailed
|
||||
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
||||
@deprecated("Moved to State", "0.12.0")
|
||||
val FailureWall = State.FailureWall
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def Shell = BasicCommandStrings.Shell
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def ClearOnFailure = "--"
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def OnFailure = "-"
|
||||
def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.")
|
||||
def OnFailureDetailed =
|
||||
OnFailure + """ command
|
||||
|
||||
Registers 'command' to run when a command fails to complete normally.
|
||||
|
||||
Only one failure command may be registered at a time, so this command
|
||||
replaces the previous command if there is one.
|
||||
|
||||
The failure command resets when it runs once, so it must be added
|
||||
again if desired."""
|
||||
|
||||
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||
def IfLast = "iflast"
|
||||
def IfLastBrief = (IfLast + " <command>", IfLastCommon)
|
||||
def IfLastCommon = "If there are no more commands after this one, 'command' is run."
|
||||
def IfLastDetailed =
|
||||
IfLast + """ command
|
||||
|
||||
""" + IfLastCommon
|
||||
|
||||
def InitCommand = "initialize"
|
||||
def InitBrief = (InitCommand, "Initializes command processing.")
|
||||
|
|
@ -352,4 +216,12 @@ load-commands -base ~/.sbt/commands
|
|||
< .sbtrc
|
||||
Runs commands from ~/.sbtrc and ./.sbtrc if they exist
|
||||
"""
|
||||
|
||||
import java.io.File
|
||||
import Path._
|
||||
|
||||
def sbtRCs(s: State): Seq[File] =
|
||||
(Path.userHome / sbtrc) ::
|
||||
(s.baseDir / sbtrc asFile) ::
|
||||
Nil
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ package sbt
|
|||
import Load.LoadedBuild
|
||||
import Artifact.{DocClassifier, SourceClassifier}
|
||||
import Configurations.{Compile, CompilerPlugin, IntegrationTest, names, Provided, Runtime, Test}
|
||||
import CrossVersion.{binarySbtVersion, binaryScalaVersion, isStable, selectVersion}
|
||||
import complete._
|
||||
import std.TaskExtra._
|
||||
import inc.{FileValueCache, Locate}
|
||||
|
|
@ -48,16 +49,16 @@ object Defaults extends BuildCommon
|
|||
def buildCore: Seq[Setting[_]] = thisBuildCore ++ globalCore
|
||||
def thisBuildCore: Seq[Setting[_]] = inScope(GlobalScope.copy(project = Select(ThisBuild)))(Seq(
|
||||
managedDirectory <<= baseDirectory(_ / "lib_managed")
|
||||
|
||||
))
|
||||
def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
|
||||
crossVersion :== CrossVersion.Disabled,
|
||||
buildDependencies <<= buildDependencies or Classpaths.constructBuildDependencies,
|
||||
taskTemporaryDirectory := IO.createTemporaryDirectory,
|
||||
onComplete <<= taskTemporaryDirectory { dir => () => IO.delete(dir); IO.createDirectory(dir) },
|
||||
concurrentRestrictions <<= concurrentRestrictions or defaultRestrictions,
|
||||
parallelExecution :== true,
|
||||
sbtVersion <<= appConfiguration { _.provider.id.version },
|
||||
sbtBinaryVersion <<= sbtVersion(v => binaryVersion(v, "0.12")),
|
||||
sbtBinaryVersion <<= sbtVersion apply binarySbtVersion,
|
||||
sbtResolver <<= sbtVersion { sbtV => if(sbtV endsWith "-SNAPSHOT") Classpaths.typesafeSnapshots else Classpaths.typesafeResolver },
|
||||
pollInterval :== 500,
|
||||
logBuffered :== false,
|
||||
|
|
@ -190,7 +191,13 @@ object Defaults extends BuildCommon
|
|||
scalacOptions in GlobalScope :== Nil,
|
||||
scalaInstance <<= scalaInstanceSetting,
|
||||
scalaVersion in GlobalScope <<= appConfiguration( _.provider.scalaProvider.version),
|
||||
scalaBinaryVersion <<= scalaVersion(v => binaryVersion(v, "2.10")),
|
||||
scalaBinaryVersion in GlobalScope <<= scalaVersion apply binaryScalaVersion,
|
||||
crossVersion <<= (crossPaths, scalaVersion) { (enabled, sv) =>
|
||||
if(enabled)
|
||||
if(isStable(sv)) CrossVersion.binary else CrossVersion.full
|
||||
else
|
||||
CrossVersion.Disabled
|
||||
},
|
||||
crossScalaVersions in GlobalScope <<= Seq(scalaVersion).join,
|
||||
crossTarget <<= (target, scalaBinaryVersion, sbtBinaryVersion, sbtPlugin, crossPaths)(makeCrossTarget)
|
||||
)
|
||||
|
|
@ -211,7 +218,7 @@ object Defaults extends BuildCommon
|
|||
discoveredMainClasses <<= compile map discoverMainClasses storeAs discoveredMainClasses triggeredBy compile,
|
||||
definedSbtPlugins <<= discoverPlugins,
|
||||
inTask(run)(runnerTask :: Nil).head,
|
||||
selectMainClass <<= discoveredMainClasses map selectRunMain,
|
||||
selectMainClass <<= (discoveredMainClasses, mainClass) map { (classes, explicit) => explicit orElse selectRunMain(classes) },
|
||||
mainClass in run <<= selectMainClass in run,
|
||||
mainClass <<= discoveredMainClasses map selectPackageMain,
|
||||
run <<= runTask(fullClasspath, mainClass in run, runner in run),
|
||||
|
|
@ -357,10 +364,8 @@ object Defaults extends BuildCommon
|
|||
packageTasks(packageSrc, packageSrcTask) ++
|
||||
packageTasks(packageDoc, packageDocTask)
|
||||
|
||||
private[this] val allSubpaths = (dir: File) => (dir.*** --- dir) x (relativeTo(dir)|flat)
|
||||
|
||||
def packageBinTask = products map { ps => ps flatMap { p => allSubpaths(p) } }
|
||||
def packageDocTask = doc map allSubpaths
|
||||
def packageBinTask = products map { _ flatMap Path.allSubpaths }
|
||||
def packageDocTask = doc map { p => Path.allSubpaths(p).toSeq }
|
||||
def packageSrcTask = concatMappings(resourceMappings, sourceMappings)
|
||||
|
||||
private type Mappings = Initialize[Task[Seq[(File, String)]]]
|
||||
|
|
@ -368,18 +373,21 @@ object Defaults extends BuildCommon
|
|||
|
||||
// drop base directories, since there are no valid mappings for these
|
||||
def sourceMappings = (unmanagedSources, unmanagedSourceDirectories, baseDirectory) map { (srcs, sdirs, base) =>
|
||||
( (srcs --- sdirs --- base) x (relativeTo(sdirs)|relativeTo(base)|flat)) toSeq
|
||||
( (srcs --- sdirs --- base) pair (relativeTo(sdirs)|relativeTo(base)|flat)) toSeq
|
||||
}
|
||||
def resourceMappings = relativeMappings(unmanagedResources, unmanagedResourceDirectories)
|
||||
def relativeMappings(files: ScopedTaskable[Seq[File]], dirs: ScopedTaskable[Seq[File]]): Initialize[Task[Seq[(File, String)]]] =
|
||||
(files, dirs) map { (rs, rdirs) =>
|
||||
(rs --- rdirs) x (relativeTo(rdirs)|flat) toSeq
|
||||
(rs --- rdirs) pair (relativeTo(rdirs)|flat) toSeq
|
||||
}
|
||||
|
||||
def collectFiles(dirs: ScopedTaskable[Seq[File]], filter: ScopedTaskable[FileFilter], excludes: ScopedTaskable[FileFilter]): Initialize[Task[Seq[File]]] =
|
||||
(dirs, filter, excludes) map { (d,f,excl) => d.descendantsExcept(f,excl).get }
|
||||
|
||||
def artifactPathSetting(art: SettingKey[Artifact]) = (crossTarget, projectID, art, scalaBinaryVersion in artifactName, artifactName) { (t, module, a, sv, toString) => t / toString(sv, module, a) asFile }
|
||||
def artifactPathSetting(art: SettingKey[Artifact]) = (crossTarget, projectID, art, scalaVersion in artifactName, scalaBinaryVersion in artifactName, artifactName) {
|
||||
(t, module, a, sv, sbv, toString) =>
|
||||
t / toString(ScalaVersion(sv, sbv), module, a) asFile
|
||||
}
|
||||
def artifactSetting = ((artifact, artifactClassifier).identity zipWith configuration.?) { case ((a,classifier),cOpt) =>
|
||||
val cPart = cOpt flatMap { c => if(c == Compile) None else Some(c.name) }
|
||||
val combined = cPart.toList ++ classifier.toList
|
||||
|
|
@ -511,7 +519,8 @@ object Defaults extends BuildCommon
|
|||
})
|
||||
}
|
||||
|
||||
def sbtPluginExtra(m: ModuleID, sbtV: String, scalaV: String): ModuleID = m.extra(CustomPomParser.SbtVersionKey -> sbtV, CustomPomParser.ScalaVersionKey -> scalaV).copy(crossVersion = false)
|
||||
def sbtPluginExtra(m: ModuleID, sbtV: String, scalaV: String): ModuleID =
|
||||
m.extra(CustomPomParser.SbtVersionKey -> sbtV, CustomPomParser.ScalaVersionKey -> scalaV).copy(crossVersion = CrossVersion.Disabled)
|
||||
def writePluginsDescriptor(plugins: Set[String], dir: File): Seq[File] =
|
||||
{
|
||||
val descriptor: File = dir / "sbt" / "sbt.plugins"
|
||||
|
|
@ -537,7 +546,7 @@ object Defaults extends BuildCommon
|
|||
def copyResourcesTask =
|
||||
(classDirectory, cacheDirectory, resources, resourceDirectories, streams) map { (target, cache, resrcs, dirs, s) =>
|
||||
val cacheFile = cache / "copy-resources"
|
||||
val mappings = (resrcs --- dirs) x (rebase(dirs, target) | flat(target))
|
||||
val mappings = (resrcs --- dirs) pair (rebase(dirs, target) | flat(target))
|
||||
s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t","\n\t",""))
|
||||
Sync(cacheFile)( mappings )
|
||||
mappings
|
||||
|
|
@ -597,25 +606,6 @@ object Defaults extends BuildCommon
|
|||
(if(aggregate) p.aggregate else Nil)
|
||||
}
|
||||
|
||||
val PartialVersion = """(\d+)\.(\d+)(?:\..+)?""".r
|
||||
def partialVersion(s: String): Option[(Int,Int)] =
|
||||
s match {
|
||||
case PartialVersion(major, minor) => Some(major.toInt, minor.toInt)
|
||||
case _ => None
|
||||
}
|
||||
private[this] def isNewer(major: Int, minor: Int, minMajor: Int, minMinor: Int): Boolean =
|
||||
major > minMajor || (major == minMajor && minor >= minMinor)
|
||||
|
||||
def binaryVersion(full: String, cutoff: String): String =
|
||||
{
|
||||
def sub(major: Int, minor: Int) = major + "." + minor
|
||||
(partialVersion(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
|
||||
}
|
||||
}
|
||||
|
||||
val CompletionsID = "completions"
|
||||
|
||||
def noAggregation: Seq[Scoped] = Seq(run, console, consoleQuick, consoleProject)
|
||||
|
|
@ -744,16 +734,16 @@ object Classpaths
|
|||
ivyLoggingLevel in GlobalScope :== UpdateLogging.DownloadOnly,
|
||||
ivyXML in GlobalScope :== NodeSeq.Empty,
|
||||
ivyValidate in GlobalScope :== false,
|
||||
ivyScala <<= ivyScala or (scalaHome, scalaVersion, scalaBinaryVersion in update) { (sh,v,vu) =>
|
||||
Some(new IvyScala(v, Nil, filterImplicit = true, checkExplicit = true, overrideScalaVersion = sh.isEmpty, substituteCross = x => IvySbt.substituteCross(x, vu)))
|
||||
ivyScala <<= ivyScala or (scalaHome, scalaVersion in update, scalaBinaryVersion in update) { (sh,fv,bv) =>
|
||||
Some(new IvyScala(fv, bv, Nil, filterImplicit = true, checkExplicit = true, overrideScalaVersion = sh.isEmpty))
|
||||
},
|
||||
moduleConfigurations in GlobalScope :== Nil,
|
||||
publishTo in GlobalScope :== None,
|
||||
artifactPath in makePom <<= artifactPathSetting(artifact in makePom),
|
||||
publishArtifact in makePom <<= publishMavenStyle,
|
||||
artifact in makePom <<= moduleName(Artifact.pom),
|
||||
projectID <<= (organization,moduleName,version,artifacts,crossPaths){ (org,module,version,as,crossEnabled) =>
|
||||
ModuleID(org, module, version).cross(crossEnabled).artifacts(as : _*)
|
||||
projectID <<= (organization,moduleName,version,artifacts,crossVersion in projectID){ (org,module,version,as,cross) =>
|
||||
ModuleID(org, module, version).cross(cross).artifacts(as : _*)
|
||||
},
|
||||
projectID <<= pluginProjectID,
|
||||
resolvers in GlobalScope :== Nil,
|
||||
|
|
@ -797,12 +787,19 @@ object Classpaths
|
|||
} tag(Tags.Update, Tags.Network),
|
||||
sbtDependency in GlobalScope <<= appConfiguration { app =>
|
||||
val id = app.provider.id
|
||||
val base = ModuleID(id.groupID, id.name, id.version, crossVersion = id.crossVersioned)
|
||||
IvySbt.substituteCross(base, app.provider.scalaProvider.version).copy(crossVersion = false)
|
||||
val scalaVersion = app.provider.scalaProvider.version
|
||||
val binVersion = binaryScalaVersion(scalaVersion)
|
||||
val cross = if(id.crossVersioned) if(isStable(scalaVersion)) CrossVersion.binary else CrossVersion.full else CrossVersion.Disabled
|
||||
val base = ModuleID(id.groupID, id.name, id.version, crossVersion = cross)
|
||||
CrossVersion(scalaVersion, binVersion)(base).copy(crossVersion = CrossVersion.Disabled)
|
||||
}
|
||||
)
|
||||
def pluginProjectID: Initialize[ModuleID] = (sbtBinaryVersion in update, scalaBinaryVersion in update, projectID, sbtPlugin) { (sbtV, scalaV, pid, isPlugin) =>
|
||||
if(isPlugin) sbtPluginExtra(pid, sbtV, scalaV) else pid
|
||||
def pluginProjectID: Initialize[ModuleID] = (sbtVersion in update, sbtBinaryVersion in update, scalaVersion in update, scalaBinaryVersion in update, projectID, sbtPlugin) {
|
||||
(sbtV, sbtBV, scalaV, scalaBV, pid, isPlugin) =>
|
||||
if(isPlugin)
|
||||
sbtPluginExtra(pid, selectVersion(sbtV, sbtBV), selectVersion(scalaV, scalaBV))
|
||||
else
|
||||
pid
|
||||
}
|
||||
def ivySbt0: Initialize[Task[IvySbt]] =
|
||||
(ivyConfiguration, credentials, streams) map { (conf, creds, s) =>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ object GlobalPlugin
|
|||
}
|
||||
}
|
||||
val globalPluginSettings = inScope(Scope.GlobalScope in LocalRootProject)(Seq(
|
||||
organization := "org.scala-tools.sbt",
|
||||
organization := "org.scala-sbt",
|
||||
onLoadMessage <<= Keys.baseDirectory("Loading global plugins from " + _),
|
||||
name := "global-plugin",
|
||||
sbtPlugin := true,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ object IvyConsole
|
|||
lazy val command =
|
||||
Command.command(Name) { state =>
|
||||
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands, state.log)
|
||||
val base = new File(CommandSupport.bootDirectory(state), Name)
|
||||
val base = new File(CommandUtil.bootDirectory(state), Name)
|
||||
IO.createDirectory(base)
|
||||
|
||||
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
||||
|
|
@ -56,7 +56,9 @@ object IvyConsole
|
|||
def parseManaged(arg: String, log: Logger): Seq[ModuleID] =
|
||||
arg match
|
||||
{
|
||||
case DepPattern(group, cross, name, version) => ModuleID(group.trim, name.trim, version.trim, crossVersion = !cross.trim.isEmpty) :: Nil
|
||||
case DepPattern(group, cross, name, version) =>
|
||||
val crossV = if(cross.trim.isEmpty) CrossVersion.Disabled else CrossVersion.binary
|
||||
ModuleID(group.trim, name.trim, version.trim, crossVersion = crossV) :: Nil
|
||||
case _ => log.warn("Ignoring invalid argument '" + arg + "'"); Nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,11 +56,10 @@ object Keys
|
|||
// val onComplete = SettingKey[RMap[Task,Result] => RMap[Task,Result]]("on-complete", "Transformation to apply to the final task result map. This may also be used to register hooks to run when task evaluation completes.")
|
||||
|
||||
// Command keys
|
||||
val globalLogging = AttributeKey[GlobalLogging]("global-logging", "Provides a global Logger, including command logging.")
|
||||
val historyPath = SettingKey[Option[File]]("history", "The location where command line history is persisted.")
|
||||
val shellPrompt = SettingKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.")
|
||||
val historyPath = SettingKey(BasicKeys.historyPath)
|
||||
val shellPrompt = SettingKey(BasicKeys.shellPrompt)
|
||||
val analysis = AttributeKey[inc.Analysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.")
|
||||
val watch = SettingKey[Watched]("watch", "Continuous execution configuration.")
|
||||
val watch = SettingKey(BasicKeys.watch)
|
||||
val pollInterval = SettingKey[Int]("poll-interval", "Interval between checks for modified sources by the continuous execution command.")
|
||||
val watchSources = TaskKey[Seq[File]]("watch-sources", "Defines the sources in this project for continuous execution to watch for changes.")
|
||||
val watchTransitiveSources = TaskKey[Seq[File]]("watch-transitive-sources", "Defines the sources in all projects for continuous execution to watch.")
|
||||
|
|
@ -138,6 +137,7 @@ object Keys
|
|||
val scalaVersion = SettingKey[String]("scala-version", "The version of Scala used for building.")
|
||||
val scalaBinaryVersion = SettingKey[String]("scala-binary-version", "The Scala version substring describing binary compatibility.")
|
||||
val crossScalaVersions = SettingKey[Seq[String]]("cross-scala-versions", "The versions of Scala used when cross-building.")
|
||||
val crossVersion = SettingKey[CrossVersion]("cross-version", "Configures handling of the Scala version when cross-building.")
|
||||
val classpathOptions = SettingKey[ClasspathOptions]("classpath-options", "Configures handling of Scala classpaths.")
|
||||
val definedSbtPlugins = TaskKey[Set[String]]("defined-sbt-plugins", "The set of names of Plugin implementations defined by this project.")
|
||||
val sbtPlugin = SettingKey[Boolean]("sbt-plugin", "If true, enables adding sbt as a dependency and auto-generation of the plugin descriptor file.")
|
||||
|
|
@ -165,7 +165,7 @@ object Keys
|
|||
val artifactPath = SettingKey[File]("artifact-path", "The location of a generated artifact.")
|
||||
val artifact = SettingKey[Artifact]("artifact", "Describes an artifact.")
|
||||
val artifactClassifier = SettingKey[Option[String]]("artifact-classifier", "Sets the classifier used by the default artifact definition.")
|
||||
val artifactName = SettingKey[(String, ModuleID, Artifact) => String]("artifact-name", "Function that produces the artifact name from its definition.")
|
||||
val artifactName = SettingKey[(ScalaVersion, ModuleID, Artifact) => String]("artifact-name", "Function that produces the artifact name from its definition.")
|
||||
val mappings = TaskKey[Seq[(File,String)]]("mappings", "Defines the mappings from a file to a path, used by packaging, for example.")
|
||||
val fileMappings = TaskKey[Seq[(File,File)]]("file-mappings", "Defines the mappings from a file to a file, used for copying files, for example.")
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ package sbt
|
|||
import inc.{FileValueCache, Locate}
|
||||
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
||||
import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, thisProject, thisProjectRef}
|
||||
import Keys.{globalLogging, isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
|
||||
import Keys.{isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
|
||||
import tools.nsc.reporters.ConsoleReporter
|
||||
import Build.{analyzed, data}
|
||||
import Scope.{GlobalScope, ThisScope}
|
||||
|
|
@ -489,7 +489,7 @@ object Load
|
|||
val inputs = Compiler.inputs(data(classpath), sources, target, Nil, Nil, definesClass, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log)
|
||||
val analysis =
|
||||
try { Compiler(inputs, log) }
|
||||
catch { case _: xsbti.CompileFailed => throw new NoMessageException } // compiler already logged errors
|
||||
catch { case _: xsbti.CompileFailed => throw new AlreadyHandledException } // compiler already logged errors
|
||||
(inputs, analysis)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package sbt
|
|||
import std.Transform
|
||||
import Project.ScopedKey
|
||||
import Scope.GlobalScope
|
||||
import MainLogging._
|
||||
import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, state, traceLevel}
|
||||
|
||||
object LogManager
|
||||
|
|
@ -21,11 +22,6 @@ object LogManager
|
|||
lazy val default: LogManager = withLoggers()
|
||||
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger]): LogManager = withLoggers(extra = extra)
|
||||
|
||||
def defaultScreen: AbstractLogger = ConsoleLogger()
|
||||
|
||||
def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger =
|
||||
to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false
|
||||
|
||||
def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk)
|
||||
|
||||
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(), extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil): LogManager =
|
||||
|
|
@ -42,40 +38,12 @@ object LogManager
|
|||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||
val screenTrace = getOr(traceLevel.key, -1)
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = (state get Keys.globalLogging).map(_.backed).toList
|
||||
val extraBacked = state.globalLogging.backed :: Nil
|
||||
multiLogger( new MultiLoggerConfig(console, backed, extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace) )
|
||||
}
|
||||
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(writer: PrintWriter, backing: GlobalLogBacking): GlobalLogging =
|
||||
{
|
||||
val backed = defaultBacked()(writer)
|
||||
val full = multiLogger(defaultMultiConfig( backed ) )
|
||||
GlobalLogging(full, backed, backing)
|
||||
}
|
||||
|
||||
def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig =
|
||||
new MultiLoggerConfig(defaultScreen, backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue)
|
||||
}
|
||||
final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int)
|
||||
|
||||
trait LogManager
|
||||
{
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
||||
}
|
||||
final case class GlobalLogBacking(file: File, last: Option[File])
|
||||
{
|
||||
def shift(newFile: File) = GlobalLogBacking(newFile, Some(file))
|
||||
def unshift = GlobalLogBacking(last getOrElse file, None)
|
||||
}
|
||||
final case class GlobalLogging(full: Logger, backed: ConsoleLogger, backing: GlobalLogBacking)
|
||||
|
|
|
|||
370
main/Main.scala
370
main/Main.scala
|
|
@ -3,132 +3,64 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import Execute.NodeView
|
||||
import complete.{DefaultParsers, HistoryCommands, Parser}
|
||||
import HistoryCommands.{Start => HistoryPrefix}
|
||||
import complete.{DefaultParsers, Parser}
|
||||
import compiler.EvalImports
|
||||
import Types.{const,idFun}
|
||||
import Types.idFun
|
||||
import Aggregation.AnyKeys
|
||||
|
||||
import Command.applyEffect
|
||||
import Keys.{analysis,historyPath,globalLogging,shellPrompt}
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.JavaConversions._
|
||||
import Function.tupled
|
||||
import java.net.URI
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import Path._
|
||||
import StandardMain._
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
/** This class is the entry point for sbt.*/
|
||||
final class xMain extends xsbti.AppMain
|
||||
{
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import BuiltinCommands.{initialAttributes, initialize, defaults, DefaultBootCommands}
|
||||
import CommandSupport.{BootCommand, DefaultsCommand, InitCommand}
|
||||
val initialCommandDefs = Seq(initialize, defaults)
|
||||
val commands = DefaultsCommand +: InitCommand +: BootCommand +: configuration.arguments.map(_.trim)
|
||||
val state = State( configuration, initialCommandDefs, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
import BuiltinCommands.{initialize, defaults}
|
||||
import CommandStrings.{BootCommand, DefaultsCommand, InitCommand}
|
||||
MainLoop.runLogged( initialState(configuration,
|
||||
Seq(initialize, defaults),
|
||||
DefaultsCommand :: InitCommand :: BootCommand :: Nil)
|
||||
)
|
||||
}
|
||||
}
|
||||
final class ScriptMain extends xsbti.AppMain
|
||||
{
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import BuiltinCommands.{initialAttributes, ScriptCommands}
|
||||
val commands = Script.Name +: configuration.arguments.map(_.trim)
|
||||
val state = State( configuration, ScriptCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
}
|
||||
MainLoop.runLogged( initialState(configuration,
|
||||
BuiltinCommands.ScriptCommands,
|
||||
Script.Name :: Nil)
|
||||
)
|
||||
}
|
||||
final class ConsoleMain extends xsbti.AppMain
|
||||
{
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import BuiltinCommands.{initialAttributes, ConsoleCommands}
|
||||
val commands = IvyConsole.Name +: configuration.arguments.map(_.trim)
|
||||
val state = State( configuration, ConsoleCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
}
|
||||
MainLoop.runLogged( initialState(configuration,
|
||||
BuiltinCommands.ConsoleCommands,
|
||||
IvyConsole.Name :: Nil)
|
||||
)
|
||||
}
|
||||
object MainLoop
|
||||
|
||||
object StandardMain
|
||||
{
|
||||
/** Entry point to run the remaining commands in State with managed global logging.*/
|
||||
def runLogged(state: State): xsbti.MainResult =
|
||||
runLoggedLoop(state, GlobalLogBacking(newBackingFile(), None))
|
||||
|
||||
/** Constructs a new, (weakly) unique, temporary file to use as the backing for global logging. */
|
||||
def newBackingFile(): File = File.createTempFile("sbt",".log")
|
||||
|
||||
/** Run loop that evaluates remaining commands and manages changes to global logging configuration.*/
|
||||
@tailrec def runLoggedLoop(state: State, logBacking: GlobalLogBacking): xsbti.MainResult =
|
||||
runAndClearLast(state, logBacking) match {
|
||||
case ret: Return => // delete current and last log files when exiting normally
|
||||
logBacking.file.delete()
|
||||
deleteLastLog(logBacking)
|
||||
ret.result
|
||||
case clear: ClearGlobalLog => // delete previous log file, move current to previous, and start writing to a new file
|
||||
deleteLastLog(logBacking)
|
||||
runLoggedLoop(clear.state, logBacking shift newBackingFile())
|
||||
case keep: KeepGlobalLog => // make previous log file the current log file
|
||||
logBacking.file.delete
|
||||
runLoggedLoop(keep.state, logBacking.unshift)
|
||||
}
|
||||
|
||||
/** Runs the next sequence of commands, cleaning up global logging after any exceptions. */
|
||||
def runAndClearLast(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
try
|
||||
runWithNewLog(state, logBacking)
|
||||
catch {
|
||||
case e: xsbti.FullReload =>
|
||||
deleteLastLog(logBacking)
|
||||
throw e // pass along a reboot request
|
||||
case e =>
|
||||
System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
||||
deleteLastLog(logBacking)
|
||||
throw e
|
||||
}
|
||||
|
||||
/** Deletes the previous global log file. */
|
||||
def deleteLastLog(logBacking: GlobalLogBacking): Unit =
|
||||
logBacking.last.foreach(_.delete())
|
||||
|
||||
/** Runs the next sequence of commands with global logging in place. */
|
||||
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
Using.fileWriter(append = true)(logBacking.file) { writer =>
|
||||
val out = new java.io.PrintWriter(writer)
|
||||
val loggedState = state.put(globalLogging, LogManager.globalDefault(out, logBacking))
|
||||
try run(loggedState) finally out.close()
|
||||
}
|
||||
sealed trait RunNext
|
||||
final class ClearGlobalLog(val state: State) extends RunNext
|
||||
final class KeepGlobalLog(val state: State) extends RunNext
|
||||
final class Return(val result: xsbti.MainResult) extends RunNext
|
||||
|
||||
/** Runs the next sequence of commands that doesn't require global logging changes.*/
|
||||
@tailrec def run(state: State): RunNext =
|
||||
state.next match
|
||||
{
|
||||
case State.Continue => run(next(state))
|
||||
case State.ClearGlobalLog => new ClearGlobalLog(state.continue)
|
||||
case State.KeepLastLog => new KeepGlobalLog(state.continue)
|
||||
case ret: State.Return => new Return(ret.result)
|
||||
}
|
||||
|
||||
def next(state: State): State =
|
||||
ErrorHandling.wideConvert { state.process(Command.process) } match
|
||||
{
|
||||
case Right(s) => s
|
||||
case Left(t: xsbti.FullReload) => throw t
|
||||
case Left(t) => BuiltinCommands.handleException(t, state)
|
||||
}
|
||||
def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State =
|
||||
{
|
||||
val commands = preCommands ++ configuration.arguments.map(_.trim)
|
||||
State( configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, BuiltinCommands.initialAttributes, initialGlobalLogging, State.Continue )
|
||||
}
|
||||
def initialGlobalLogging: GlobalLogging =
|
||||
GlobalLogging.initial(MainLogging.globalDefault _, File.createTempFile("sbt",".log"))
|
||||
}
|
||||
|
||||
import DefaultParsers._
|
||||
import CommandSupport._
|
||||
import CommandStrings._
|
||||
import BasicCommands._
|
||||
import CommandUtil._
|
||||
|
||||
object BuiltinCommands
|
||||
{
|
||||
def initialAttributes = AttributeMap.empty
|
||||
|
|
@ -140,22 +72,9 @@ object BuiltinCommands
|
|||
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
|
||||
|
||||
def boot = Command.make(BootCommand)(bootParser)
|
||||
def nop = Command.custom(s => success(() => s))
|
||||
def ignore = Command.command(FailureWall)(idFun)
|
||||
|
||||
def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] =
|
||||
selected.distinct flatMap { detailMap get _ }
|
||||
|
||||
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s => logger(s).info(aboutString(s)); s }
|
||||
|
||||
def helpParser(s: State) =
|
||||
{
|
||||
val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s))
|
||||
val helpCommands = h.detail.keySet
|
||||
val args = (token(Space) ~> token( NotSpace examples helpCommands )).*
|
||||
applyEffect(args)(runHelp(s, h))
|
||||
}
|
||||
// This parser schedules the default boot commands unless overridden by an alias
|
||||
def bootParser(s: State) =
|
||||
{
|
||||
|
|
@ -163,16 +82,6 @@ object BuiltinCommands
|
|||
delegateToAlias(BootCommand, success(orElse) )(s)
|
||||
}
|
||||
|
||||
def runHelp(s: State, h: Help)(args: Seq[String]): State =
|
||||
{
|
||||
val message =
|
||||
if(args.isEmpty)
|
||||
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
|
||||
else
|
||||
detail(args, h.detail) mkString("\n", "\n\n", "\n")
|
||||
System.out.println(message)
|
||||
s
|
||||
}
|
||||
def sbtVersion(s: State): String = s.configuration.provider.id.version
|
||||
def scalaVersion(s: State): String = s.configuration.provider.scalaProvider.version
|
||||
def aboutString(s: State): String =
|
||||
|
|
@ -230,154 +139,15 @@ object BuiltinCommands
|
|||
aligned(" ", " ", taskDetail(s)) mkString("\n", "\n", "")
|
||||
|
||||
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) }
|
||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] =
|
||||
{
|
||||
val width = in.map(_._1.length).max
|
||||
in.map { case (a, b) => (" " + fill(a, width) + sep + b) }
|
||||
}
|
||||
def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0)
|
||||
|
||||
def alias = Command.make(AliasCommand, AliasBrief, AliasDetailed) { s =>
|
||||
val name = token(OpOrID.examples( aliasNames(s) : _*) )
|
||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||
val sfree = removeAliases(s)
|
||||
val to = matched(sfree.combinedParser, partial = true) | any.+.string
|
||||
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
|
||||
applyEffect(base)(t => runAlias(s, t) )
|
||||
}
|
||||
|
||||
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
||||
args match
|
||||
{
|
||||
case None => printAliases(s); s
|
||||
case Some(x ~ None) if !x.isEmpty => printAlias(s, x.trim); s
|
||||
case Some(name ~ Some(None)) => removeAlias(s, name.trim)
|
||||
case Some(name ~ Some(Some(value))) => addAlias(s, name.trim, value.trim)
|
||||
}
|
||||
|
||||
def shell = Command.command(Shell, ShellBrief, ShellDetailed) { s =>
|
||||
val history = (s get historyPath.key) getOrElse Some((s.baseDir / ".history").asFile)
|
||||
val prompt = (s get shellPrompt.key) match { case Some(pf) => pf(s); case None => "> " }
|
||||
val reader = new FullReader(history, s.combinedParser)
|
||||
val line = reader.readLine(prompt)
|
||||
line match {
|
||||
case Some(line) =>
|
||||
val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands)
|
||||
if(line.trim.isEmpty) newState else newState.clearGlobalLog
|
||||
case None => s
|
||||
}
|
||||
}
|
||||
|
||||
def multiParser(s: State): Parser[Seq[String]] =
|
||||
{
|
||||
val nonSemi = token(charClass(_ != ';').+, hide= const(true))
|
||||
( token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser&nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim) ).+
|
||||
}
|
||||
|
||||
def multiApplied(s: State) =
|
||||
Command.applyEffect( multiParser(s) )( _ ::: s )
|
||||
|
||||
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed) )
|
||||
|
||||
lazy val otherCommandParser = (s: State) => token(OptSpace ~> combinedLax(s, any.+) )
|
||||
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
||||
matched(s.combinedParser | token(any, hide= const(true)))
|
||||
|
||||
def ifLast = Command(IfLast, IfLastBrief, IfLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||
if(s.remainingCommands.isEmpty) arg :: s else s
|
||||
}
|
||||
def append = Command(AppendCommand, AppendLastBrief, AppendLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||
s.copy(remainingCommands = s.remainingCommands :+ arg)
|
||||
}
|
||||
|
||||
def setOnFailure = Command(OnFailure, OnFailureBrief, OnFailureDetailed)(otherCommandParser) { (s, arg) =>
|
||||
s.copy(onFailure = Some(arg))
|
||||
}
|
||||
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||
|
||||
def reboot = Command(RebootCommand, RebootBrief, RebootDetailed)(rebootParser) { (s, full) =>
|
||||
s.reboot(full)
|
||||
}
|
||||
def rebootParser(s: State) = token(Space ~> "full" ^^^ true) ?? false
|
||||
|
||||
def defaults = Command.command(DefaultsCommand) { s =>
|
||||
s ++ DefaultCommands
|
||||
}
|
||||
def call = Command(ApplyCommand, ApplyBrief, ApplyDetailed)(_ => spaceDelimited("<class name>")) { (state,args) =>
|
||||
val loader = getClass.getClassLoader
|
||||
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader))
|
||||
(state /: loaded) { case (s, obj: (State => State)) => obj(s) }
|
||||
}
|
||||
|
||||
def initialize = Command.command(InitCommand) { s =>
|
||||
/*"load-commands -base ~/.sbt/commands" :: */readLines( readable( sbtRCs(s) ) ) ::: s
|
||||
}
|
||||
|
||||
def readParser(s: State) =
|
||||
{
|
||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||
val portAndSuccess = token(OptSpace) ~> Port
|
||||
portAndSuccess || files
|
||||
}
|
||||
|
||||
def read = Command.make(ReadCommand, ReadBrief, ReadDetailed)(s => applyEffect(readParser(s))(doRead(s)) )
|
||||
|
||||
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
||||
arg match
|
||||
{
|
||||
case Left(portAndSuccess) =>
|
||||
val port = math.abs(portAndSuccess)
|
||||
val previousSuccess = portAndSuccess >= 0
|
||||
readMessage(port, previousSuccess) match
|
||||
{
|
||||
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(ReadCommand + " " + (-port)))
|
||||
case None =>
|
||||
System.err.println("Connection closed.")
|
||||
s.fail
|
||||
}
|
||||
case Right(from) =>
|
||||
val notFound = notReadable(from)
|
||||
if(notFound.isEmpty)
|
||||
readLines(from) ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
|
||||
else {
|
||||
logger(s).error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
|
||||
s
|
||||
}
|
||||
}
|
||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
||||
{
|
||||
// split into two connections because this first connection ends the previous communication
|
||||
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
||||
// and this second connection starts the next communication
|
||||
xsbt.IPC.client(port) { ipc =>
|
||||
val message = ipc.receive
|
||||
if(message eq null) None else Some(message)
|
||||
}
|
||||
}
|
||||
|
||||
def continuous =
|
||||
Command(ContinuousExecutePrefix, Help(continuousBriefHelp) )(otherCommandParser) { (s, arg) =>
|
||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||
val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg)
|
||||
Watched.executeContinuously(w, s, arg, repeat)
|
||||
}
|
||||
}
|
||||
|
||||
def history = Command.custom(historyParser, historyHelp)
|
||||
def historyParser(s: State): Parser[() => State] =
|
||||
Command.applyEffect(HistoryCommands.actionParser) { histFun =>
|
||||
val logError = (msg: String) => s.log.error(msg)
|
||||
val hp = s get historyPath.key getOrElse None
|
||||
val lines = hp.toList.flatMap( p => IO.readLines(p) ).toIndexedSeq
|
||||
histFun( complete.History(lines, hp, logError) ) match
|
||||
{
|
||||
case Some(commands) =>
|
||||
commands foreach println //printing is more appropriate than logging
|
||||
(commands ::: s).continue
|
||||
case None => s.fail
|
||||
}
|
||||
}
|
||||
|
||||
def eval = Command.single(EvalCommand, evalBrief, evalDetailed) { (s, arg) =>
|
||||
val log = logger(s)
|
||||
val extracted = Project extract s
|
||||
|
|
@ -482,7 +252,7 @@ object BuiltinCommands
|
|||
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
|
||||
def lastLogFile(s: State) =
|
||||
{
|
||||
val backing = CommandSupport.globalLogging(s).backing
|
||||
val backing = s.globalLogging.backing
|
||||
if(isLastOnly(s)) backing.last else Some(backing.file)
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +284,7 @@ object BuiltinCommands
|
|||
}
|
||||
|
||||
def act = Command.customHelp(Act.actParser, actHelp)
|
||||
def actHelp = (s: State) => CommandSupport.showHelp ++ keysHelp(s)
|
||||
def actHelp = (s: State) => CommandStrings.showHelp ++ keysHelp(s)
|
||||
def keysHelp(s: State): Help =
|
||||
if(Project.isProjectLoaded(s))
|
||||
Help.detailOnly(taskDetail(s))
|
||||
|
|
@ -530,16 +300,9 @@ object BuiltinCommands
|
|||
for( (uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, log)
|
||||
s
|
||||
}
|
||||
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
||||
(s get key) match {
|
||||
case None => logger(s).error(ifMissing); s.fail
|
||||
case Some(nav) => f(nav)
|
||||
}
|
||||
|
||||
def project = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
||||
|
||||
def exit = Command.command(TerminateAction, exitBrief, exitBrief ) ( _ exit true )
|
||||
|
||||
def loadFailed = Command.command(LoadFailed)(handleLoadFailed)
|
||||
@tailrec def handleLoadFailed(s: State): State =
|
||||
{
|
||||
|
|
@ -576,71 +339,4 @@ object BuiltinCommands
|
|||
SessionSettings.checkSession(session, s)
|
||||
Project.setProject(session, structure, s)
|
||||
}
|
||||
|
||||
def handleException(e: Throwable, s: State): State =
|
||||
handleException(e, s, logger(s))
|
||||
def handleException(e: Throwable, s: State, log: Logger): State =
|
||||
{
|
||||
e match
|
||||
{
|
||||
case _: Incomplete => () // already handled by evaluateTask
|
||||
case _: NoMessageException => ()
|
||||
case ite: InvocationTargetException =>
|
||||
val cause = ite.getCause
|
||||
if(cause == null || cause == ite) logFullException(ite, log) else handleException(cause, s, log)
|
||||
case _: MessageOnlyException => log.error(e.toString)
|
||||
case _: Project.Uninitialized => logFullException(e, log, true)
|
||||
case _ => logFullException(e, log)
|
||||
}
|
||||
s.fail
|
||||
}
|
||||
def logFullException(e: Throwable, log: Logger, messageOnly: Boolean = false)
|
||||
{
|
||||
log.trace(e)
|
||||
log.error(if(messageOnly) e.getMessage else ErrorHandling reducedToString e)
|
||||
log.error("Use 'last' for the full log.")
|
||||
}
|
||||
|
||||
def addAlias(s: State, name: String, value: String): State =
|
||||
if(Command validID name) {
|
||||
val removed = removeAlias(s, name)
|
||||
if(value.isEmpty) removed else removed.copy(definedCommands = newAlias(name, value) +: removed.definedCommands)
|
||||
} else {
|
||||
System.err.println("Invalid alias name '" + name + "'.")
|
||||
s.fail
|
||||
}
|
||||
|
||||
def removeAliases(s: State): State = removeTagged(s, CommandAliasKey)
|
||||
def removeAlias(s: State, name: String): State = s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)) )
|
||||
|
||||
def removeTagged(s: State, tag: AttributeKey[_]): State = s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
||||
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] = as.filter(c => ! (c.tags contains tag))
|
||||
|
||||
def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c))
|
||||
def isNamed(name: String, alias: Option[(String,String)]): Boolean = alias match { case None => false; case Some((n,_)) => name == n }
|
||||
|
||||
def getAlias(c: Command): Option[(String,String)] = c.tags get CommandAliasKey
|
||||
def printAlias(s: State, name: String): Unit = printAliases(aliases(s,(n,v) => n == name) )
|
||||
def printAliases(s: State): Unit = printAliases(allAliases(s))
|
||||
def printAliases(as: Seq[(String,String)]): Unit =
|
||||
for( (name,value) <- as)
|
||||
println("\t" + name + " = " + value)
|
||||
|
||||
def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1)
|
||||
def allAliases(s: State): Seq[(String,String)] = aliases(s, (n,v) => true)
|
||||
def aliases(s: State, pred: (String,String) => Boolean): Seq[(String,String)] =
|
||||
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
||||
|
||||
def newAlias(name: String, value: String): Command =
|
||||
Command.make(name, (name, "'" + value + "'"), "Alias of '" + value + "'")(aliasBody(name, value)).tag(CommandAliasKey, (name, value))
|
||||
def aliasBody(name: String, value: String)(state: State): Parser[() => State] =
|
||||
OptSpace ~> Parser(Command.combine(removeAlias(state,name).definedCommands)(state))(value)
|
||||
|
||||
def delegateToAlias(name: String, orElse: Parser[() => State])(state: State): Parser[() => State] =
|
||||
aliases(state, (nme,_) => nme == name).headOption match {
|
||||
case None => orElse
|
||||
case Some((n,v)) => aliasBody(n,v)(state)
|
||||
}
|
||||
|
||||
val CommandAliasKey = AttributeKey[(String,String)]("is-command-alias", "Internal: marker for Commands created as aliases for another command.")
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
val prompt = get(shellPrompt)
|
||||
val watched = get(watch)
|
||||
val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true))
|
||||
val newDefinedCommands = commandDefs ++ BuiltinCommands.removeTagged(s.definedCommands, projectCommand)
|
||||
val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged(s.definedCommands, projectCommand)
|
||||
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).put(historyPath.key, history)
|
||||
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ object Script
|
|||
val scriptArg = state.remainingCommands.headOption getOrElse error("No script file specified")
|
||||
val script = new File(scriptArg).getAbsoluteFile
|
||||
val hash = Hash.halve(Hash.toHex(Hash(script.getAbsolutePath)))
|
||||
val base = new File(CommandSupport.bootDirectory(state), hash)
|
||||
val base = new File(CommandUtil.bootDirectory(state), hash)
|
||||
IO.createDirectory(base)
|
||||
|
||||
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,6 @@ object TaskData
|
|||
private[this] def fakeState(structure: BuildStructure): State =
|
||||
{
|
||||
val config = Keys.appConfiguration in Scope.GlobalScope get structure.data
|
||||
State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, State.Continue)
|
||||
State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, StandardMain.initialGlobalLogging, State.Continue)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ package sbt
|
|||
import FileInfo.{exists, hash}
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import Types.:+:
|
||||
import Types.{:+:, idFun}
|
||||
import scala.xml.NodeSeq
|
||||
import sbinary.{DefaultProtocol,Format}
|
||||
import DefaultProtocol.{immutableMapFormat, immutableSetFormat, optionsAreFormat}
|
||||
|
|
@ -80,8 +80,19 @@ object CacheIvy
|
|||
)
|
||||
implicit def exclusionRuleFormat(implicit sf: Format[String]): Format[ExclusionRule] =
|
||||
wrap[ExclusionRule, (String, String, String, Seq[String])]( e => (e.organization, e.name, e.artifact, e.configurations), { case (o,n,a,cs) => ExclusionRule(o,n,a,cs) })
|
||||
|
||||
implicit def crossVersionFormat: Format[CrossVersion] = wrap(crossToInt, crossFromInt)
|
||||
|
||||
private[this] final val DisabledValue = 0
|
||||
private[this] final val BinaryValue = 1
|
||||
private[this] final val FullValue = 2
|
||||
|
||||
import CrossVersion.{Binary, Disabled, Full}
|
||||
private[this] val crossFromInt = (i: Int) => i match { case BinaryValue => new Binary(idFun); case FullValue => new Full(idFun); case _ => Disabled }
|
||||
private[this] val crossToInt = (c: CrossVersion) => c match { case Disabled => 0; case b: Binary => BinaryValue; case f: Full => FullValue }
|
||||
|
||||
implicit def moduleIDFormat(implicit sf: Format[String], af: Format[Artifact], bf: Format[Boolean], ef: Format[ExclusionRule]): Format[ModuleID] =
|
||||
wrap[ModuleID, ((String,String,String,Option[String]),(Boolean,Boolean,Seq[Artifact],Seq[ExclusionRule],Map[String,String],Boolean))](
|
||||
wrap[ModuleID, ((String,String,String,Option[String]),(Boolean,Boolean,Seq[Artifact],Seq[ExclusionRule],Map[String,String],CrossVersion))](
|
||||
m => ((m.organization,m.name,m.revision,m.configurations), (m.isChanging, m.isTransitive, m.explicitArtifacts, m.exclusions, m.extraAttributes, m.crossVersion)),
|
||||
{ case ((o,n,r,cs),(ch,t,as,excl,x,cv)) => ModuleID(o,n,r,cs,ch,t,as,excl,x,cv) }
|
||||
)
|
||||
|
|
@ -144,6 +155,7 @@ object CacheIvy
|
|||
|
||||
implicit def artifactToHL = (a: Artifact) => a.name :+: a.`type` :+: a.extension :+: a.classifier :+: names(a.configurations) :+: a.url :+: a.extraAttributes :+: HNil
|
||||
implicit def exclusionToHL = (e: ExclusionRule) => e.organization :+: e.name :+: e.artifact :+: e.configurations :+: HNil
|
||||
implicit def crossToHL = (c: CrossVersion) => crossToInt(c) :+: HNil
|
||||
|
||||
/* implicit def deliverConfToHL = (p: DeliverConfiguration) => p.deliverIvyPattern :+: p.status :+: p.configurations :+: HNil
|
||||
implicit def publishConfToHL = (p: PublishConfiguration) => p.ivyFile :+: p.resolverName :+: p.artifacts :+: HNil*/
|
||||
|
|
@ -156,13 +168,14 @@ object CacheIvy
|
|||
implicit def connectionIC: InputCache[SshConnection] = wrapIn
|
||||
implicit def artifactIC: InputCache[Artifact] = wrapIn
|
||||
implicit def exclusionIC: InputCache[ExclusionRule] = wrapIn
|
||||
implicit def crossVersionIC: InputCache[CrossVersion] = wrapIn
|
||||
/* implicit def publishConfIC: InputCache[PublishConfiguration] = wrapIn
|
||||
implicit def deliverConfIC: InputCache[DeliverConfiguration] = wrapIn*/
|
||||
|
||||
object L1 {
|
||||
implicit def retrieveToHL = (r: RetrieveConfiguration) => exists(r.retrieveDirectory) :+: r.outputPattern :+: HNil
|
||||
implicit def ivyPathsToHL = (p: IvyPaths) => exists(p.baseDirectory) :+: p.ivyHome.map(exists.apply) :+: HNil
|
||||
implicit def ivyScalaHL = (i: IvyScala) => i.scalaVersion :+: names(i.configurations) :+: i.checkExplicit :+: i.filterImplicit :+: HNil
|
||||
implicit def ivyScalaHL = (i: IvyScala) => i.scalaFullVersion :+: i.scalaBinaryVersion :+: names(i.configurations) :+: i.checkExplicit :+: i.filterImplicit :+: HNil
|
||||
implicit def configurationToHL = (c: Configuration) => c.name :+: c.description :+: c.isPublic :+: names(c.extendsConfigs) :+: c.transitive :+: HNil
|
||||
|
||||
implicit def passwordToHL = (s: PasswordAuthentication) => Hash(s.user) :+: password(s.password) :+: HNil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import complete.HistoryCommands
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import java.io.File
|
||||
import Path._
|
||||
|
||||
object BasicCommandStrings
|
||||
{
|
||||
val HelpCommand = "help"
|
||||
val Exit = "exit"
|
||||
val Quit = "quit"
|
||||
|
||||
/** The command name to terminate the program.*/
|
||||
val TerminateAction: String = Exit
|
||||
|
||||
def helpBrief = (HelpCommand + " [command]*", "Displays this help message or prints detailed help on requested commands.")
|
||||
def helpDetailed = """
|
||||
If an argument is provided, this prints detailed help for that command.
|
||||
Otherwise, this prints a help summary."""
|
||||
|
||||
def historyHelp = Help.briefDetail(HistoryCommands.descriptions)
|
||||
|
||||
def exitBrief = "Terminates the build."
|
||||
|
||||
def ReadCommand = "<"
|
||||
def ReadFiles = " file1 file2 ..."
|
||||
def ReadBrief = (ReadCommand + " <file>*", "Reads command lines from the provided files.")
|
||||
def ReadDetailed =
|
||||
ReadCommand + ReadFiles + """
|
||||
|
||||
Reads the lines from the given files and inserts them as commands.
|
||||
All empty lines and lines that start with '#' are ignored.
|
||||
If a file does not exist or is not readable, this command fails.
|
||||
|
||||
All the lines from all the files are read before any of the commands
|
||||
are executed. Thus, if any file is not readable, none of commands
|
||||
from any of the files (even the existing ones) will be run.
|
||||
|
||||
You probably need to escape this command if entering it at your shell."""
|
||||
|
||||
def ApplyCommand = "apply"
|
||||
def ApplyBrief = (ApplyCommand + " <module-name>*", ApplyDetailed)
|
||||
def ApplyDetailed = "Transforms the current State by calling <module-name>.apply(currentState) for each listed module name."
|
||||
|
||||
def RebootCommand = "reboot"
|
||||
def RebootSummary = RebootCommand + " [full]"
|
||||
def RebootBrief = (RebootSummary, "Reboots sbt and then executes the remaining commands.")
|
||||
def RebootDetailed =
|
||||
RebootSummary + """
|
||||
|
||||
This command is equivalent to exiting sbt, restarting, and running the
|
||||
remaining commands with the exception that the JVM is not shut down.
|
||||
|
||||
If 'full' is specified, the boot directory (`~/.sbt/boot` by default)
|
||||
is deleted before restarting. This forces an update of sbt and Scala
|
||||
and is useful when working with development versions of sbt or Scala."""
|
||||
|
||||
def Multi = ";"
|
||||
def MultiBrief = (Multi + " <command> (" + Multi + " <command>)*", "Runs the provided semicolon-separated commands.")
|
||||
def MultiDetailed =
|
||||
Multi + " command1 " + Multi + """ command2 ...
|
||||
|
||||
Runs the specified commands."""
|
||||
|
||||
def AppendCommand = "append"
|
||||
def AppendLastBrief = (AppendCommand + " <command>", AppendLastDetailed)
|
||||
def AppendLastDetailed = "Appends 'command' to list of commands to run."
|
||||
|
||||
val AliasCommand = "alias"
|
||||
def AliasBrief = (AliasCommand, "Adds, removes, or prints command aliases.")
|
||||
def AliasDetailed =
|
||||
AliasCommand + """
|
||||
|
||||
Prints a list of defined aliases.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name
|
||||
|
||||
Prints the alias defined for `name`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=value
|
||||
|
||||
Sets the alias `name` to `value`, replacing any existing alias with that name.
|
||||
Whenever `name` is entered, the corresponding `value` is run.
|
||||
If any argument is provided to `name`, it is appended as argument to `value`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=
|
||||
|
||||
Removes the alias for `name`."""
|
||||
|
||||
def Shell = "shell"
|
||||
def ShellBrief = ShellDetailed
|
||||
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
||||
|
||||
def ClearOnFailure = "--"
|
||||
def OnFailure = "-"
|
||||
def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.")
|
||||
def OnFailureDetailed =
|
||||
OnFailure + """ command
|
||||
|
||||
Registers 'command' to run when a command fails to complete normally.
|
||||
|
||||
Only one failure command may be registered at a time, so this command
|
||||
replaces the previous command if there is one.
|
||||
|
||||
The failure command resets when it runs once, so it must be added
|
||||
again if desired."""
|
||||
|
||||
def IfLast = "iflast"
|
||||
def IfLastBrief = (IfLast + " <command>", IfLastCommon)
|
||||
def IfLastCommon = "If there are no more commands after this one, 'command' is run."
|
||||
def IfLastDetailed =
|
||||
IfLast + """ command
|
||||
|
||||
""" + IfLastCommon
|
||||
|
||||
val ContinuousExecutePrefix = "~"
|
||||
def continuousBriefHelp = (ContinuousExecutePrefix + " <command>", "Executes the specified command whenever source files change.")
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
package sbt
|
||||
|
||||
import complete.{DefaultParsers, HistoryCommands, Parser}
|
||||
import DefaultParsers._
|
||||
import Types.{const,idFun}
|
||||
import Function.tupled
|
||||
import Command.applyEffect
|
||||
import State.FailureWall
|
||||
import HistoryCommands.{Start => HistoryPrefix}
|
||||
import BasicCommandStrings._
|
||||
import CommandUtil._
|
||||
import BasicKeys._
|
||||
|
||||
import java.io.File
|
||||
|
||||
object BasicCommands
|
||||
{
|
||||
lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, reboot, call, exit, continuous, history, shell, read, alias)
|
||||
|
||||
def nop = Command.custom(s => success(() => s))
|
||||
def ignore = Command.command(FailureWall)(idFun)
|
||||
|
||||
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||
|
||||
def helpParser(s: State) =
|
||||
{
|
||||
val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s))
|
||||
val helpCommands = h.detail.keySet
|
||||
val args = (token(Space) ~> token( NotSpace examples helpCommands )).*
|
||||
applyEffect(args)(runHelp(s, h))
|
||||
}
|
||||
|
||||
def runHelp(s: State, h: Help)(args: Seq[String]): State =
|
||||
{
|
||||
val message =
|
||||
if(args.isEmpty)
|
||||
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
|
||||
else
|
||||
detail(args, h.detail) mkString("\n", "\n\n", "\n")
|
||||
System.out.println(message)
|
||||
s
|
||||
}
|
||||
def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] =
|
||||
selected.distinct flatMap { detailMap get _ }
|
||||
|
||||
def multiParser(s: State): Parser[Seq[String]] =
|
||||
{
|
||||
val nonSemi = token(charClass(_ != ';').+, hide= const(true))
|
||||
( token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser&nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim) ).+
|
||||
}
|
||||
|
||||
def multiApplied(s: State) =
|
||||
Command.applyEffect( multiParser(s) )( _ ::: s )
|
||||
|
||||
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed) )
|
||||
|
||||
lazy val otherCommandParser = (s: State) => token(OptSpace ~> combinedLax(s, any.+) )
|
||||
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
||||
matched(s.combinedParser | token(any, hide= const(true)))
|
||||
|
||||
def ifLast = Command(IfLast, IfLastBrief, IfLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||
if(s.remainingCommands.isEmpty) arg :: s else s
|
||||
}
|
||||
def append = Command(AppendCommand, AppendLastBrief, AppendLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||
s.copy(remainingCommands = s.remainingCommands :+ arg)
|
||||
}
|
||||
|
||||
def setOnFailure = Command(OnFailure, OnFailureBrief, OnFailureDetailed)(otherCommandParser) { (s, arg) =>
|
||||
s.copy(onFailure = Some(arg))
|
||||
}
|
||||
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||
|
||||
def reboot = Command(RebootCommand, RebootBrief, RebootDetailed)(rebootParser) { (s, full) =>
|
||||
s.reboot(full)
|
||||
}
|
||||
def rebootParser(s: State) = token(Space ~> "full" ^^^ true) ?? false
|
||||
|
||||
def call = Command(ApplyCommand, ApplyBrief, ApplyDetailed)(_ => spaceDelimited("<class name>")) { (state,args) =>
|
||||
val loader = getClass.getClassLoader
|
||||
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader))
|
||||
(state /: loaded) { case (s, obj: (State => State)) => obj(s) }
|
||||
}
|
||||
|
||||
def exit = Command.command(TerminateAction, exitBrief, exitBrief ) ( _ exit true )
|
||||
|
||||
|
||||
def continuous =
|
||||
Command(ContinuousExecutePrefix, Help(continuousBriefHelp) )(otherCommandParser) { (s, arg) =>
|
||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||
val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg)
|
||||
Watched.executeContinuously(w, s, arg, repeat)
|
||||
}
|
||||
}
|
||||
|
||||
def history = Command.custom(historyParser, BasicCommandStrings.historyHelp)
|
||||
def historyParser(s: State): Parser[() => State] =
|
||||
Command.applyEffect(HistoryCommands.actionParser) { histFun =>
|
||||
val logError = (msg: String) => s.log.error(msg)
|
||||
val hp = s get historyPath getOrElse None
|
||||
val lines = hp.toList.flatMap( p => IO.readLines(p) ).toIndexedSeq
|
||||
histFun( complete.History(lines, hp, logError) ) match
|
||||
{
|
||||
case Some(commands) =>
|
||||
commands foreach println //printing is more appropriate than logging
|
||||
(commands ::: s).continue
|
||||
case None => s.fail
|
||||
}
|
||||
}
|
||||
|
||||
def shell = Command.command(Shell, ShellBrief, ShellDetailed) { s =>
|
||||
val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history"))
|
||||
val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " }
|
||||
val reader = new FullReader(history, s.combinedParser)
|
||||
val line = reader.readLine(prompt)
|
||||
line match {
|
||||
case Some(line) =>
|
||||
val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands)
|
||||
if(line.trim.isEmpty) newState else newState.clearGlobalLog
|
||||
case None => s
|
||||
}
|
||||
}
|
||||
|
||||
def read = Command.make(ReadCommand, ReadBrief, ReadDetailed)(s => applyEffect(readParser(s))(doRead(s)) )
|
||||
def readParser(s: State) =
|
||||
{
|
||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||
val portAndSuccess = token(OptSpace) ~> Port
|
||||
portAndSuccess || files
|
||||
}
|
||||
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
||||
arg match
|
||||
{
|
||||
case Left(portAndSuccess) =>
|
||||
val port = math.abs(portAndSuccess)
|
||||
val previousSuccess = portAndSuccess >= 0
|
||||
readMessage(port, previousSuccess) match
|
||||
{
|
||||
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(ReadCommand + " " + (-port)))
|
||||
case None =>
|
||||
System.err.println("Connection closed.")
|
||||
s.fail
|
||||
}
|
||||
case Right(from) =>
|
||||
val notFound = notReadable(from)
|
||||
if(notFound.isEmpty)
|
||||
readLines(from) ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
|
||||
else {
|
||||
s.log.error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
|
||||
s
|
||||
}
|
||||
}
|
||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
||||
{
|
||||
// split into two connections because this first connection ends the previous communication
|
||||
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
||||
// and this second connection starts the next communication
|
||||
xsbt.IPC.client(port) { ipc =>
|
||||
val message = ipc.receive
|
||||
if(message eq null) None else Some(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def alias = Command.make(AliasCommand, AliasBrief, AliasDetailed) { s =>
|
||||
val name = token(OpOrID.examples( aliasNames(s) : _*) )
|
||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||
val sfree = removeAliases(s)
|
||||
val to = matched(sfree.combinedParser, partial = true) | any.+.string
|
||||
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
|
||||
applyEffect(base)(t => runAlias(s, t) )
|
||||
}
|
||||
|
||||
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
||||
args match
|
||||
{
|
||||
case None => printAliases(s); s
|
||||
case Some(x ~ None) if !x.isEmpty => printAlias(s, x.trim); s
|
||||
case Some(name ~ Some(None)) => removeAlias(s, name.trim)
|
||||
case Some(name ~ Some(Some(value))) => addAlias(s, name.trim, value.trim)
|
||||
}
|
||||
def addAlias(s: State, name: String, value: String): State =
|
||||
if(Command validID name) {
|
||||
val removed = removeAlias(s, name)
|
||||
if(value.isEmpty) removed else removed.copy(definedCommands = newAlias(name, value) +: removed.definedCommands)
|
||||
} else {
|
||||
System.err.println("Invalid alias name '" + name + "'.")
|
||||
s.fail
|
||||
}
|
||||
|
||||
def removeAliases(s: State): State = removeTagged(s, CommandAliasKey)
|
||||
def removeAlias(s: State, name: String): State = s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)) )
|
||||
|
||||
def removeTagged(s: State, tag: AttributeKey[_]): State = s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
||||
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] = as.filter(c => ! (c.tags contains tag))
|
||||
|
||||
def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c))
|
||||
def isNamed(name: String, alias: Option[(String,String)]): Boolean = alias match { case None => false; case Some((n,_)) => name == n }
|
||||
|
||||
def getAlias(c: Command): Option[(String,String)] = c.tags get CommandAliasKey
|
||||
def printAlias(s: State, name: String): Unit = printAliases(aliases(s,(n,v) => n == name) )
|
||||
def printAliases(s: State): Unit = printAliases(allAliases(s))
|
||||
def printAliases(as: Seq[(String,String)]): Unit =
|
||||
for( (name,value) <- as)
|
||||
println("\t" + name + " = " + value)
|
||||
|
||||
def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1)
|
||||
def allAliases(s: State): Seq[(String,String)] = aliases(s, (n,v) => true)
|
||||
def aliases(s: State, pred: (String,String) => Boolean): Seq[(String,String)] =
|
||||
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
||||
|
||||
def newAlias(name: String, value: String): Command =
|
||||
Command.make(name, (name, "'" + value + "'"), "Alias of '" + value + "'")(aliasBody(name, value)).tag(CommandAliasKey, (name, value))
|
||||
def aliasBody(name: String, value: String)(state: State): Parser[() => State] =
|
||||
OptSpace ~> Parser(Command.combine(removeAlias(state,name).definedCommands)(state))(value)
|
||||
|
||||
def delegateToAlias(name: String, orElse: Parser[() => State])(state: State): Parser[() => State] =
|
||||
aliases(state, (nme,_) => nme == name).headOption match {
|
||||
case None => orElse
|
||||
case Some((n,v)) => aliasBody(n,v)(state)
|
||||
}
|
||||
|
||||
val CommandAliasKey = AttributeKey[(String,String)]("is-command-alias", "Internal: marker for Commands created as aliases for another command.")
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
object BasicKeys
|
||||
{
|
||||
val historyPath = AttributeKey[Option[File]]("history", "The location where command line history is persisted.")
|
||||
val shellPrompt = AttributeKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.")
|
||||
val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.")
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
object CommandUtil
|
||||
{
|
||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (line => IO.readLines(line)) flatMap processLine
|
||||
def processLine(s: String) = { val trimmed = s.trim; if(ignoreLine(trimmed)) None else Some(trimmed) }
|
||||
def ignoreLine(s: String) = s.isEmpty || s.startsWith("#")
|
||||
|
||||
private def canRead = (_: File).canRead
|
||||
def notReadable(files: Seq[File]): Seq[File] = files filterNot canRead
|
||||
def readable(files: Seq[File]): Seq[File] = files filter canRead
|
||||
|
||||
// slightly better fallback in case of older launcher
|
||||
def bootDirectory(state: State): File =
|
||||
try { state.configuration.provider.scalaProvider.launcher.bootDirectory }
|
||||
catch { case e: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||
|
||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] =
|
||||
{
|
||||
val width = in.map(_._1.length).max
|
||||
in.map { case (a, b) => (" " + fill(a, width) + sep + b) }
|
||||
}
|
||||
def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0)
|
||||
|
||||
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
||||
(s get key) match {
|
||||
case None => s.log.error(ifMissing); s.fail
|
||||
case Some(nav) => f(nav)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009, 2010, 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import java.io.{File, PrintWriter}
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
object MainLoop
|
||||
{
|
||||
/** Entry point to run the remaining commands in State with managed global logging.*/
|
||||
def runLogged(state: State): xsbti.MainResult =
|
||||
runLoggedLoop(state, state.globalLogging.backing)
|
||||
|
||||
/** Run loop that evaluates remaining commands and manages changes to global logging configuration.*/
|
||||
@tailrec def runLoggedLoop(state: State, logBacking: GlobalLogBacking): xsbti.MainResult =
|
||||
runAndClearLast(state, logBacking) match {
|
||||
case ret: Return => // delete current and last log files when exiting normally
|
||||
logBacking.file.delete()
|
||||
deleteLastLog(logBacking)
|
||||
ret.result
|
||||
case clear: ClearGlobalLog => // delete previous log file, move current to previous, and start writing to a new file
|
||||
deleteLastLog(logBacking)
|
||||
runLoggedLoop(clear.state, logBacking.shiftNew())
|
||||
case keep: KeepGlobalLog => // make previous log file the current log file
|
||||
logBacking.file.delete
|
||||
runLoggedLoop(keep.state, logBacking.unshift)
|
||||
}
|
||||
|
||||
/** Runs the next sequence of commands, cleaning up global logging after any exceptions. */
|
||||
def runAndClearLast(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
try
|
||||
runWithNewLog(state, logBacking)
|
||||
catch {
|
||||
case e: xsbti.FullReload =>
|
||||
deleteLastLog(logBacking)
|
||||
throw e // pass along a reboot request
|
||||
case e =>
|
||||
System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
||||
deleteLastLog(logBacking)
|
||||
throw e
|
||||
}
|
||||
|
||||
/** Deletes the previous global log file. */
|
||||
def deleteLastLog(logBacking: GlobalLogBacking): Unit =
|
||||
logBacking.last.foreach(_.delete())
|
||||
|
||||
/** Runs the next sequence of commands with global logging in place. */
|
||||
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
Using.fileWriter(append = true)(logBacking.file) { writer =>
|
||||
val out = new java.io.PrintWriter(writer)
|
||||
val loggedState = state.copy(globalLogging = logBacking.newLogger(out, logBacking))
|
||||
try run(loggedState) finally out.close()
|
||||
}
|
||||
sealed trait RunNext
|
||||
final class ClearGlobalLog(val state: State) extends RunNext
|
||||
final class KeepGlobalLog(val state: State) extends RunNext
|
||||
final class Return(val result: xsbti.MainResult) extends RunNext
|
||||
|
||||
/** Runs the next sequence of commands that doesn't require global logging changes.*/
|
||||
@tailrec def run(state: State): RunNext =
|
||||
state.next match
|
||||
{
|
||||
case State.Continue => run(next(state))
|
||||
case State.ClearGlobalLog => new ClearGlobalLog(state.continue)
|
||||
case State.KeepLastLog => new KeepGlobalLog(state.continue)
|
||||
case ret: State.Return => new Return(ret.result)
|
||||
}
|
||||
|
||||
def next(state: State): State =
|
||||
ErrorHandling.wideConvert { state.process(Command.process) } match
|
||||
{
|
||||
case Right(s) => s
|
||||
case Left(t: xsbti.FullReload) => throw t
|
||||
case Left(t) => handleException(t, state)
|
||||
}
|
||||
|
||||
def handleException(e: Throwable, s: State): State =
|
||||
handleException(e, s, s.log)
|
||||
def handleException(e: Throwable, s: State, log: Logger): State =
|
||||
{
|
||||
e match
|
||||
{
|
||||
case _: AlreadyHandledException | _: UnprintableException => ()
|
||||
case ite: InvocationTargetException =>
|
||||
val cause = ite.getCause
|
||||
if(cause == null || cause == ite) logFullException(ite, log) else handleException(cause, s, log)
|
||||
case _: MessageOnlyException => log.error(e.toString)
|
||||
case _ => logFullException(e, log)
|
||||
}
|
||||
s.fail
|
||||
}
|
||||
def logFullException(e: Throwable, log: Logger)
|
||||
{
|
||||
log.trace(e)
|
||||
log.error(ErrorHandling reducedToString e)
|
||||
log.error("Use 'last' for the full log.")
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
import CommandSupport.{FailureWall, logger}
|
||||
|
||||
/**
|
||||
Data structure representing all command execution information.
|
||||
|
|
@ -27,6 +26,7 @@ final case class State(
|
|||
remainingCommands: Seq[String],
|
||||
history: State.History,
|
||||
attributes: AttributeMap,
|
||||
globalLogging: GlobalLogging,
|
||||
next: State.Next
|
||||
) extends Identity {
|
||||
lazy val combinedParser = Command.combine(definedCommands)(this)
|
||||
|
|
@ -112,6 +112,8 @@ trait StateOps {
|
|||
|
||||
object State
|
||||
{
|
||||
final val FailureWall = "---"
|
||||
|
||||
/** Represents the next action for the command processor.*/
|
||||
sealed trait Next
|
||||
/** Indicates that the command processor should process the next command.*/
|
||||
|
|
@ -175,7 +177,7 @@ object State
|
|||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key)))
|
||||
def has(key: AttributeKey[_]) = s.attributes contains key
|
||||
def remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key)
|
||||
def log = CommandSupport.logger(s)
|
||||
def log = s.globalLogging.full
|
||||
def fail =
|
||||
{
|
||||
val remaining = s.remainingCommands.dropWhile(_ != FailureWall)
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import CommandSupport.{ClearOnFailure,FailureWall}
|
||||
import BasicCommandStrings.ClearOnFailure
|
||||
import State.FailureWall
|
||||
import annotation.tailrec
|
||||
import java.io.File
|
||||
import Types.const
|
||||
|
|
@ -63,7 +64,7 @@ object Watched
|
|||
catch { case e: Exception =>
|
||||
val log = s.log
|
||||
log.error("Error occurred obtaining files to watch. Terminating continuous execution...")
|
||||
BuiltinCommands.handleException(e, s, log)
|
||||
MainLoop.handleException(e, s, log)
|
||||
(false, watchState, s.fail)
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ object Sbt extends Build
|
|||
{
|
||||
override lazy val settings = super.settings ++ buildSettings ++ Status.settings
|
||||
def buildSettings = Seq(
|
||||
organization := "org.scala-tools.sbt",
|
||||
organization := "org.scala-sbt",
|
||||
version := "0.12.0-SNAPSHOT",
|
||||
publishArtifact in packageDoc := false,
|
||||
scalaVersion := "2.9.1",
|
||||
|
|
@ -111,8 +111,11 @@ object Sbt extends Build
|
|||
classfileSub, classpathSub, compileIncrementalSub, compilePersistSub, compilerSub, completeSub, apiSub,
|
||||
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)
|
||||
|
||||
lazy val commandSub = testedBaseProject(commandPath, "Command") dependsOn(interfaceSub, ioSub, launchInterfaceSub, logSub, completeSub, classpathSub)
|
||||
|
||||
// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
|
||||
lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub)
|
||||
lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub, commandSub)
|
||||
|
||||
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
|
||||
// technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
|
||||
// with the sole purpose of providing certain identifiers without qualification (with a package object)
|
||||
|
|
@ -126,6 +129,7 @@ object Sbt extends Build
|
|||
def utilPath = file("util")
|
||||
def compilePath = file("compile")
|
||||
def mainPath = file("main")
|
||||
def commandPath = mainPath / "command"
|
||||
def scriptedPath = file("scripted")
|
||||
|
||||
def sbtSettings = Seq(
|
||||
|
|
@ -137,8 +141,8 @@ object Sbt extends Build
|
|||
(launcher, scriptedSbtClasspath, scriptedSbtInstance, _, v, sv, ssv, sourcePath, args) =>
|
||||
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, scriptedSbtInstance.loader)
|
||||
val m = ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
|
||||
val r = m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[String], classOf[String], classOf[String], classOf[Array[String]], classOf[File])
|
||||
try { r.invoke(m, sourcePath, true: java.lang.Boolean, v, sv, ssv, args.toArray[String], launcher) }
|
||||
val r = m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[String], classOf[String], classOf[String], classOf[Array[String]], classOf[File], classOf[Array[String]])
|
||||
try { r.invoke(m, sourcePath, true: java.lang.Boolean, v, sv, ssv, args.toArray[String], launcher, Array[String]()) }
|
||||
catch { case ite: java.lang.reflect.InvocationTargetException => throw ite.getCause }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders
|
||||
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra
|
||||
{
|
||||
@deprecated("Renamed to CommandStrings.", "0.12.0")
|
||||
val CommandSupport = CommandStrings
|
||||
|
||||
@deprecated("Use SettingKey, which is a drop-in replacement.", "0.11.1")
|
||||
type ScopedSetting[T] = SettingKey[T]
|
||||
@deprecated("Use TaskKey, which is a drop-in replacement.", "0.11.1")
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ $ absent ran
|
|||
$ exists ran
|
||||
$ delete ran
|
||||
|
||||
# single project, Aggregate = Enabled on Mark
|
||||
> set aggregate in Mark := false
|
||||
# single project, aggregate = true on Mark
|
||||
> set aggregate in Mark := true
|
||||
> mark
|
||||
$ exists ran
|
||||
$ delete ran
|
||||
|
||||
# single project, Aggregate = Disabled on Mark
|
||||
# single project, aggregate = false on Mark
|
||||
> set aggregate in Mark := false
|
||||
> mark
|
||||
$ exists ran
|
||||
|
|
@ -45,7 +45,7 @@ $ absent ran
|
|||
$ exists sub/ran
|
||||
$ delete sub/ran
|
||||
|
||||
# unset the root task. the sub task shouldn't be runnable from root
|
||||
# unset the root task. the sub task shouldn't be runnable from root without aggregation
|
||||
> session remove 1
|
||||
-> mark
|
||||
$ absent ran sub/ran
|
||||
|
|
@ -80,20 +80,6 @@ $ exists ran sub/ran
|
|||
$ absent sub/sub/ran
|
||||
$ delete ran sub/ran
|
||||
|
||||
# check explicit aggregation. running on root should run root/mark and sub2/mark
|
||||
> set aggregate in Mark := Aggregation(sub2 :: Nil)
|
||||
> mark
|
||||
$ exists ran sub/sub/ran
|
||||
$ absent sub/ran
|
||||
$ delete ran sub/sub/ran
|
||||
|
||||
# check intransitive aggregation. running on root should not continue to sub2/mark
|
||||
> set aggregate in Mark := Aggregation(sub :: Nil, false)
|
||||
> mark
|
||||
$ exists ran sub/ran
|
||||
$ absent sub/sub/ran
|
||||
$ delete ran sub/ran
|
||||
|
||||
# the aggregation setting in a leaf shouldn't affect whether it can be run directly
|
||||
> set aggregate in (sub2, Mark) := false
|
||||
> sub2/mark
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ publishTo <<= baseDirectory { base =>
|
|||
|
||||
projectID <<= projectID { _.extra("e:color" -> "red") }
|
||||
|
||||
organization := "org.scala-tools.sbt"
|
||||
organization := "org.scala-sbt"
|
||||
|
||||
version := "1.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ resolvers <<= baseDirectory( base =>
|
|||
|
||||
libraryDependencies <<= baseDirectory { base =>
|
||||
val color = IO.read(base / "color")
|
||||
val dep = "org.scala-tools.sbt" %% "define-color" % "1.0" extra("e:color" -> color)
|
||||
val dep = "org.scala-sbt" %% "define-color" % "1.0" extra("e:color" -> color)
|
||||
dep :: Nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ object InfoTest extends Build
|
|||
lazy val root = Project("root", file(".")) settings(
|
||||
ivyPaths <<= (baseDirectory, target)( (dir, t) => new IvyPaths(dir, Some(t / "ivy-cache"))),
|
||||
ivyXML <<= (customInfo, organization, moduleName, version) apply inlineXML,
|
||||
scalaVersion := "2.9.0",
|
||||
projectID ~= (_ cross false),
|
||||
customInfo <<= baseDirectory{_ / "info" exists },
|
||||
TaskKey[Unit]("check-download") <<= checkDownload,
|
||||
|
|
@ -24,9 +25,9 @@ object InfoTest extends Build
|
|||
ScalaQuery is a type-safe database query API for Scala.
|
||||
</description>
|
||||
</info>
|
||||
<dependency org="org.scalacheck" name="scalacheck" rev="1.5"/>)
|
||||
<dependency org="org.scala-tools.testing" name="scalacheck_2.9.0" rev="1.9"/>)
|
||||
else
|
||||
<dependency org="org.scalacheck" name="scalacheck" rev="1.5"/>
|
||||
<dependency org="org.scala-tools.testing" name="scalacheck_2.9.0" rev="1.9"/>
|
||||
|
||||
def checkDownload = (dependencyClasspath in Compile) map { cp => if(cp.isEmpty) error("Dependency not downloaded"); () }
|
||||
def checkInfo = (customInfo, delivered) map { (addInfo, d) =>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
resolvers += ScalaToolsReleases
|
||||
|
||||
libraryDependencies += "org.scalacheck" % "scalacheck" % "1.5"
|
||||
|
||||
ivyPaths <<= baseDirectory( dir => new IvyPaths(dir, Some(dir / "ivy-home")))
|
||||
|
|
@ -7,4 +9,4 @@ TaskKey[Unit]("check") <<= update map { report =>
|
|||
assert(!files.isEmpty, "ScalaCheck module not found in update report")
|
||||
val missing = files.filter(! _.exists)
|
||||
assert(missing.isEmpty, "Reported ScalaCheck artifact files don't exist: " + missing.mkString(", "))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
#Project properties
|
||||
#Fri Jan 30 20:49:57 EST 2009
|
||||
project.name=Inline Dependency Test A
|
||||
project.version=1.0
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import sbt._
|
||||
|
||||
class UpdateTestProject(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val sc = "org.scalacheck" % "scalacheck" % "1.5"
|
||||
override def ivyCacheDirectory = Some(outputPath / "ivy-cache")
|
||||
override def disableCrossPaths = true
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
object MakePomTest extends Build
|
||||
{
|
||||
lazy val root = Project("root", file(".")) settings(
|
||||
resolvers += ScalaToolsReleases,
|
||||
readPom <<= makePom map XML.loadFile,
|
||||
TaskKey[Unit]("check-pom") <<= checkPom,
|
||||
TaskKey[Unit]("check-extra") <<= checkExtra,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
moduleConfigurations += ModuleConfiguration("org.scala-lang", "*", "2.10.0-.*", scalaSnapshots)
|
||||
}
|
||||
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20111001.020530-165"
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20120122.024228-256"
|
||||
|
||||
resolvers := Nil
|
||||
|
|
@ -4,6 +4,6 @@
|
|||
moduleConfigurations += ModuleConfiguration("org.not-scala-lang", "*", "2.10.0-.*", scalaSnapshots)
|
||||
}
|
||||
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20111001.020530-165"
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20120122.024228-256"
|
||||
|
||||
resolvers := Nil
|
||||
|
|
@ -4,6 +4,6 @@
|
|||
moduleConfigurations += ModuleConfiguration("org.scala-lang", "*", "2.10.0-.*", scalaSnapshots)
|
||||
}
|
||||
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20111001.020530-165"
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20120122.024228-256"
|
||||
|
||||
resolvers := Nil
|
||||
|
|
@ -4,6 +4,6 @@
|
|||
moduleConfigurations += ModuleConfiguration("org.scala-lang", "*", "2.10.0-.*", scalaSnapshots)
|
||||
}
|
||||
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20111001.020530-164"
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.10.0-20120122.024228-255"
|
||||
|
||||
resolvers := Nil
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
object ParentTest extends Build
|
||||
{
|
||||
lazy val parent: Project = Project("Flowmodel", file(".")) aggregate(core, reporters)
|
||||
lazy val core: Project = Project("Flowmodel core", file("core"), delegates = parent :: Nil)
|
||||
lazy val reporters: Project = Project("Extra reporters", file("reporters"), delegates = parent :: Nil) aggregate(jfreechart) dependsOn(jfreechart)
|
||||
lazy val jfreechart: Project = Project("JFreeChart reporters", file("jfreechart")/*, delegates = reporters :: Nil*/) dependsOn(core)
|
||||
lazy val core: Project = Project("Flowmodel-core", file("core"), delegates = parent :: Nil)
|
||||
lazy val reporters: Project = Project("Extra-reporters", file("reporters"), delegates = parent :: Nil) aggregate(jfreechart) dependsOn(jfreechart)
|
||||
lazy val jfreechart: Project = Project("JFreeChart-reporters", file("jfreechart")/*, delegates = reporters :: Nil*/) dependsOn(core)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
object PomRepoTest extends Build
|
||||
{
|
||||
lazy val root = Project("root", file(".")) settings(
|
||||
resolvers ++= Seq(local, ScalaToolsSnapshots),
|
||||
resolvers ++= Seq(local, ScalaToolsReleases, ScalaToolsSnapshots),
|
||||
InputKey[Unit]("check-pom") <<= InputTask(_ => spaceDelimited("<args>")) { result => (makePom, result, streams) map checkPomRepositories },
|
||||
makePomConfiguration <<= (makePomConfiguration, baseDirectory) { (conf, base) =>
|
||||
conf.copy(filterRepositories = pomIncludeRepository(base, conf.filterRepositories) )
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ object PomTest extends Build
|
|||
{
|
||||
override def settings = super.settings :+ (TaskKey[Unit]("check-pom") <<= checkPom)
|
||||
|
||||
lazy val subJar = Project("Sub Jar", file("subJar"))
|
||||
lazy val subWar = Project("Sub War", file("subWar")) settings( warArtifact)
|
||||
lazy val subParent = Project("Sub Parent", file("subParent")) settings( publishArtifact in Compile := false )
|
||||
lazy val subJar = Project("sub-jar", file("subJar"))
|
||||
lazy val subWar = Project("sub-war", file("subWar")) settings( warArtifact)
|
||||
lazy val subParent = Project("sub-parent", file("subParent")) settings( publishArtifact in Compile := false )
|
||||
|
||||
def art(p: ProjectReference) = makePom in p
|
||||
def checkPom = (art(subJar), art(subWar), art(subParent)) map { (jar, war, pom) =>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,23 @@ import sbt._
|
|||
import Keys._
|
||||
object P extends Build
|
||||
{
|
||||
override def settings = super.settings ++ Seq( scalaVersion in update := "2.9.0" )
|
||||
override def settings = super.settings ++ Seq(
|
||||
scalaBinaryVersion in update := "2.9.0",
|
||||
resolvers += ScalaToolsReleases
|
||||
)
|
||||
|
||||
def configIvyScala =
|
||||
ivyScala ~= { _.map(_.copy(checkExplicit = false)) }
|
||||
|
||||
val declared = SettingKey[Boolean]("declared")
|
||||
lazy val a = Project("A", file("a")) settings(
|
||||
libraryDependencies += "org.scala-tools.sbinary" %% "sbinary" % "0.4.0" % "provided"
|
||||
libraryDependencies += "org.scala-tools.sbinary" %% "sbinary" % "0.4.0" % "provided",
|
||||
configIvyScala
|
||||
)
|
||||
|
||||
lazy val b = Project("B", file("b")) dependsOn(a) settings(
|
||||
libraryDependencies <<= declared(d => if(d) Seq("org.scala-tools.sbinary" %% "sbinary" % "0.4.0" % "provided") else Nil),
|
||||
declared <<= baseDirectory(_ / "declare.lib" exists)
|
||||
declared <<= baseDirectory(_ / "declare.lib" exists),
|
||||
configIvyScala
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
libraryDependencies <<= (libraryDependencies, appConfiguration) { (deps, conf) =>
|
||||
deps :+ ("org.scala-tools.sbt" %% "sbt" % conf.provider.id.version)
|
||||
deps :+ ("org.scala-sbt" %% "sbt" % conf.provider.id.version)
|
||||
}
|
||||
|
|
@ -3,6 +3,10 @@ import Keys._
|
|||
|
||||
object Build extends Build
|
||||
{
|
||||
override def settings = super.settings ++ Seq(
|
||||
sbtBinaryVersion <<= sbtVersion
|
||||
)
|
||||
|
||||
lazy val root = Project("root", file(".")) aggregate(a,b,c)
|
||||
lazy val a = Project("a", file("a"))
|
||||
lazy val b = Project("b", file("b"))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
libraryDependencies += "org.scalatest" % "scalatest" % "1.3"
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "1.6.1" % "test"
|
||||
|
||||
|
||||
testOptions in Configurations.Test ++= {
|
||||
def args(path: String, args: String*): Seq[TestOption] = if(file(path).exists) Tests.Argument(args : _*) :: Nil else Nil
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ object B extends Build
|
|||
lazy val root =
|
||||
Project("root", file("."))
|
||||
.configs( IntegrationTest )
|
||||
.settings( libraryDependencies += specs )
|
||||
.settings( Defaults.itSettings : _*)
|
||||
.settings(
|
||||
libraryDependencies += specs,
|
||||
resolvers += ScalaToolsReleases
|
||||
)
|
||||
|
||||
lazy val specs = "org.scala-tools.testing" %% "specs" % "1.6.7.2" % "it,test" intransitive()
|
||||
lazy val specs = "org.specs2" %% "specs2" % "1.7.1" % "it,test"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
resolvers += ScalaToolsReleases
|
||||
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.8" % "test"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.foo.junit.test.blah
|
||||
|
||||
import org.junit._
|
||||
|
||||
class Failure
|
||||
{
|
||||
@Test def fail() { error("Fail!") }
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.foo.junit.test.blah
|
||||
|
||||
import org.junit._
|
||||
|
||||
class Success
|
||||
{
|
||||
@Test def succeed() { }
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
> test
|
||||
|
||||
$ copy-file changes/Success.scala src/test/scala/Success.scala
|
||||
> test
|
||||
|
||||
> test-only com.foo.junit.test.blah.Success
|
||||
|
||||
$ copy-file changes/Failure.scala src/test/scala/Failure.scala
|
||||
-> test
|
||||
-> test-only com.foo.junit.test.blah.Failure
|
||||
> test-only com.foo.junit.test.blah.Success
|
||||
|
|
@ -1 +1,3 @@
|
|||
libraryDependencies += "org.scala-tools.testing" %% "specs" % "1.6.7.2" intransitive()
|
||||
libraryDependencies += "org.specs2" %% "specs2" % "1.7.1" % "test"
|
||||
|
||||
resolvers += ScalaToolsReleases
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
object BasicTest extends Specification
|
||||
{
|
||||
"Test resource on test classpath" in {
|
||||
getClass.getResource("TestResource.txt") mustNotBe null
|
||||
getClass.getResource("TestResource.txt") must not beNull
|
||||
}
|
||||
"Main resource on test classpath" in {
|
||||
getClass.getResource("MainResource.txt") mustNotBe null
|
||||
getClass.getResource("MainResource.txt") must not beNull
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,3 @@
|
|||
libraryDependencies += "org.scala-tools.testing" %% "specs" % "1.6.7.2" intransitive()
|
||||
libraryDependencies += "org.specs2" %% "specs2" % "1.7.1" % "test"
|
||||
|
||||
resolvers += ScalaToolsReleases
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import org.specs._
|
||||
import org.specs2.mutable._
|
||||
|
||||
class B extends Specification
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ object ScriptedPlugin extends Plugin {
|
|||
ivyConfigurations += scriptedConf,
|
||||
scriptedSbt <<= (appConfiguration)(_.provider.id.version),
|
||||
scriptedScalas <<= (scalaVersion) { (scala) => ScriptedScalas(scala, scala) },
|
||||
libraryDependencies <<= (libraryDependencies, scriptedScalas, scriptedSbt) {(deps, scalas, version) => deps :+ "org.scala-tools.sbt" % ("scripted-sbt_" + scalas.build) % version % scriptedConf.toString },
|
||||
libraryDependencies <<= (libraryDependencies, scriptedScalas, scriptedSbt) {(deps, scalas, version) => deps :+ "org.scala-sbt" % ("scripted-sbt_" + scalas.build) % version % scriptedConf.toString },
|
||||
sbtLauncher <<= (appConfiguration)(app => IO.classLocationFile(app.provider.scalaProvider.launcher.getClass)),
|
||||
sbtTestDirectory <<= sourceDirectory / "sbt-test",
|
||||
scriptedBufferLog := true,
|
||||
|
|
|
|||
|
|
@ -92,12 +92,12 @@ object ScriptedTests
|
|||
val bootProperties = new File(args(5))
|
||||
val tests = args.drop(6)
|
||||
val logger = ConsoleLogger()
|
||||
run(directory, buffer, sbtVersion, defScalaVersion, buildScalaVersions, tests, logger, bootProperties, Seq())
|
||||
run(directory, buffer, sbtVersion, defScalaVersion, buildScalaVersions, tests, logger, bootProperties, Array())
|
||||
}
|
||||
def run(resourceBaseDirectory: File, bufferLog: Boolean, sbtVersion: String, defScalaVersion: String, buildScalaVersions: String, tests: Array[String], bootProperties: File, launchOpts: Seq[String]): Unit =
|
||||
def run(resourceBaseDirectory: File, bufferLog: Boolean, sbtVersion: String, defScalaVersion: String, buildScalaVersions: String, tests: Array[String], bootProperties: File, launchOpts: Array[String]): Unit =
|
||||
run(resourceBaseDirectory, bufferLog, sbtVersion, defScalaVersion, buildScalaVersions, tests, ConsoleLogger(), bootProperties, launchOpts)//new FullLogger(Logger.xlog2Log(log)))
|
||||
|
||||
def run(resourceBaseDirectory: File, bufferLog: Boolean, sbtVersion: String, defScalaVersion: String, buildScalaVersions: String, tests: Array[String], logger: AbstractLogger, bootProperties: File, launchOpts: Seq[String])
|
||||
def run(resourceBaseDirectory: File, bufferLog: Boolean, sbtVersion: String, defScalaVersion: String, buildScalaVersions: String, tests: Array[String], logger: AbstractLogger, bootProperties: File, launchOpts: Array[String])
|
||||
{
|
||||
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, sbtVersion, defScalaVersion, buildScalaVersions, bootProperties, launchOpts)
|
||||
for( ScriptedTest(group, name) <- get(tests, resourceBaseDirectory, logger) )
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
version: 2.9.1
|
||||
|
||||
[app]
|
||||
org: org.scala-tools.sbt
|
||||
org: org.scala-sbt
|
||||
name: sbt
|
||||
version: read(sbt.version)[0.12.0-SNAPSHOT]
|
||||
class: ${sbt.main.class-sbt.xMain}
|
||||
|
|
@ -13,8 +13,6 @@
|
|||
local
|
||||
typesafe-ivy-releases: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]
|
||||
maven-central
|
||||
scala-tools-releases
|
||||
scala-tools-snapshots
|
||||
|
||||
[ivy]
|
||||
ivy-home: ${sbt.ivy.home-${user.home}/.ivy2/}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
version: 2.9.1
|
||||
|
||||
[app]
|
||||
org: org.scala-tools.sbt
|
||||
org: org.scala-sbt
|
||||
name: sbt
|
||||
version: 0.12.0-SNAPSHOT
|
||||
class: sbt.ScriptMain
|
||||
|
|
@ -13,5 +13,3 @@
|
|||
local
|
||||
typesafe-ivy-releases: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]
|
||||
maven-central
|
||||
scala-tools-releases
|
||||
scala-tools-snapshots
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
version: 2.9.1
|
||||
|
||||
[app]
|
||||
org: org.scala-tools.sbt
|
||||
org: org.scala-sbt
|
||||
name: sbt
|
||||
version: 0.12.0-SNAPSHOT
|
||||
class: sbt.ConsoleMain
|
||||
|
|
@ -13,5 +13,3 @@
|
|||
local
|
||||
typesafe-ivy-releases: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]
|
||||
maven-central
|
||||
scala-tools-releases
|
||||
scala-tools-snapshots
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Incomplete.{Error, Value => IValue}
|
|||
* @param causes a list of incompletions that prevented `node` from completing
|
||||
* @param directCause the exception that caused `node` to not complete */
|
||||
final case class Incomplete(node: Option[AnyRef], tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
|
||||
extends Exception(message.orNull, directCause.orNull) {
|
||||
extends Exception(message.orNull, directCause.orNull) with UnprintableException {
|
||||
override def toString = "Incomplete(node=" + node + ", tpe=" + tpe + ", msg=" + message + ", causes=" + causes + ", directCause=" + directCause +")"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class TestFramework(val implClassName: String)
|
|||
try { Some(Class.forName(implClassName, true, loader).newInstance.asInstanceOf[Framework]) }
|
||||
catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None }
|
||||
}
|
||||
override def toString = "TestFramework(" + implClassName + ")"
|
||||
}
|
||||
final class TestDefinition(val name: String, val fingerprint: Fingerprint)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ trait Init[Scope]
|
|||
if(dist < 0) None else Some(dist)
|
||||
}
|
||||
|
||||
final class Uninitialized(val undefined: Seq[Undefined], msg: String) extends Exception(msg)
|
||||
final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString)
|
||||
final class Undefined(val definingKey: ScopedKey[_], val referencedKey: ScopedKey[_])
|
||||
final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.")
|
||||
def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(definingKey, referencedKey)
|
||||
|
|
|
|||
|
|
@ -4,4 +4,11 @@
|
|||
package sbt
|
||||
|
||||
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
|
||||
final class NoMessageException extends RuntimeException
|
||||
|
||||
/** 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 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
|
||||
|
|
@ -17,6 +17,7 @@ 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.*/
|
||||
|
|
@ -26,21 +27,40 @@ object IO
|
|||
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
|
||||
|
||||
/** 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) 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.erasure)
|
||||
|
||||
/** 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.erasure)
|
||||
|
||||
def toFile(url: URL) =
|
||||
/** 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) }
|
||||
|
||||
|
|
@ -61,6 +81,13 @@ object IO
|
|||
def assertDirectories(file: File*) { 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('.')
|
||||
|
|
@ -70,8 +97,13 @@ object IO
|
|||
(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.*/
|
||||
|
||||
/** 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)
|
||||
{
|
||||
val absFile = file.getAbsoluteFile
|
||||
|
|
@ -178,13 +210,16 @@ object IO
|
|||
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.*/
|
||||
/** 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)
|
||||
|
|
@ -223,7 +258,11 @@ object IO
|
|||
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 =
|
||||
|
|
@ -241,6 +280,8 @@ object IO
|
|||
}
|
||||
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)
|
||||
|
|
@ -250,6 +291,7 @@ object IO
|
|||
|
||||
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]
|
||||
|
|
@ -259,7 +301,10 @@ object IO
|
|||
for( (f, true) <- isEmpty) f.delete
|
||||
}
|
||||
|
||||
/** Deletes each file or directory (recursively) in `files`.*/
|
||||
def delete(files: Iterable[File]): Unit = files.foreach(delete)
|
||||
|
||||
/** Deletes `file`, recursively if it is a directory. */
|
||||
def delete(file: File)
|
||||
{
|
||||
translate("Error deleting file " + file + ": ")
|
||||
|
|
@ -273,26 +318,32 @@ object IO
|
|||
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.
|
||||
* @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.
|
||||
* @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)
|
||||
|
|
|
|||
|
|
@ -139,6 +139,13 @@ sealed abstract class PathFinder
|
|||
final def \ (literal: String): PathFinder = this / literal
|
||||
|
||||
def x_: Traversable[(File,T)] = x(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)] =
|
||||
x(mapper, errorIfNone)
|
||||
|
||||
/** 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.*/
|
||||
|
|
@ -154,7 +161,7 @@ sealed abstract class PathFinder
|
|||
* <code>descendantsExcept("*.jar", ".svn")</code>*/
|
||||
def descendantsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder =
|
||||
(this ** include) --- (this ** intermediateExclude ** include)
|
||||
@deprecated("Use `descendantsExcept` instead.", "0.11.3")
|
||||
@deprecated("Use `descendantsExcept` instead.", "0.12.0")
|
||||
def descendentsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder =
|
||||
descendantsExcept(include, intermediateExclude)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,35 +10,62 @@ 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)
|
||||
|
||||
def rebase(oldBase: File, newBase0: String): PathMap =
|
||||
/** 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 newBase = normalizeBase(newBase0)
|
||||
val normNewBase = normalizeBase(newBase)
|
||||
(file: File) =>
|
||||
if(file == oldBase)
|
||||
Some( if(newBase.isEmpty) "." else newBase )
|
||||
Some( if(normNewBase.isEmpty) "." else normNewBase )
|
||||
else
|
||||
IO.relativize(oldBase, file).map(newBase + _)
|
||||
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 => 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)
|
||||
def flatRebase(newBase0: String): PathMap =
|
||||
|
||||
/** 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 newBase = normalizeBase(newBase0)
|
||||
f => Some(newBase + f.getName)
|
||||
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)
|
||||
def resolve(newDirectory: File): FileMap = file => Some(new File(newDirectory, file.getPath))
|
||||
|
||||
/** 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)
|
||||
|
|
@ -46,9 +73,22 @@ trait Mapper
|
|||
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 descendents 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 descendents 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 )
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.{File, PrintWriter}
|
||||
|
||||
final case class GlobalLogging(full: Logger, backed: ConsoleLogger, backing: GlobalLogBacking)
|
||||
final case class GlobalLogBacking(file: File, last: Option[File], newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: () => File)
|
||||
{
|
||||
def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newLogger, newBackingFile)
|
||||
def shiftNew() = shift(newBackingFile())
|
||||
def unshift = GlobalLogBacking(last getOrElse file, None, newLogger, newBackingFile)
|
||||
}
|
||||
object GlobalLogBacking
|
||||
{
|
||||
def apply(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File): GlobalLogBacking =
|
||||
GlobalLogBacking(newBackingFile, None, newLogger, newBackingFile _)
|
||||
}
|
||||
object GlobalLogging
|
||||
{
|
||||
def initial(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File): GlobalLogging =
|
||||
{
|
||||
val log = ConsoleLogger()
|
||||
GlobalLogging(log, log, GlobalLogBacking(newLogger, newBackingFile))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
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(writer: PrintWriter, backing: GlobalLogBacking): GlobalLogging =
|
||||
{
|
||||
val backed = defaultBacked()(writer)
|
||||
val full = multiLogger(defaultMultiConfig( backed ) )
|
||||
GlobalLogging(full, backed, backing)
|
||||
}
|
||||
|
||||
def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig =
|
||||
new MultiLoggerConfig(defaultScreen, backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue)
|
||||
|
||||
def defaultScreen: AbstractLogger = ConsoleLogger()
|
||||
|
||||
def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger =
|
||||
to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false
|
||||
}
|
||||
|
||||
final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int)
|
||||
Loading…
Reference in New Issue