/* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ package sbt import Build.data import Scope.{fillTaskAxis, GlobalScope, ThisScope} import xsbt.api.Discovery import Project.{inConfig, Initialize, inScope, inTask, ScopedKey, Setting, SettingsDefinition} 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} import org.scalatools.testing.{AnnotatedFingerprint, SubclassFingerprint} import sys.error import scala.xml.NodeSeq import org.apache.ivy.core.module.{descriptor, id} import descriptor.ModuleDescriptor, id.ModuleRevisionId import java.io.File import java.net.URL import java.util.concurrent.Callable import sbinary.DefaultProtocol.StringFormat import Cache.seqFormat import Types._ import Path._ import Keys._ object Defaults extends BuildCommon { final val CacheDirectoryName = "cache" def configSrcSub(key: SettingKey[File]): Initialize[File] = (key in ThisScope.copy(config = Global), 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 lock(app: xsbti.AppConfiguration): xsbti.GlobalLock = app.provider.scalaProvider.launcher.globalLock def extractAnalysis[T](a: Attributed[T]): (T, inc.Analysis) = (a.data, a.metadata get Keys.analysis getOrElse inc.Analysis.Empty) def analysisMap[T](cp: Seq[Attributed[T]]): Map[T, inc.Analysis] = (for(a <- cp; an <- a.metadata get Keys.analysis) yield (a.data, an) ).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( 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 apply binarySbtVersion, sbtResolver <<= sbtVersion { sbtV => if(sbtV endsWith "-SNAPSHOT") Classpaths.typesafeSnapshots else Classpaths.typesafeResolver }, pollInterval :== 500, logBuffered :== false, connectInput :== false, cancelable :== false, cancelable :== false, autoScalaLibrary :== true, onLoad <<= onLoad ?? idFun[State], tags in test := Seq(Tags.Test -> 1), tags in testOnly <<= tags in test, onUnload <<= (onUnload ?? idFun[State]), onUnload <<= (onUnload, taskTemporaryDirectory) { (f, dir) => s => { try f(s) finally IO.delete(dir) } }, watchingMessage <<= watchingMessage ?? Watched.defaultWatchingMessage, triggeredMessage <<= triggeredMessage ?? Watched.defaultTriggeredMessage, definesClass :== FileValueCache(Locate.definesClass _ ).get, trapExit :== false, trapExit in run :== true, traceLevel in run :== 0, traceLevel in runMain :== 0, logBuffered in testOnly :== true, logBuffered in test :== true, traceLevel in console :== Int.MaxValue, traceLevel in consoleProject :== Int.MaxValue, autoCompilerPlugins :== true, internalConfigurationMap :== Configurations.internalMap _, initialize :== (), credentials :== Nil, scalaHome :== None, javaHome :== None, extraLoggers :== { _ => Nil }, skip :== false, watchSources :== Nil, version :== "0.1-SNAPSHOT", outputStrategy :== None, exportJars :== false, fork :== false, javaOptions :== Nil, sbtPlugin :== false, crossPaths :== true, classpathTypes :== Set("jar", "bundle"), aggregate :== true, maxErrors :== 100, showTiming :== true, timingFormat :== Aggregation.defaultFormat, showSuccess :== true, commands :== Nil, retrieveManaged :== false, buildStructure <<= state map Project.structure, settings <<= buildStructure map ( _.data ), artifactClassifier :== None, artifactClassifier in packageSrc :== Some(SourceClassifier), artifactClassifier in packageDoc :== Some(DocClassifier), checksums <<= appConfiguration(Classpaths.bootChecksums), pomExtra :== NodeSeq.Empty, pomPostProcess :== idFun, pomAllRepositories :== false, includeFilter :== NothingFilter, includeFilter in unmanagedSources :== "*.java" | "*.scala", includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll", includeFilter in unmanagedResources :== AllPassFilter, excludeFilter :== (".*" - ".") || HiddenFileFilter, pomIncludeRepository :== Classpaths.defaultRepositoryFilter )) def projectCore: Seq[Setting[_]] = Seq( name <<= thisProject(_.id), logManager <<= extraLoggers(LogManager.defaults), onLoadMessage <<= onLoadMessage or (name, thisProjectRef)("Set current project to " + _ + " (in build " + _.build +")"), runnerTask ) def paths = Seq( baseDirectory <<= thisProject(_.base), target <<= baseDirectory / "target", historyPath <<= target(t => Some(t / ".history")), sourceDirectory <<= baseDirectory / "src", sourceManaged <<= crossTarget / "src_managed", resourceManaged <<= crossTarget / "resource_managed", cacheDirectory <<= (crossTarget, thisProject)(_ / CacheDirectoryName / _.id / "global") ) lazy val configPaths = sourceConfigPaths ++ resourceConfigPaths ++ outputConfigPaths lazy val sourceConfigPaths = Seq( sourceDirectory <<= configSrcSub(sourceDirectory), sourceManaged <<= configSrcSub(sourceManaged), scalaSource <<= sourceDirectory / "scala", javaSource <<= sourceDirectory / "java", unmanagedSourceDirectories <<= Seq(scalaSource, javaSource).join, // remove when sourceFilter, defaultExcludes are removed includeFilter in unmanagedSources <<= (sourceFilter in unmanagedSources) or (includeFilter in unmanagedSources), excludeFilter in unmanagedSources <<= (defaultExcludes in unmanagedSources) or (excludeFilter in unmanagedSources), unmanagedSources <<= collectFiles(unmanagedSourceDirectories, includeFilter in unmanagedSources, excludeFilter in unmanagedSources), watchSources in ConfigGlobal <++= unmanagedSources, managedSourceDirectories <<= Seq(sourceManaged).join, managedSources <<= generate(sourceGenerators), sourceGenerators :== Nil, sourceDirectories <<= Classpaths.concatSettings(unmanagedSourceDirectories, managedSourceDirectories), sources <<= Classpaths.concat(unmanagedSources, managedSources) ) lazy val resourceConfigPaths = Seq( resourceDirectory <<= sourceDirectory / "resources", resourceManaged <<= configSrcSub(resourceManaged), unmanagedResourceDirectories <<= Seq(resourceDirectory).join, managedResourceDirectories <<= Seq(resourceManaged).join, resourceDirectories <<= Classpaths.concatSettings(unmanagedResourceDirectories, managedResourceDirectories), // remove when defaultExcludes are removed excludeFilter in unmanagedResources <<= (defaultExcludes in unmanagedResources) or (excludeFilter in unmanagedResources), unmanagedResources <<= collectFiles(unmanagedResourceDirectories, includeFilter in unmanagedResources, excludeFilter in unmanagedResources), watchSources in ConfigGlobal <++= unmanagedResources, resourceGenerators :== Nil, resourceGenerators <+= (definedSbtPlugins, resourceManaged) map writePluginsDescriptor, managedResources <<= generate(resourceGenerators), resources <<= Classpaths.concat(managedResources, unmanagedResources) ) lazy val outputConfigPaths = Seq( cacheDirectory <<= (crossTarget, thisProject, configuration) { _ / CacheDirectoryName / _.id / _.name }, classDirectory <<= (crossTarget, configuration) { (outDir, conf) => outDir / (prefix(conf.name) + "classes") }, docDirectory <<= (crossTarget, configuration) { (outDir, conf) => outDir / (prefix(conf.name) + "api") } ) def addBaseSources = Seq( unmanagedSources <<= (unmanagedSources, baseDirectory, includeFilter in unmanagedSources, excludeFilter in unmanagedSources) map { (srcs,b,f,excl) => (srcs +++ b * (f -- excl)).get } ) def compileBase = inTask(console)(compilersSetting :: Nil) ++ Seq( classpathOptions in GlobalScope :== ClasspathOptions.boot, classpathOptions in GlobalScope in console :== ClasspathOptions.repl, compileOrder in GlobalScope :== CompileOrder.Mixed, compilersSetting, javacOptions in GlobalScope :== Nil, scalacOptions in GlobalScope :== Nil, scalaInstance <<= scalaInstanceSetting, scalaVersion in GlobalScope <<= appConfiguration( _.provider.scalaProvider.version), 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) ) def makeCrossTarget(t: File, sv: String, sbtv: String, plugin: Boolean, cross: Boolean): File = { val scalaBase = if(cross) t / ("scala-" + sv) else t if(plugin) scalaBase / ("sbt-" + sbtv) else scalaBase } def compilersSetting = compilers <<= (scalaInstance, appConfiguration, streams, classpathOptions, javaHome) map { (si, app, s, co, jh) => Compiler.compilers(si, co, jh)(app, s.log) } lazy val configTasks = docSetting(doc) ++ compileInputsSettings ++ Seq( initialCommands in GlobalScope :== "", cleanupCommands in GlobalScope :== "", compile <<= compileTask tag(Tags.Compile, Tags.CPU), compileIncSetup <<= compileIncSetupTask, console <<= consoleTask, consoleQuick <<= consoleQuickTask, discoveredMainClasses <<= compile map discoverMainClasses storeAs discoveredMainClasses triggeredBy compile, definedSbtPlugins <<= discoverPlugins, inTask(run)(runnerTask :: Nil).head, 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), runMain <<= runMainTask(fullClasspath, runner in run), copyResources <<= copyResourcesTask ) lazy val projectTasks: Seq[Setting[_]] = Seq( cleanFiles <<= Seq(managedDirectory, target).join, cleanKeepFiles <<= historyPath(_.toList), clean <<= (cleanFiles, cleanKeepFiles) map doClean, consoleProject <<= consoleProjectTask, watchTransitiveSources <<= watchTransitiveSourcesTask, watch <<= watchSetting ) def generate(generators: SettingKey[Seq[Task[Seq[File]]]]): Initialize[Task[Seq[File]]] = generators {_.join.map(_.flatten) } def inAllConfigurations[T](key: TaskKey[T]): Initialize[Task[Seq[T]]] = (state, thisProjectRef) flatMap { (state, ref) => 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 } def watchTransitiveSourcesTask: Initialize[Task[Seq[File]]] = inDependencies[Task[Seq[File]]](watchSources.task, const(std.TaskExtra.constant(Nil)), aggregate = true, includeRoot = true) apply { _.join.map(_.flatten) } def transitiveUpdateTask: Initialize[Task[Seq[UpdateReport]]] = forDependencies(ref => (update.task in ref).?, aggregate = false, includeRoot = false) apply( _.flatten.join) def watchSetting: Initialize[Watched] = (pollInterval, thisProjectRef, watchingMessage, triggeredMessage) { (interval, base, msg, trigMsg) => new Watched { val scoped = watchTransitiveSources in base val key = ScopedKey(scoped.scope, scoped.key) override def pollInterval = interval override def watchingMessage(s: WatchState) = msg(s) override def triggeredMessage(s: WatchState) = trigMsg(s) override def watchPaths(s: State) = EvaluateTask.evaluateTask(Project structure s, key, s, base) match { case Some(Value(ps)) => ps case Some(Inc(i)) => throw i case None => error("key not found: " + Project.displayFull(key)) } } } def scalaInstanceSetting = (appConfiguration, scalaVersion, scalaHome) map { (app, version, home) => val launcher = app.provider.scalaProvider.launcher home match { case None => ScalaInstance(version, launcher) case Some(h) => ScalaInstance(h, launcher) } } lazy val testTasks: Seq[Setting[_]] = testTaskOptions(test) ++ testTaskOptions(testOnly) ++ Seq( testLoader <<= (fullClasspath, scalaInstance, taskTemporaryDirectory) map { (cp, si, temp) => TestFramework.createTestLoader(data(cp), si, IO.createUniqueDirectory(temp)) }, testFrameworks in GlobalScope :== { import sbt.TestFrameworks._ Seq(ScalaCheck, Specs2, Specs, ScalaTest, JUnit) }, loadedTestFrameworks <<= (testFrameworks, streams, testLoader) map { (frameworks, s, loader) => frameworks.flatMap(f => f.create(loader, s.log).map( x => (f,x)).toIterable).toMap }, definedTests <<= detectTests, definedTestNames <<= definedTests map ( _.map(_.name).distinct) storeAs definedTestNames triggeredBy compile, testListeners in GlobalScope :== Nil, testOptions in GlobalScope :== Nil, testExecution in test <<= testExecutionTask(test), testExecution in testOnly <<= testExecutionTask(testOnly), executeTests <<= (streams in test, loadedTestFrameworks, testExecution in test, testLoader, definedTests, resolvedScoped, state) flatMap { (s, frameworkMap, config, loader, discovered, scoped, st) => implicit val display = Project.showContextKey(st) Tests(frameworkMap, loader, discovered, config, noTestsMessage(ScopedKey(scoped.scope, test.key)), s.log) }, test <<= (executeTests, streams) map { (results, s) => Tests.showResults(s.log, results) }, testOnly <<= testOnlyTask ) private[this] def noTestsMessage(scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): String = "No tests to run for " + display(scoped) lazy val TaskGlobal: Scope = ThisScope.copy(task = Global) lazy val ConfigGlobal: Scope = ThisScope.copy(config = Global) def testTaskOptions(key: Scoped): Seq[Setting[_]] = inTask(key)( Seq( testListeners <<= (streams, resolvedScoped, streamsManager, logBuffered, testListeners in TaskGlobal) map { (s, sco, sm, buff, ls) => TestLogger(s.log, testLogger(sm, test in sco.scope), buff) +: ls }, testOptions <<= (testOptions in TaskGlobal, testListeners) map { (options, ls) => Tests.Listeners(ls) +: options } ) ) def testLogger(manager: Streams, baseKey: Scoped)(tdef: TestDefinition): Logger = { val scope = baseKey.scope val extra = scope.extra match { case Select(x) => x; case _ => AttributeMap.empty } val key = ScopedKey(scope.copy(extra = Select(testExtra(extra, tdef))), baseKey.key) manager(key).log } def buffered(log: Logger): Logger = new BufferedLogger(FullLogger(log)) def testExtra(extra: AttributeMap, tdef: TestDefinition): AttributeMap = { val mod = tdef.fingerprint match { case f: SubclassFingerprint => f.isModule; case f: AnnotatedFingerprint => f.isModule; case _ => false } extra.put(name.key, tdef.name).put(isModule, mod) } def testExecutionTask(task: Scoped): Initialize[Task[Tests.Execution]] = (testOptions in task, parallelExecution in task, tags in task) map { (opts, par, ts) => new Tests.Execution(opts, par, ts) } def testOnlyTask = InputTask( loadForParser(definedTestNames)( (s, i) => testOnlyParser(s, i getOrElse Nil) ) ) { result => (streams, loadedTestFrameworks, testExecution in testOnly, testLoader, definedTests, resolvedScoped, result, state) flatMap { case (s, frameworks, config, loader, discovered, scoped, (tests, frameworkOptions), st) => val filter = selectedFilter(tests) val modifiedOpts = Tests.Filter(filter) +: Tests.Argument(frameworkOptions : _*) +: config.options val newConfig = new Tests.Execution(modifiedOpts, config.parallel, config.tags) implicit val display = Project.showContextKey(st) Tests(frameworks, loader, discovered, newConfig, noTestsMessage(scoped), s.log) map { results => Tests.showResults(s.log, results) } } } def selectedFilter(args: Seq[String]): String => Boolean = { val filters = args map GlobFilter.apply s => filters.isEmpty || filters.exists { _ accept s } } def detectTests: Initialize[Task[Seq[TestDefinition]]] = (loadedTestFrameworks, compile, streams) map { (frameworkMap, analysis, s) => Tests.discover(frameworkMap.values.toSeq, analysis, s.log)._1 } def defaultRestrictions: Initialize[Seq[Tags.Rule]] = parallelExecution { par => val max = EvaluateTask.SystemProcessors Tags.limitAll(if(par) max else 1) :: Nil } lazy val packageBase: Seq[Setting[_]] = Seq( artifact <<= moduleName(n => Artifact(n)), packageOptions in GlobalScope :== Nil, artifactName in GlobalScope :== ( Artifact.artifactName _ ) ) lazy val packageConfig: Seq[Setting[_]] = inTask(packageBin)(Seq( packageOptions <<= (name, version, homepage, organization, organizationName, mainClass, packageOptions) map { (name, ver, h, org, orgName, main, p) => Package.addSpecManifestAttributes(name, ver, orgName) +: Package.addImplManifestAttributes(name, ver, h, org, orgName) +: main.map(Package.MainClass.apply) ++: p })) ++ inTask(packageSrc)(Seq( packageOptions <<= (name, version, organizationName, packageOptions) map { Package.addSpecManifestAttributes(_, _, _) +: _ })) ++ packageTasks(packageBin, packageBinMappings) ++ packageTasks(packageSrc, packageSrcMappings) ++ packageTasks(packageDoc, packageDocMappings) ++ Seq(`package` <<= packageBin) def packageBinMappings = products map { _ flatMap Path.allSubpaths } def packageDocMappings = doc map { Path.allSubpaths(_).toSeq } def packageSrcMappings = concatMappings(resourceMappings, sourceMappings) @deprecated("Use `packageBinMappings` instead", "0.12.0") def packageBinTask = packageBinMappings @deprecated("Use `packageDocMappings` instead", "0.12.0") def packageDocTask = packageDocMappings @deprecated("Use `packageSrcMappings` instead", "0.12.0") def packageSrcTask = packageSrcMappings private type Mappings = Initialize[Task[Seq[(File, String)]]] def concatMappings(as: Mappings, bs: Mappings) = (as zipWith bs)( (a,b) => (a :^: b :^: KNil) map { case a :+: b :+: HNil => a ++ b } ) // drop base directories, since there are no valid mappings for these def sourceMappings = (unmanagedSources, unmanagedSourceDirectories, baseDirectory) map { (srcs, sdirs, base) => ( (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) 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, 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 if(combined.isEmpty) a.copy(classifier = None, configurations = cOpt.toList) else { val classifierString = combined mkString "-" val confs = cOpt.toList flatMap { c => artifactConfigurations(a, c, classifier) } a.copy(classifier = Some(classifierString), `type` = Artifact.classifierType(classifierString), configurations = confs) } } def artifactConfigurations(base: Artifact, scope: Configuration, classifier: Option[String]): Iterable[Configuration] = if(base.configurations.isEmpty) classifier match { case Some(c) => Artifact.classifierConf(c) :: Nil case None => scope :: Nil } else base.configurations def pairID[A,B] = (a: A, b: B) => (a,b) def perTaskCache(key: TaskKey[_]): Setting[File] = cacheDirectory ~= { _ / ("for_" + key.key.label) } def packageTasks(key: TaskKey[File], mappingsTask: Initialize[Task[Seq[(File,String)]]]) = inTask(key)( Seq( key in TaskGlobal <<= packageTask, packageConfiguration <<= packageConfigurationTask, mappings <<= mappingsTask, packagedArtifact <<= (artifact, key) map pairID, artifact <<= artifactSetting, perTaskCache(key), artifactPath <<= artifactPathSetting(artifact) )) def packageTask: Initialize[Task[File]] = (packageConfiguration, cacheDirectory, streams) map { (config, cacheDir, s) => Package(config, cacheDir, s.log) config.jar } def packageConfigurationTask: Initialize[Task[Package.Configuration]] = (mappings, artifactPath, packageOptions) map { (srcs, path, options) => new Package.Configuration(srcs, path, options) } def selectRunMain(classes: Seq[String]): Option[String] = sbt.SelectMainClass(Some(SimpleReader readLine _), classes) def selectPackageMain(classes: Seq[String]): Option[String] = sbt.SelectMainClass(None, classes) def doClean(clean: Seq[File], preserve: Seq[File]): Unit = IO.withTemporaryDirectory { temp => val mappings = preserve.filter(_.exists).zipWithIndex map { case (f, i) => (f, new File(temp, i.toHexString)) } IO.move(mappings) IO.delete(clean) IO.move(mappings.map(_.swap)) } def runMainTask(classpath: TaskKey[Classpath], scalaRun: TaskKey[ScalaRun]): Initialize[InputTask[Unit]] = { import DefaultParsers._ InputTask( loadForParser(discoveredMainClasses)( (s, names) => runMainParser(s, names getOrElse Nil) ) ) { result => (classpath, scalaRun, streams, result) map { case (cp, runner, s, (mainClass, args)) => toError(runner.run(mainClass, data(cp), args, s.log)) } } } def runTask(classpath: TaskKey[Classpath], mainClassTask: TaskKey[Option[String]], scalaRun: TaskKey[ScalaRun]): Initialize[InputTask[Unit]] = inputTask { result => (classpath, mainClassTask, scalaRun, streams, result) map { (cp, main, runner, s, args) => val mainClass = main getOrElse error("No main class detected.") toError(runner.run(mainClass, data(cp), args, s.log)) } } def runnerTask = runner <<= runnerInit def runnerInit: Initialize[Task[ScalaRun]] = (taskTemporaryDirectory, scalaInstance, baseDirectory, javaOptions, outputStrategy, fork, javaHome, trapExit, connectInput) map { (tmp, si, base, options, strategy, forkRun, javaHomeDir, trap, connectIn) => if(forkRun) { new ForkRun( ForkOptions(scalaJars = si.jars, javaHome = javaHomeDir, connectInput = connectIn, outputStrategy = strategy, runJVMOptions = options, workingDirectory = Some(base)) ) } else new Run(si, trap, tmp) } def docSetting(key: TaskKey[File]): Seq[Setting[_]] = inTask(key)(compileInputsSettings ++ Seq( perTaskCache(key), target <<= docDirectory, // deprecate docDirectory in favor of 'target in doc'; remove when docDirectory is removed scalacOptions <<= scaladocOptions or scalacOptions, // deprecate scaladocOptions in favor of 'scalacOptions in doc'; remove when scaladocOptions is removed key in TaskGlobal <<= (cacheDirectory, compileInputs, target, configuration, streams) map { (cache, in, out, config, s) => // For Scala/Java hybrid projects, the output docs are rebased to `scala` or `java` sub-directory accordingly. We do hybrid // mode iff both *.scala and *.java files exist -- other doc resources (package.html, *.jpg etc.) don't influence the decision. val srcs = in.config.sources val hybrid = srcs.exists(_.name.endsWith(".scala")) && srcs.exists(_.name.endsWith(".java")) val (scalaOut, javaOut) = if (hybrid) (out / "scala", out / "java") else (out, out) val cp = in.config.classpath.toList - in.config.classesDirectory Doc(in.config.maxErrors, in.compilers.scalac).cached(cache / "scala", nameForSrc(config.name), srcs, cp, scalaOut, in.config.options, s.log) Doc(in.config.maxErrors, in.compilers.javac).cached(cache / "java", nameForSrc(config.name), srcs, cp, javaOut, in.config.javacOptions, s.log) out } )) def mainRunTask = run <<= runTask(fullClasspath in Runtime, mainClass in run, runner in run) def mainRunMainTask = runMain <<= runMainTask(fullClasspath in Runtime, runner in run) def discoverMainClasses(analysis: inc.Analysis): Seq[String] = Discovery.applications(Tests.allDefs(analysis)) collect { case (definition, discovered) if(discovered.hasMain) => definition.name } def consoleProjectTask = (state, streams, initialCommands in consoleProject) map { (state, s, extra) => ConsoleProject(state, extra)(s.log); println() } def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console) def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick) def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = (compilers in task, classpath in task, scalacOptions in task, initialCommands in task, cleanupCommands in task, taskTemporaryDirectory in task, scalaInstance in task, streams) map { (cs, cp, options, initCommands, cleanup, temp, si, s) => val loader = sbt.classpath.ClasspathUtilities.makeLoader(data(cp), si.loader, si, IO.createUniqueDirectory(temp)) (new Console(cs.scalac))(data(cp), options, loader, initCommands, cleanup)()(s.log).foreach(msg => error(msg)) println() } def compileTask = (compileInputs, streams) map { (i,s) => Compiler(i,s.log) } def compileIncSetupTask = (dependencyClasspath, cacheDirectory, skip in compile, definesClass) map { (cp, cacheDir, skip, definesC) => Compiler.IncSetup(analysisMap(cp), definesC, skip, cacheDir / "inc_compile") } def compileInputsSettings: Seq[Setting[_]] = { val optionsPair = TaskKey.local[(Seq[String], Seq[String])] Seq(optionsPair <<= (scalacOptions, javacOptions) map Pair.apply, compileInputs <<= (dependencyClasspath, sources, compilers, optionsPair, classDirectory, compileOrder, compileIncSetup, maxErrors, streams) map { (cp, srcs, cs, optsPair, classes, order, incSetup, maxErr, s) => Compiler.inputs(classes +: data(cp), srcs, classes, optsPair._1, optsPair._2, maxErr, order)(cs, incSetup, s.log) }) } 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" if(plugins.isEmpty) { IO.delete(descriptor) Nil } else { IO.writeLines(descriptor, plugins.toSeq.sorted) 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; } def copyResourcesTask = (classDirectory, cacheDirectory, resources, resourceDirectories, streams) map { (target, cache, resrcs, dirs, s) => val cacheFile = cache / "copy-resources" 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 } def runMainParser: (State, Seq[String]) => Parser[(String, Seq[String])] = { import DefaultParsers._ (state, mainClasses) => Space ~> token(NotSpace examples mainClasses.toSet) ~ spaceDelimited("") } def testOnlyParser: (State, Seq[String]) => Parser[(Seq[String],Seq[String])] = { (state, tests) => import DefaultParsers._ val selectTests = distinctParser(tests.toSet, true) val options = (token(Space) ~> token("--") ~> spaceDelimited("