sbt/main/Default.scala

637 lines
32 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 java.io.File
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}
import EvaluateTask.{resolvedScoped, streams}
import complete._
import std.TaskExtra._
import scala.xml.{Node => XNode,NodeSeq}
import org.apache.ivy.core.module.{descriptor, id}
import descriptor.ModuleDescriptor, id.ModuleRevisionId
import Types._
object Default
{
import Path._
import GlobFilter._
import Keys._
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.toSeq
}
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
2011-02-27 22:28:00 +01:00
def buildCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
2011-03-02 12:46:28 +01:00
pollInterval :== 1,
javaHome :== None,
outputStrategy :== None,
fork :== false,
javaOptions :== Nil,
crossPaths :== true,
shellPrompt :== (_ => "> "),
aggregate :== Aggregation.Enabled,
maxErrors :== 100,
commands :== Nil,
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.toSeq },
scalaSource <<= sourceDirectory / "scala",
javaSource <<= sourceDirectory / "java",
javaSourceRoots <<= toSeq(javaSource),
resourceDirectory <<= sourceDirectory / "resources",
sourceDirectories <<= (scalaSource, javaSourceRoots) { _ +: _ },
resourceDirectories <<= toSeq(resourceDirectory),
resources <<= (resourceDirectories, defaultExcludes) map { (dirs, excl) => dirs.descendentsExcept("*",excl).getFiles.toSeq }
)
def addBaseSources = Seq(
2011-03-02 12:46:28 +01:00
sources <<= (sources, baseDirectory, sourceFilter, defaultExcludes) map { (srcs,b,f,excl) => (srcs +++ b * (f -- excl)).getFiles.toSeq }
)
def webPaths = Seq(
2011-03-02 12:46:28 +01:00
webappDir <<= sourceDirectory / "webapp"
)
def compileBase = Seq(
2011-03-02 12:46:28 +01:00
compilers <<= (scalaInstance, appConfiguration, streams) map { (si, app, s) => Compile.compilers(si)(app, s.log) },
javacOptions in GlobalScope :== Nil,
scalacOptions in GlobalScope :== Nil,
scalaInstance <<= (appConfiguration, scalaVersion){ (app, version) => ScalaInstance(version, app.provider.scalaProvider.launcher) },
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,
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 }
}
}
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) =>
2011-02-26 00:35:52 +01:00
val tests = Test.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 => Test.Listeners(listeners) :: Nil },
executeTests <<= (streams in test, loadedTestFrameworks, testOptions, testLoader, definedTests) flatMap {
2011-02-26 00:35:52 +01:00
(s, frameworkMap, options, loader, discovered) => Test(frameworkMap, loader, discovered, options, s.log)
2011-02-06 17:33:29 +01:00
},
2011-03-02 12:46:28 +01:00
test <<= (executeTests, streams) map { (results, s) => Test.showResults(s.log, results) },
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 = Test.Filter(if(tests.isEmpty) _ => true else tests.toSet ) +: Test.Argument(frameworkOptions : _*) +: opts
Test(frameworks, loader, discovered, modifiedOpts, s.log) map { results =>
Test.showResults(s.log, results)
}
}
)
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 = (_: File).###.***.xx.toSeq
2011-03-02 12:46:28 +01:00
def packageBinTask = concat(classMappings, resourceMappings)
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 } )
2011-03-02 12:46:28 +01:00
def classMappings = (compileInputs, compile) 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] =
2011-03-02 12:46:28 +01:00
Discovery.applications(Test.allDefs(analysis)) collect { case (definition, discovered) if(discovered.hasMain) => definition.name }
def consoleProjectTask = (EvaluateTask.state, streams, initialCommands in consoleProject) map { (state, s, extra) => Console.sbt(state, extra)(s.log) }
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()
}
2011-03-02 12:46:28 +01:00
def compileTask = (compileInputs, streams) map { (i,s) => Compile(i,s.log) }
def compileInputsTask =
2011-03-02 12:46:28 +01:00
(dependencyClasspath, sources, javaSourceRoots, compilers, javacOptions, scalacOptions, cacheDirectory, classDirectory, streams) map {
(cp, srcs, javaRoots, 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"
Compile.inputs(classpath, srcs, classes, scalacOpts, javacOpts, javaRoots, analysis, cache, 100)(cs, s.log)
}
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]] =
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
val options = (Space ~> "--" ~> spaceDelimited("<arg>")) ?? 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[ProjectRef], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
allProjects.flatMap { p => key in p get data }
val CompletionsID = "completions"
2011-02-06 19:01:50 +01:00
2011-02-26 00:35:52 +01:00
lazy val defaultWebPaths = inConfig(CompileConf)(webPaths)
2011-03-02 12:46:28 +01:00
def noAggregation = Seq(run, console, consoleQuick)
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
lazy val defaultWebTasks = Nil
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
lazy val defaultWebSettings = defaultSettings ++ defaultWebPaths ++ defaultWebTasks
}
object Classpaths
{
import Path._
import GlobFilter._
import Keys._
import Scope.ThisScope
import Default._
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, update) map { (config, up) => up.getOrElse(config.name, error("Configuration '" + config.name + "' unresolved by 'update'.")) },
unmanagedJars <<= (configuration, unmanagedBase, classpathFilter, defaultExcludes) map { (config, base, filter, excl) =>
(base * (filter -- excl) +++ (base / config.name).descendentsExcept(filter, excl)).getFiles.toSeq
}
)
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-02 12:46:28 +01:00
makePom <<= (ivyModule, makePomConfiguration, packageToPublish) map { (module, config, _) => IvyActions.makePom(module, config); config.file },
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",
resolvers <<= (projectResolver,baseResolvers).map( (pr,rs) => pr +: Resolver.withDefaultResolvers(rs)),
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(base => new IvyPaths(base, None)),
otherResolvers in GlobalScope :== Nil,
projectResolver <<= projectResolverTask,
projectDependencies <<= projectDependenciesTask,
libraryDependencies in GlobalScope :== Nil,
allDependencies <<= concat(projectDependencies,libraryDependencies),
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 : _*) },
baseResolvers in GlobalScope :== Nil,
projectDescriptors <<= depMap,
retrievePattern :== "[type]/[organisation]/[module]/[artifact](-[revision])(-[classifier]).[ext]",
updateConfiguration <<= (retrieveConfiguration, ivyLoggingLevel)((conf,level) => new UpdateConfiguration(conf, level) ),
retrieveConfiguration :== None, //Some(new RetrieveConfiguration(managedDependencyPath asFile, retrievePattern, true))
ivyConfiguration <<= (resolvers, 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)
}
)
2011-03-02 12:46:28 +01:00
def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[Unit]] =
(ivyModule, config, deliverDepends) map { (module, config, _) => IvyActions.deliver(module, config) }
def publishTask(config: TaskKey[PublishConfiguration], deliverKey: TaskKey[_]): Initialize[Task[Unit]] =
(ivyModule, config, deliverKey) map { (module, config, _) => IvyActions.publish(module, config) }
import Cache._
import CacheIvy.{classpathFormat, publishIC, updateIC}
def cachedUpdate(cacheFile: File, module: IvySbt#Module, config: UpdateConfiguration, log: Logger): Map[String, Seq[File]] =
{
implicit val updateCache = updateIC
val work = (_: IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil) match { case conf :+: settings :+: config :+: HNil =>
log.info("Updating...")
val r = IvyActions.update(module, config)
log.info("Done updating.")
r
}
val f = cached(cacheFile)(work)
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-02 12:46:28 +01:00
(thisProject, thisProjectRef, settings) flatMap { (root, rootRef, data) =>
val dependencies = (p: (ProjectRef, Project)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) })
depMap(Dag.topologicalSort((rootRef,root))(dependencies).dropRight(1), data)
}
def depMap(projects: Seq[(ProjectRef,Project)], data: Settings[Scope]): 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 =>
val md = mod.moduleDescriptor
(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: Project, conf: Configuration, data: Settings[Scope]): Seq[(ProjectRef,String)] =
{
val visited = asScalaSet(new LinkedHashSet[(ProjectRef,String)])
def visit(p: ProjectRef, project: Project, 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.ClasspathDependency(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: Project, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
interDependencies(projectRef, project, conf, data, true, unmanagedLibs)
def internalDependencies0(projectRef: ProjectRef, project: Project, 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: Project, 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: Project): 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)
2011-03-02 12:46:28 +01:00
def getConfiguration(ref: ProjectRef, dep: Project, conf: String): Configuration =
dep.configurations.find(_.name == conf) getOrElse missingConfiguration(Project display ref, conf)
2011-03-02 12:46:28 +01:00
def productsTask(dep: ProjectRef, conf: String, data: Settings[Scope]): Task[Classpath] =
getClasspath(products, dep, conf, data)
def unmanagedLibs(dep: ProjectRef, 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: ProjectRef, conf: String, data: Settings[Scope]): Task[Classpath] =
( key in (dep, ConfigKey(conf)) ) get data getOrElse const(Nil)
2011-03-02 12:46:28 +01:00
def defaultConfigurationTask(p: ProjectRef, data: Settings[Scope]): Configuration =
flatten(defaultConfiguration in p get data) getOrElse Configurations.Default
def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap identity
}