sbt/main/Defaults.scala

718 lines
36 KiB
Scala
Raw Normal View History

2011-02-06 03:40:10 +01:00
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt
import Build.data
2011-02-27 22:28:00 +01:00
import Scope.{GlobalScope, ThisScope}
2011-03-02 12:46:28 +01:00
import compiler.Discovery
2011-02-27 22:28:00 +01:00
import Project.{inConfig, Initialize, inScope, inTask, ScopedKey, Setting}
import Configurations.{Compile => CompileConf, Test => TestConf}
2011-03-21 03:54:01 +01:00
import EvaluateTask.resolvedScoped
import complete._
import std.TaskExtra._
2011-03-14 02:34:17 +01:00
import scala.xml.{Node => XNode,NodeSeq}
import org.apache.ivy.core.module.{descriptor, id}
import descriptor.ModuleDescriptor, id.ModuleRevisionId
2011-03-14 02:34:17 +01:00
import java.io.File
import java.net.URL
2011-03-14 02:34:17 +01:00
import Types._
import Path._
import GlobFilter._
import Keys._
2011-03-14 02:34:17 +01:00
object Defaults
{
implicit def richFileSetting(s: ScopedSetting[File]): RichFileSetting = new RichFileSetting(s)
implicit def richFilesSetting(s: ScopedSetting[Seq[File]]): RichFilesSetting = new RichFilesSetting(s)
final class RichFileSetting(s: ScopedSetting[File]) extends RichFileBase
{
def /(c: String): Initialize[File] = s { _ / c }
2011-02-06 17:33:29 +01:00
protected[this] def map0(f: PathFinder => PathFinder) = s(file => finder(f)(file :: Nil))
}
final class RichFilesSetting(s: ScopedSetting[Seq[File]]) extends RichFileBase
{
def /(s: String): Initialize[Seq[File]] = map0 { _ / s }
2011-02-06 17:33:29 +01:00
protected[this] def map0(f: PathFinder => PathFinder) = s(finder(f))
}
sealed abstract class RichFileBase
{
def *(filter: FileFilter): Initialize[Seq[File]] = map0 { _ * filter }
def **(filter: FileFilter): Initialize[Seq[File]] = map0 { _ ** filter }
protected[this] def map0(f: PathFinder => PathFinder): Initialize[Seq[File]]
protected[this] def finder(f: PathFinder => PathFinder): Seq[File] => Seq[File] =
in => f(in).getFiles
}
2011-03-02 12:46:28 +01:00
def configSrcSub(key: ScopedSetting[File]): Initialize[File] = (key, configuration) { (src, conf) => src / nameForSrc(conf.name) }
def nameForSrc(config: String) = if(config == "compile") "main" else config
def prefix(config: String) = if(config == "compile") "" else config + "-"
def toSeq[T](key: ScopedSetting[T]): Initialize[Seq[T]] = key( _ :: Nil)
2011-02-27 05:56:30 +01:00
def extractAnalysis[T](a: Attributed[T]): (T, inc.Analysis) =
2011-03-02 12:46:28 +01:00
(a.data, a.metadata get Keys.analysis getOrElse inc.Analysis.Empty)
2011-02-27 05:56:30 +01:00
def analysisMap[T](cp: Seq[Attributed[T]]): Map[T, inc.Analysis] =
(cp map extractAnalysis).toMap
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(
pollInterval :== 500,
scalaHome :== None,
2011-03-02 12:46:28 +01:00
javaHome :== None,
outputStrategy :== None,
fork :== false,
javaOptions :== Nil,
2011-03-14 02:34:17 +01:00
sbtPlugin :== false,
2011-03-02 12:46:28 +01:00
crossPaths :== true,
2011-03-14 02:34:17 +01:00
generatedResources :== Nil,
classpathTypes := Set("jar", "bundle"),
// shellPrompt :== (_ => "> "),
2011-03-02 12:46:28 +01:00
aggregate :== Aggregation.Enabled,
maxErrors :== 100,
showTiming :== true,
timingFormat :== Aggregation.defaultFormat,
showSuccess :== true,
2011-03-02 12:46:28 +01:00
commands :== Nil,
retrieveManaged :== false,
2011-03-02 12:46:28 +01:00
settings <<= EvaluateTask.state map { state => Project.structure(state).data }
2011-02-27 22:28:00 +01:00
))
def projectCore: Seq[Setting[_]] = Seq(
2011-03-02 12:46:28 +01:00
name <<= thisProject(_.id),
version :== "0.1"
)
def paths = Seq(
2011-03-02 12:46:28 +01:00
baseDirectory <<= thisProject(_.base),
target <<= baseDirectory / "target",
defaultExcludes in GlobalScope :== (".*" - ".") || HiddenFileFilter,
historyPath <<= target(t => Some(t / ".history")),
cacheDirectory <<= target / "cache",
sourceDirectory <<= baseDirectory / "src",
sourceFilter in GlobalScope :== ("*.java" | "*.scala"),
sourceManaged <<= baseDirectory / "src_managed"
)
lazy val configPaths = Seq(
2011-03-02 12:46:28 +01:00
sourceDirectory <<= configSrcSub( sourceDirectory in Scope(This,Global,This,This) ),
sourceManaged <<= configSrcSub(sourceManaged),
cacheDirectory <<= (cacheDirectory, configuration) { _ / _.name },
classDirectory <<= (target, configuration) { (outDir, conf) => outDir / (prefix(conf.name) + "classes") },
docDirectory <<= (target, configuration) { (outDir, conf) => outDir / (prefix(conf.name) + "api") },
sources <<= (sourceDirectories, sourceFilter, defaultExcludes) map { (d,f,excl) => d.descendentsExcept(f,excl).getFiles },
2011-03-02 12:46:28 +01:00
scalaSource <<= sourceDirectory / "scala",
javaSource <<= sourceDirectory / "java",
resourceDirectory <<= sourceDirectory / "resources",
2011-03-14 02:34:17 +01:00
generatedResourceDirectory <<= target / "res_managed",
sourceDirectories <<= (scalaSource, javaSource) { _ :: _ :: Nil },
2011-03-14 02:34:17 +01:00
resourceDirectories <<= (resourceDirectory, generatedResourceDirectory) { _ :: _ :: Nil },
resources <<= (resourceDirectories, defaultExcludes, generatedResources, generatedResourceDirectory) map resourcesTask,
generatedResources <<= (definedSbtPlugins, generatedResourceDirectory) map writePluginsDescriptor
)
def addBaseSources = Seq(
sources <<= (sources, baseDirectory, sourceFilter, defaultExcludes) map { (srcs,b,f,excl) => (srcs +++ b * (f -- excl)).getFiles }
)
def compileBase = Seq(
2011-03-08 23:50:19 +01:00
classpathOptions in GlobalScope :== ClasspathOptions.auto,
compilers <<= (scalaInstance, appConfiguration, streams, classpathOptions, javaHome) map { (si, app, s, co, jh) => Compiler.compilers(si, co, jh)(app, s.log) },
2011-03-02 12:46:28 +01:00
javacOptions in GlobalScope :== Nil,
scalacOptions in GlobalScope :== Nil,
scalaInstance <<= scalaInstanceSetting,
2011-03-02 12:46:28 +01:00
scalaVersion <<= appConfiguration( _.provider.scalaProvider.version),
target <<= (target, scalaInstance, crossPaths)( (t,si,cross) => if(cross) t / ("scala-" + si.actualVersion) else t )
2011-02-06 03:40:10 +01:00
)
2011-02-26 00:35:52 +01:00
lazy val configTasks = Seq(
2011-03-02 12:46:28 +01:00
initialCommands in GlobalScope :== "",
compile <<= compileTask,
compileInputs <<= compileInputsTask,
console <<= consoleTask,
consoleQuick <<= consoleQuickTask,
discoveredMainClasses <<= compile map discoverMainClasses,
2011-03-14 02:34:17 +01:00
definedSbtPlugins <<= discoverPlugins,
2011-03-02 12:46:28 +01:00
inTask(run)(runnerSetting :: Nil).head,
selectMainClass <<= discoveredMainClasses map selectRunMain,
mainClass in run :== selectMainClass,
mainClass <<= discoveredMainClasses map selectPackageMain,
run <<= runTask(fullClasspath, mainClass in run, runner in run),
scaladocOptions <<= scalacOptions(identity),
doc <<= docTask,
copyResources <<= copyResourcesTask
)
2011-02-26 00:35:52 +01:00
lazy val projectTasks: Seq[Setting[_]] = Seq(
2011-03-02 12:46:28 +01:00
cleanFiles <<= (target, sourceManaged) { _ :: _ :: Nil },
clean <<= cleanFiles map IO.delete,
consoleProject <<= consoleProjectTask,
watchSources <<= watchSourcesTask,
watchTransitiveSources <<= watchTransitiveSourcesTask,
watch <<= watchSetting
2011-02-06 17:33:29 +01:00
)
2011-03-02 12:46:28 +01:00
def inAllConfigurations[T](key: ScopedTask[T]): Initialize[Task[Seq[T]]] = (EvaluateTask.state, thisProjectRef) flatMap { (state, ref) =>
2011-03-01 14:54:06 +01:00
val structure = Project structure state
val configurations = Project.getProject(ref, structure).toList.flatMap(_.configurations)
configurations.flatMap { conf =>
key in (ref, conf) get structure.data
} join
}
2011-03-02 12:46:28 +01:00
def watchTransitiveSourcesTask: Initialize[Task[Seq[File]]] =
(EvaluateTask.state, thisProjectRef) flatMap { (s, base) =>
inAllDependencies(base, watchSources.setting, Project structure s).join.map(_.flatten)
2011-03-01 14:54:06 +01:00
}
2011-03-02 12:46:28 +01:00
def watchSourcesTask: Initialize[Task[Seq[File]]] = Seq(sources, resources).map(inAllConfigurations).join { _.join.map(_.flatten.flatten) }
2011-03-01 14:54:06 +01:00
2011-03-02 12:46:28 +01:00
def watchSetting: Initialize[Watched] = (pollInterval, thisProjectRef) { (interval, base) =>
2011-03-01 14:54:06 +01:00
new Watched {
2011-03-02 12:46:28 +01:00
val scoped = watchTransitiveSources in base
2011-03-01 14:54:06 +01:00
val key = ScopedKey(scoped.scope, scoped.key)
override def pollInterval = interval
override def watchPaths(s: State) = EvaluateTask.evaluateTask(Project structure s, key, s, base) match { case Some(Value(ps)) => ps; case _ => Nil }
}
}
def scalaInstanceSetting = (appConfiguration, scalaVersion, scalaHome){ (app, version, home) =>
val launcher = app.provider.scalaProvider.launcher
home match {
case None => ScalaInstance(version, launcher)
case Some(h) => ScalaInstance(h, launcher)
}
}
2011-03-14 02:34:17 +01:00
def resourcesTask(dirs: Seq[File], excl: FileFilter, gen: Seq[File], genDir: File) = gen ++ (dirs --- genDir).descendentsExcept("*",excl).getFiles
2011-03-01 14:54:06 +01:00
2011-02-06 17:33:29 +01:00
lazy val testTasks = Seq(
2011-03-02 12:46:28 +01:00
testLoader <<= (fullClasspath, scalaInstance) map { (cp, si) => TestFramework.createTestLoader(data(cp), si) },
testFrameworks in GlobalScope :== {
2011-02-06 17:33:29 +01:00
import sbt.TestFrameworks._
Seq(ScalaCheck, Specs, ScalaTest, ScalaCheckCompat, ScalaTestCompat, SpecsCompat, JUnit)
},
2011-03-02 12:46:28 +01:00
loadedTestFrameworks <<= (testFrameworks, streams, testLoader) map { (frameworks, s, loader) =>
2011-02-06 17:33:29 +01:00
frameworks.flatMap(f => f.create(loader, s.log).map( x => (f,x)).toIterable).toMap
},
2011-03-02 12:46:28 +01:00
definedTests <<= (loadedTestFrameworks, compile, streams) map { (frameworkMap, analysis, s) =>
val tests = Tests.discover(frameworkMap.values.toSeq, analysis, s.log)._1
IO.writeLines(s.text(CompletionsID), tests.map(_.name).distinct)
tests
2011-02-06 17:33:29 +01:00
},
2011-03-02 12:46:28 +01:00
testListeners <<= (streams in test) map ( s => TestLogger(s.log) :: Nil ),
testOptions <<= testListeners map { listeners => Tests.Listeners(listeners) :: Nil },
2011-03-02 12:46:28 +01:00
executeTests <<= (streams in test, loadedTestFrameworks, testOptions, testLoader, definedTests) flatMap {
(s, frameworkMap, options, loader, discovered) => Tests(frameworkMap, loader, discovered, options, s.log)
2011-02-06 17:33:29 +01:00
},
test <<= (executeTests, streams) map { (results, s) => Tests.showResults(s.log, results) },
2011-03-02 12:46:28 +01:00
testOnly <<= testOnlyTask
2011-02-06 03:40:10 +01:00
)
2011-03-02 12:46:28 +01:00
def testOnlyTask =
InputTask(resolvedScoped(testOnlyParser)) ( result =>
2011-03-02 12:46:28 +01:00
(streams, loadedTestFrameworks, testOptions, testLoader, definedTests, result) flatMap {
2011-02-26 00:35:52 +01:00
case (s, frameworks, opts, loader, discovered, (tests, frameworkOptions)) =>
val modifiedOpts = Tests.Filter(if(tests.isEmpty) _ => true else tests.toSet ) +: Tests.Argument(frameworkOptions : _*) +: opts
Tests(frameworks, loader, discovered, modifiedOpts, s.log) map { results =>
Tests.showResults(s.log, results)
}
}
)
2011-03-14 02:34:17 +01:00
2011-02-10 14:16:07 +01:00
lazy val packageBase = Seq(
2011-03-02 12:46:28 +01:00
jarNameSetting,
packageOptions in GlobalScope :== Nil,
nameToString in GlobalScope :== (ArtifactName.show _)
2011-02-10 14:16:07 +01:00
)
lazy val packageConfig = Seq(
2011-03-02 12:46:28 +01:00
jarName <<= (jarName, configuration) { (n,c) => n.copy(config = c.name) },
packageOptions in packageBin <<= (packageOptions, mainClass in packageBin) map { _ ++ _.map(Package.MainClass.apply).toList }
2011-02-10 14:16:07 +01:00
) ++
2011-03-02 12:46:28 +01:00
packageTasks(packageBin, "", packageBinTask) ++
packageTasks(packageSrc, "src", packageSrcTask) ++
packageTasks(packageDoc, "doc", packageDocTask)
2011-02-10 14:16:07 +01:00
private[this] val allSubpaths = (dir: File) => (dir.*** --- dir) x relativeTo(dir)
2011-02-10 14:16:07 +01:00
def packageBinTask = classMappings
2011-03-02 12:46:28 +01:00
def packageDocTask = doc map allSubpaths
def packageSrcTask = concat(resourceMappings, sourceMappings)
private type Mappings = Initialize[Task[Seq[(File, String)]]]
def concat(as: Mappings, bs: Mappings) = (as zipWith bs)( (a,b) => (a :^: b :^: KNil) map { case a :+: b :+: HNil => a ++ b } )
def classMappings = (compileInputs, products) map { (in, _) => allSubpaths(in.config.classesDirectory) }
// drop base directories, since there are no valid mappings for these
2011-03-02 12:46:28 +01:00
def sourceMappings = (sources, sourceDirectories, baseDirectory) map { (srcs, sdirs, base) =>
( (srcs --- sdirs --- base) x (relativeTo(sdirs)|relativeTo(base))) toSeq
}
2011-03-02 12:46:28 +01:00
def resourceMappings = (resources, resourceDirectories) map { (rs, rdirs) =>
(rs --- rdirs) x relativeTo(rdirs) toSeq
2011-02-10 14:16:07 +01:00
}
2011-03-02 12:46:28 +01:00
def jarNameSetting = jarName <<= (moduleID, version, scalaVersion, crossPaths) { (n,v, sv, withCross) =>
ArtifactName(base = n, version = v, config = "", tpe = "", ext = "jar", cross = if(withCross) sv else "")
2011-02-10 14:16:07 +01:00
}
2011-03-02 12:46:28 +01:00
def jarPathSetting = jarPath <<= (target, jarName, nameToString) { (t, n, toString) => t / toString(n) }
2011-02-10 14:16:07 +01:00
2011-03-02 12:46:28 +01:00
def packageTasks(key: TaskKey[Package.Configuration], tpeString: String, mappingsTask: Initialize[Task[Seq[(File,String)]]]) =
2011-02-10 14:16:07 +01:00
inTask(key)( Seq(
key in ThisScope.copy(task = Global) <<= packageTask,
2011-03-02 12:46:28 +01:00
mappings <<= mappingsTask,
jarType :== tpeString,
jarName <<= (jarType,jarName){ (tpe, name) => (name.copy(tpe = tpe)) },
cacheDirectory <<= cacheDirectory / key.key.label,
jarPathSetting
2011-02-10 14:16:07 +01:00
))
2011-03-02 12:46:28 +01:00
def packageTask: Initialize[Task[Package.Configuration]] =
(jarPath, mappings, packageOptions, cacheDirectory, streams) map { (jar, srcs, options, cacheDir, s) =>
val config = new Package.Configuration(srcs, jar, options)
Package(config, cacheDir, s.log)
2011-02-10 14:16:07 +01:00
config
}
2011-02-06 03:40:10 +01:00
def selectRunMain(classes: Seq[String]): Option[String] =
sbt.SelectMainClass(Some(SimpleReader readLine _), classes)
def selectPackageMain(classes: Seq[String]): Option[String] =
sbt.SelectMainClass(None, classes)
2011-03-02 12:46:28 +01:00
def runTask(classpath: ScopedTask[Classpath], mainClassTask: ScopedTask[Option[String]], scalaRun: ScopedSetting[ScalaRun]): Initialize[InputTask[Unit]] =
InputTask(_ => complete.Parsers.spaceDelimited("<arg>")) { result =>
2011-03-02 12:46:28 +01:00
(classpath, mainClassTask, scalaRun, streams, result) map { (cp, main, runner, s, args) =>
val mainClass = main getOrElse error("No main class detected.")
runner.run(mainClass, data(cp), args, s.log) foreach error
2011-02-06 03:40:10 +01:00
}
}
2011-03-02 12:46:28 +01:00
def runnerSetting =
runner <<= (scalaInstance, baseDirectory, javaOptions, outputStrategy, fork, javaHome) { (si, base, options, strategy, forkRun, javaHomeDir) =>
if(forkRun) {
new ForkRun( ForkOptions(scalaJars = si.jars, javaHome = javaHomeDir, outputStrategy = strategy,
2011-02-26 00:35:52 +01:00
runJVMOptions = options, workingDirectory = Some(base)) )
} else
new Run(si)
}
def docTask: Initialize[Task[File]] =
2011-03-02 12:46:28 +01:00
(compileInputs, streams, docDirectory, configuration, scaladocOptions) map { (in, s, target, config, options) =>
2011-02-06 17:33:29 +01:00
val d = new Scaladoc(in.config.maxErrors, in.compilers.scalac)
d(nameForSrc(config.name), in.config.sources, in.config.classpath, target, options)(s.log)
2011-02-10 14:16:07 +01:00
target
2011-02-06 17:33:29 +01:00
}
2011-03-02 12:46:28 +01:00
def mainRunTask = run <<= runTask(fullClasspath in Configurations.Runtime, mainClass in run, runner in run)
2011-02-06 03:40:10 +01:00
2011-02-27 05:56:30 +01:00
def discoverMainClasses(analysis: inc.Analysis): Seq[String] =
Discovery.applications(Tests.allDefs(analysis)) collect { case (definition, discovered) if(discovered.hasMain) => definition.name }
2011-03-02 12:46:28 +01:00
def consoleProjectTask = (EvaluateTask.state, streams, initialCommands in consoleProject) map { (state, s, extra) => Console.sbt(state, extra)(s.log); println() }
2011-03-02 12:46:28 +01:00
def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console)
def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick)
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = (compilers, classpath, scalacOptions in task, initialCommands in task, streams) map {
(cs, cp, options, initCommands, s) =>
(new Console(cs.scalac))(data(cp), options, initCommands, s.log).foreach(msg => error(msg))
2011-02-06 03:40:10 +01:00
println()
}
def compileTask = (compileInputs, streams) map { (i,s) => Compiler(i,s.log) }
def compileInputsTask =
(dependencyClasspath, sources, compilers, javacOptions, scalacOptions, cacheDirectory, classDirectory, streams) map {
(cp, srcs, cs, javacOpts, scalacOpts, cacheDir, classes, s) =>
val classpath = classes +: data(cp)
val analysis = analysisMap(cp)
2011-03-02 12:46:28 +01:00
val cache = cacheDir / "compile"
Compiler.inputs(classpath, srcs, classes, scalacOpts, javacOpts, analysis, cache, 100)(cs, s.log)
}
2011-03-14 02:34:17 +01:00
def writePluginsDescriptor(plugins: Set[String], dir: File): List[File] =
{
val descriptor: File = dir / "sbt" / "sbt.plugins"
if(plugins.isEmpty)
{
IO.delete(descriptor)
Nil
}
else
{
2011-03-17 03:21:02 +01:00
IO.writeLines(descriptor, plugins.toSeq.sorted)
2011-03-14 02:34:17 +01:00
descriptor :: Nil
}
}
def discoverPlugins: Initialize[Task[Set[String]]] = (compile, sbtPlugin, streams) map { (analysis, isPlugin, s) => if(isPlugin) discoverSbtPlugins(analysis, s.log) else Set.empty }
def discoverSbtPlugins(analysis: inc.Analysis, log: Logger): Set[String] =
{
val pluginClass = classOf[Plugin].getName
val discovery = Discovery(Set(pluginClass), Set.empty)( Tests allDefs analysis )
discovery collect { case (df, disc) if (disc.baseClasses contains pluginClass) && disc.isModule => df.name } toSet;
}
2011-03-02 12:46:28 +01:00
def copyResourcesTask =
(classDirectory, cacheDirectory, resources, resourceDirectories, streams) map { (target, cache, resrcs, dirs, s) =>
2011-02-06 19:01:50 +01:00
val cacheFile = cache / "copy-resources"
2011-03-02 12:46:28 +01:00
val mappings = (resrcs --- dirs) x rebase(dirs, target)
2011-02-06 19:01:50 +01:00
s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t","\n\t",""))
Sync(cacheFile)( mappings )
mappings
}
def testOnlyParser(resolved: ScopedKey[_]): State => Parser[(Seq[String],Seq[String])] =
{ state =>
import DefaultParsers._
def distinctParser(exs: Set[String]): Parser[Seq[String]] =
2011-03-25 02:25:57 +01:00
token(Space ~> (NotSpace - "--").examples(exs) ).flatMap(ex => distinctParser(exs - ex).map(ex +: _)) ?? Nil
2011-03-02 12:46:28 +01:00
val tests = savedLines(state, resolved, definedTests)
val selectTests = distinctParser(tests.toSet) // todo: proper IDs
2011-03-25 02:25:57 +01:00
val options = (token(Space ~> "--") ~> spaceDelimited("<option>")) ?? Nil
selectTests ~ options
}
def savedLines(state: State, reader: ScopedKey[_], readFrom: Scoped): Seq[String] =
{
val structure = Project.structure(state)
structure.data.definingScope(reader.scope, readFrom.key) match {
case Some(defined) =>
val key = ScopedKey(Scope.fillTaskAxis(defined, readFrom.key), readFrom.key)
structure.streams.use(reader){ ts => IO.readLines(ts.readText(key, CompletionsID)) }
case None => Nil
}
}
2011-03-01 14:54:06 +01:00
def inAllDependencies[T](base: ProjectRef, key: ScopedSetting[T], structure: Load.BuildStructure): Seq[T] =
{
def deps(ref: ProjectRef): Seq[ProjectRef] =
Project.getProject(ref, structure).toList.flatMap { p =>
p.dependencies.map(_.project) ++ p.aggregate
}
inAllDeps(base, deps, key, structure.data)
}
def inAllDeps[T](base: ProjectRef, deps: ProjectRef => Seq[ProjectRef], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
inAllProjects(Dag.topologicalSort(base)(deps), key, data)
def inAllProjects[T](allProjects: Seq[Reference], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
2011-03-01 14:54:06 +01:00
allProjects.flatMap { p => key in p get data }
val CompletionsID = "completions"
2011-02-06 19:01:50 +01:00
def noAggregation = Seq(run, console, consoleQuick, consoleProject)
lazy val disableAggregation = noAggregation map disableAggregate
def disableAggregate(k: Scoped) =
2011-03-02 12:46:28 +01:00
aggregate in Scope.GlobalScope.copy(task = Select(k.key)) :== false
2011-02-26 00:35:52 +01:00
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
2011-02-06 03:40:10 +01:00
2011-02-26 00:35:52 +01:00
lazy val baseClasspaths = Classpaths.publishSettings ++ Classpaths.baseSettings
lazy val configSettings = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig
lazy val compileSettings = configSettings ++ (mainRunTask +: addBaseSources)
lazy val testSettings = configSettings ++ testTasks
2011-02-26 00:35:52 +01:00
lazy val itSettings = inConfig(Configurations.IntegrationTest)(testSettings)
lazy val defaultConfigs = inConfig(CompileConf)(compileSettings) ++ inConfig(TestConf)(testSettings)
2011-02-27 22:28:00 +01:00
lazy val defaultSettings: Seq[Setting[_]] = projectCore ++ paths ++ baseClasspaths ++ baseTasks ++ compileBase ++ defaultConfigs ++ disableAggregation
}
object Classpaths
{
import Path._
import GlobFilter._
import Keys._
import Scope.ThisScope
import Defaults._
import Attributed.{blank, blankSeq}
def concat[T](a: ScopedTaskable[Seq[T]], b: ScopedTaskable[Seq[T]]): Initialize[Task[Seq[T]]] = (a,b) map (_ ++ _)
2011-02-27 05:56:30 +01:00
lazy val configSettings: Seq[Setting[_]] = Seq(
2011-03-02 12:46:28 +01:00
externalDependencyClasspath <<= concat(unmanagedClasspath, managedClasspath),
dependencyClasspath <<= concat(internalDependencyClasspath, externalDependencyClasspath),
fullClasspath <<= concat(products, dependencyClasspath),
internalDependencyClasspath <<= internalDependencies,
unmanagedClasspath <<= unmanagedDependencies,
products <<= makeProducts,
managedClasspath <<= (configuration, classpathTypes, update) map managedJars,
2011-03-02 12:46:28 +01:00
unmanagedJars <<= (configuration, unmanagedBase, classpathFilter, defaultExcludes) map { (config, base, filter, excl) =>
(base * (filter -- excl) +++ (base / config.name).descendentsExcept(filter, excl)).getFiles
}
)
2011-02-10 14:16:07 +01:00
def defaultPackageTasks: Seq[ScopedTask[_]] =
2011-03-02 12:46:28 +01:00
for(task <- Seq(packageBin, `packageSrc`, `packageDoc`); conf <- Seq(CompileConf, TestConf)) yield (task in conf)
2011-02-27 05:56:30 +01:00
val publishSettings: Seq[Setting[_]] = Seq(
2011-03-02 12:46:28 +01:00
publishMavenStyle in GlobalScope :== true,
packageToPublish <<= defaultPackageTasks.dependOn,
deliverDepends <<= (publishMavenStyle, makePom.setting, packageToPublish.setting) { (mavenStyle, mkpom, ptp) =>
if(mavenStyle) mkpom.map(_ => ()) else ptp
},
2011-03-22 23:19:48 +01:00
makePom <<= (ivyModule, makePomConfiguration, packageToPublish, streams) map { (module, config, _, s) => IvyActions.makePom(module, config, s.log); config.file },
2011-03-02 12:46:28 +01:00
deliver <<= deliverTask(publishConfiguration),
deliverLocal <<= deliverTask(publishLocalConfiguration),
publish <<= publishTask(publishConfiguration, deliver),
publishLocal <<= publishTask(publishLocalConfiguration, deliverLocal)
)
2011-02-27 05:56:30 +01:00
val baseSettings: Seq[Setting[_]] = Seq(
2011-03-02 12:46:28 +01:00
unmanagedBase <<= baseDirectory / "lib",
normalizedName <<= name(StringUtilities.normalize),
organization :== normalizedName,
classpathFilter in GlobalScope :== "*.jar",
fullResolvers <<= (projectResolver,resolvers,sbtPlugin,sbtResolver) map { (pr,rs,isPlugin,sr) =>
val base = pr +: Resolver.withDefaultResolvers(rs)
if(isPlugin) sr +: base else base
},
2011-03-02 12:46:28 +01:00
offline in GlobalScope :== false,
moduleID :== normalizedName,
defaultConfiguration in GlobalScope :== Some(Configurations.Compile),
defaultConfigurationMapping in GlobalScope <<= defaultConfiguration{ case Some(d) => "*->" + d.name; case None => "*->*" },
ivyPaths <<= (baseDirectory, appConfiguration) { (base, app) => new IvyPaths(base, Option(app.provider.scalaProvider.launcher.ivyHome)) },
2011-03-02 12:46:28 +01:00
otherResolvers in GlobalScope :== Nil,
projectResolver <<= projectResolverTask,
projectDependencies <<= projectDependenciesTask,
libraryDependencies in GlobalScope :== Nil,
allDependencies <<= (projectDependencies,libraryDependencies,sbtPlugin,sbtDependency) map { (projDeps, libDeps, isPlugin, sbtDep) =>
val base = projDeps ++ libDeps
if(isPlugin) sbtDep +: base else base
},
2011-03-02 12:46:28 +01:00
ivyLoggingLevel in GlobalScope :== UpdateLogging.Quiet,
ivyXML in GlobalScope :== NodeSeq.Empty,
ivyValidate in GlobalScope :== false,
ivyScala in GlobalScope <<= scalaVersion(v => Some(new IvyScala(v, Nil, false, false))),
moduleConfigurations in GlobalScope :== Nil,
publishTo in GlobalScope :== None,
pomName <<= (moduleID, version, scalaVersion, crossPaths) { (n,v,sv, withCross) =>
ArtifactName(base = n, version = v, config = "", tpe = "", ext = "pom", cross = if(withCross) sv else "")
},
2011-03-02 12:46:28 +01:00
pomFile <<= (target, pomName, nameToString) { (t, n, toString) => t / toString(n) },
pomArtifact <<= (publishMavenStyle, moduleID)( (mavenStyle, name) => if(mavenStyle) Artifact(name, "pom", "pom") :: Nil else Nil),
artifacts <<= (pomArtifact,moduleID)( (pom,name) => Artifact(name) +: pom),
projectID <<= (organization,moduleID,version,artifacts){ (org,module,version,as) => ModuleID(org, module, version).cross(true).artifacts(as : _*) },
resolvers in GlobalScope :== Nil,
2011-03-02 12:46:28 +01:00
projectDescriptors <<= depMap,
retrievePattern :== "[type]/[organisation]/[module]/[artifact](-[revision])(-[classifier]).[ext]",
updateConfiguration <<= (retrieveConfiguration, ivyLoggingLevel)((conf,level) => new UpdateConfiguration(conf, false, level) ),
retrieveConfiguration <<= (managedDirectory, retrievePattern, retrieveManaged) { (libm, pattern, enabled) => if(enabled) Some(new RetrieveConfiguration(libm, pattern)) else None },
ivyConfiguration <<= (fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, appConfiguration) map { (rs, paths, other, moduleConfs, off, app) =>
// todo: pass logger from streams directly to IvyActions
val lock = app.provider.scalaProvider.launcher.globalLock
2011-03-02 12:46:28 +01:00
new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Some(lock), ConsoleLogger())
},
2011-03-02 12:46:28 +01:00
moduleSettings <<= (projectID, allDependencies, ivyXML, thisProject, defaultConfiguration, ivyScala, ivyValidate) map {
(pid, deps, ivyXML, project, defaultConf, ivyS, validate) => new InlineConfiguration(pid, deps, ivyXML, project.configurations, defaultConf, ivyS, validate)
},
2011-03-02 12:46:28 +01:00
makePomConfiguration <<= pomFile(file => makePomConfigurationTask(file)),
publishConfiguration <<= (target, publishTo, ivyLoggingLevel) map { (outputDirectory, publishTo, level) => publishConfig( publishPatterns(outputDirectory), resolverName = getPublishTo(publishTo).name, logging = level) },
publishLocalConfiguration <<= (target, ivyLoggingLevel) map { (outputDirectory, level) => publishConfig( publishPatterns(outputDirectory, true), logging = level ) },
ivySbt <<= ivyConfiguration map { conf => new IvySbt(conf) },
ivyModule <<= (ivySbt, moduleSettings) map { (ivySbt, settings) => new ivySbt.Module(settings) },
update <<= (ivyModule, updateConfiguration, cacheDirectory, streams) map { (module, config, cacheDirectory, s) =>
cachedUpdate(cacheDirectory / "update", module, config, s.log)
},
transitiveClassifiers :== Seq("sources", "javadoc"),
2011-03-22 23:19:48 +01:00
updateClassifiers <<= (ivySbt, projectID, update, transitiveClassifiers, updateConfiguration, ivyScala, streams) map { (is, pid, up, classifiers, c, ivyScala, s) =>
IvyActions.transitive(is, pid, up, classifiers, c, ivyScala, s.log)
},
updateSbtClassifiers <<= (ivySbt, projectID, transitiveClassifiers, updateConfiguration, sbtDependency, ivyScala, streams) map { (is, pid, classifiers, c, sbtDep, ivyScala, s) =>
IvyActions.transitiveScratch(is, pid, "sbt", sbtDep :: Nil, classifiers, c, ivyScala, s.log)
},
sbtResolver in GlobalScope :== dbResolver,
sbtDependency in GlobalScope <<= appConfiguration { app =>
val id = app.provider.id
ModuleID(id.groupID, id.name, id.version, crossVersion = id.crossVersioned)
}
)
2011-03-02 12:46:28 +01:00
def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[Unit]] =
2011-03-22 23:19:48 +01:00
(ivyModule, config, deliverDepends, streams) map { (module, config, _, s) => IvyActions.deliver(module, config, s.log) }
2011-03-02 12:46:28 +01:00
def publishTask(config: TaskKey[PublishConfiguration], deliverKey: TaskKey[_]): Initialize[Task[Unit]] =
2011-03-22 23:19:48 +01:00
(ivyModule, config, deliverKey, streams) map { (module, config, _, s) => IvyActions.publish(module, config, s.log) }
import Cache._
import CacheIvy.{classpathFormat, publishIC, updateIC, updateReportF}
def cachedUpdate(cacheFile: File, module: IvySbt#Module, config: UpdateConfiguration, log: Logger): UpdateReport =
{
implicit val updateCache = updateIC
implicit val updateReport = updateReportF
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
def work = (_: In) match { case conf :+: settings :+: config :+: HNil =>
log.info("Updating...")
2011-03-22 23:19:48 +01:00
val r = IvyActions.update(module, config, log)
log.info("Done updating.")
r
}
val f =
Tracked.inputChanged(cacheFile / "inputs") { (inChanged: Boolean, in: In) =>
val outCache = Tracked.lastOutput[In, UpdateReport](cacheFile / "output") {
case (_, Some(out)) if !inChanged && allFiles(out).forall(_.exists) => out
case _ => work(in)
}
outCache(in)
}
f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)
}
/*
// can't cache deliver/publish easily since files involved are hidden behind patterns. publish will be difficult to verify target-side anyway
def cachedPublish(cacheFile: File)(g: (IvySbt#Module, PublishConfiguration) => Unit, module: IvySbt#Module, config: PublishConfiguration) => Unit =
{ case module :+: config :+: HNil =>
/* implicit val publishCache = publishIC
val f = cached(cacheFile) { (conf: IvyConfiguration, settings: ModuleSettings, config: PublishConfiguration) =>*/
g(module, config)
/*}
f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)*/
}*/
2011-03-02 12:46:28 +01:00
def makePomConfigurationTask(file: File, configurations: Option[Iterable[Configuration]] = None, extra: NodeSeq = NodeSeq.Empty, process: XNode => XNode = n => n, filterRepositories: MavenRepository => Boolean = _ => true) =
new MakePomConfiguration(file, configurations, extra, process, filterRepositories)
def getPublishTo(repo: Option[Resolver]): Resolver = repo getOrElse error("Repository for publishing is not specified.")
2011-03-02 12:46:28 +01:00
def publishConfig(patterns: PublishPatterns, resolverName: String = "local", status: String = "release", logging: UpdateLogging.Value = UpdateLogging.DownloadOnly) =
new PublishConfiguration(patterns, status, resolverName, None, logging)
def publishPatterns(outputPath: Path, publishIvy: Boolean = false): PublishPatterns =
{
val deliverPattern = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath
val srcArtifactPatterns: Seq[String] =
{
val pathPatterns =
(outputPath / "[artifact]-[revision]-[type](-[classifier]).[ext]") ::
(outputPath / "[artifact]-[revision](-[classifier]).[ext]") ::
Nil
pathPatterns.map(_.absolutePath)
}
new PublishPatterns( if(publishIvy) Some(deliverPattern) else None, srcArtifactPatterns)
}
2011-03-02 12:46:28 +01:00
def projectDependenciesTask =
(thisProject, settings) map { (p, data) =>
p.dependencies flatMap { dep => (projectID in dep.project) get data map { _.copy(configurations = dep.configuration) } }
}
def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
2011-03-22 23:19:48 +01:00
(thisProject, thisProjectRef, settings, streams) flatMap { (root, rootRef, data, s) =>
val dependencies = (p: (ProjectRef, ResolvedProject)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) })
2011-03-22 23:19:48 +01:00
depMap(Dag.topologicalSort((rootRef,root))(dependencies).dropRight(1), data, s.log)
}
2011-03-22 23:19:48 +01:00
def depMap(projects: Seq[(ProjectRef,ResolvedProject)], data: Settings[Scope], log: Logger): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
2011-03-02 12:46:28 +01:00
projects.flatMap { case (p,_) => ivyModule in p get data }.join.map { mods =>
(mods.map{ mod =>
2011-03-22 23:19:48 +01:00
val md = mod.moduleDescriptor(log)
(md.getModuleRevisionId, md)
}).toMap
}
2011-03-02 12:46:28 +01:00
def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors map { m =>
new RawRepository(new ProjectResolver("inter-project", m))
}
2011-03-02 12:46:28 +01:00
def analyzed[T](data: T, analysis: inc.Analysis) = Attributed.blank(data).put(Keys.analysis, analysis)
def makeProducts: Initialize[Task[Classpath]] =
2011-03-02 12:46:28 +01:00
(compile, compileInputs, copyResources) map { (analysis, i, _) => analyzed(i.config.classesDirectory, analysis) :: Nil }
def internalDependencies: Initialize[Task[Classpath]] =
2011-03-02 12:46:28 +01:00
(thisProjectRef, thisProject, configuration, settings) flatMap internalDependencies0
def unmanagedDependencies: Initialize[Task[Classpath]] =
2011-03-02 12:46:28 +01:00
(thisProjectRef, thisProject, configuration, settings) flatMap unmanagedDependencies0
import java.util.LinkedHashSet
import collection.JavaConversions.asScalaSet
def interSort(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Seq[(ProjectRef,String)] =
{
val visited = asScalaSet(new LinkedHashSet[(ProjectRef,String)])
def visit(p: ProjectRef, project: ResolvedProject, c: Configuration)
{
val applicableConfigs = allConfigs(c)
for(ac <- applicableConfigs) // add all configurations in this project
visited add (p, ac.name)
val masterConfs = configurationNames(project)
for( Project.ResolvedClasspathDependency(dep, confMapping) <- project.dependencies)
{
2011-03-02 12:46:28 +01:00
val depProject = thisProject in dep get data getOrElse error("Invalid project: " + dep)
val mapping = mapped(confMapping, masterConfs, configurationNames(depProject), "compile", "*->compile")
// map master configuration 'c' and all extended configurations to the appropriate dependency configuration
for(ac <- applicableConfigs; depConfName <- mapping(ac.name))
{
2011-03-02 12:46:28 +01:00
val depConf = getConfiguration(dep, depProject, depConfName)
if( ! visited( (dep, depConfName) ) )
visit(dep, depProject, depConf)
}
}
}
visit(projectRef, project, conf)
visited.toSeq
}
def unmanagedDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
interDependencies(projectRef, project, conf, data, true, unmanagedLibs)
def internalDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
2011-03-02 12:46:28 +01:00
interDependencies(projectRef, project, conf, data, false, productsTask)
def interDependencies(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope], includeSelf: Boolean,
f: (ProjectRef, String, Settings[Scope]) => Task[Classpath]): Task[Classpath] =
{
val visited = interSort(projectRef, project, conf, data)
val tasks = asScalaSet(new LinkedHashSet[Task[Classpath]])
for( (dep, c) <- visited )
if(includeSelf || (dep != projectRef) || conf.name != c )
tasks += f(dep, c, data)
(tasks.toSeq.join).map(_.flatten.distinct)
}
def mapped(confString: Option[String], masterConfs: Seq[String], depConfs: Seq[String], default: String, defaultMapping: String): String => Seq[String] =
{
lazy val defaultMap = parseMapping(defaultMapping, masterConfs, depConfs, _ :: Nil)
parseMapping(confString getOrElse default, masterConfs, depConfs, defaultMap)
}
def parseMapping(confString: String, masterConfs: Seq[String], depConfs: Seq[String], default: String => Seq[String]): String => Seq[String] =
union(confString.split(";") map parseSingleMapping(masterConfs, depConfs, default))
def parseSingleMapping( masterConfs: Seq[String], depConfs: Seq[String], default: String => Seq[String])(confString: String): String => Seq[String] =
{
val ms: Seq[(String,Seq[String])] =
trim(confString.split("->",2)) match {
case x :: Nil => for(a <- parseList(x, masterConfs)) yield (a,default(a))
case x :: y :: Nil => val target = parseList(y, depConfs); for(a <- parseList(x, masterConfs)) yield (a,target)
case _ => error("Invalid configuration '" + confString + "'") // shouldn't get here
}
val m = ms.toMap
s => m.getOrElse(s, Nil)
}
def union[A,B](maps: Seq[A => Seq[B]]): A => Seq[B] =
a => (Seq[B]() /: maps) { _ ++ _(a) } distinct;
def configurationNames(p: ResolvedProject): Seq[String] = p.configurations.map( _.name)
def parseList(s: String, allConfs: Seq[String]): Seq[String] = (trim(s split ",") flatMap replaceWildcard(allConfs)).distinct
def replaceWildcard(allConfs: Seq[String])(conf: String): Seq[String] =
if(conf == "") Nil else if(conf == "*") allConfs else conf :: Nil
private def trim(a: Array[String]): List[String] = a.toList.map(_.trim)
def missingConfiguration(in: String, conf: String) =
error("Configuration '" + conf + "' not defined in '" + in + "'")
def allConfigs(conf: Configuration): Seq[Configuration] =
Dag.topologicalSort(conf)(_.extendsConfigs)
def getConfiguration(ref: ProjectRef, dep: ResolvedProject, conf: String): Configuration =
dep.configurations.find(_.name == conf) getOrElse missingConfiguration(Project display ref, conf)
def productsTask(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
2011-03-02 12:46:28 +01:00
getClasspath(products, dep, conf, data)
def unmanagedLibs(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
2011-03-02 12:46:28 +01:00
getClasspath(unmanagedJars, dep, conf, data)
def getClasspath(key: TaskKey[Classpath], dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
( key in (dep, ConfigKey(conf)) ) get data getOrElse const(Nil)
def defaultConfigurationTask(p: ResolvedReference, data: Settings[Scope]): Configuration =
2011-03-02 12:46:28 +01:00
flatten(defaultConfiguration in p get data) getOrElse Configurations.Default
def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap identity
def managedJars(config: Configuration, jarTypes: Set[String], up: UpdateReport): Classpath = managedFiles(config, up)(isJar(jarTypes))
def allFiles(up: UpdateReport): Seq[File] = data( up.configurations flatMap { cr => allJars(cr)(_ => true) }).distinct
def managedFiles(config: Configuration, up: UpdateReport)(pred: Artifact => Boolean): Classpath =
allJars( confReport(config.name, up) )(pred)
def confReport(config: String, up: UpdateReport): ConfigurationReport =
2011-03-17 03:21:02 +01:00
up.configuration(config) getOrElse error("Configuration '" + config + "' unresolved by 'update'.")
def allJars(cr: ConfigurationReport)(pred: Artifact => Boolean): Seq[File] = cr.modules.flatMap(mr => allJars(mr.artifacts)(pred))
def allJars(as: Seq[(Artifact,File)])(pred: Artifact => Boolean): Seq[File] = as collect { case (a, f) if pred(a) => f }
def isJar(jarTypes: Set[String])(a: Artifact): Boolean =
jarTypes contains a.`type`
lazy val dbResolver = Resolver.url("sbt-db", new URL("http://databinder.net/repo/"))(Resolver.ivyStylePatterns)
}