summaryrefslogtreecommitdiff
path: root/project/Build.scala
blob: abab77566624377f98432a2c8efd267a720f94ed (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
import sbt._
import Keys._
import partest._
import SameTest._

object ScalaBuild extends Build with Layers {
  // New tasks/settings specific to the scala build.
  lazy val lockerLock: TaskKey[Unit] = TaskKey("locker-lock", 
    "Locks the locker layer of the compiler build such that it won't rebuild on changed source files.")
  lazy val lockerUnlock: TaskKey[Unit] = TaskKey("locker-unlock", 
    "Unlocks the locker layer of the compiler so that it will be recompiled on changed source files.")
  lazy val lockFile: SettingKey[File] = SettingKey("lock-file", 
    "Location of the lock file compiling this project.")
  // New tasks/settings specific to the scala build.
  lazy val lock: TaskKey[Unit] = TaskKey("lock", "Locks this project so it won't be recompiled.")
  lazy val unlock: TaskKey[Unit] = TaskKey("unlock", "Unlocks this project so it will be recompiled.")
  lazy val makeDist: TaskKey[File] = TaskKey("make-dist", 
    "Creates a mini-distribution (scala home directory) for this build in a zip file.")
  lazy val makeExplodedDist: TaskKey[File] = TaskKey("make-exploded-dist", 
    "Creates a mini-distribution (scala home directory) for this build in a directory.")
  lazy val makeDistMappings: TaskKey[Map[File, String]] = TaskKey("make-dist-mappings", 
    "Creates distribution mappings for creating zips,jars,directorys,etc.")
  lazy val buildFixed = AttributeKey[Boolean]("build-uri-fixed")

  // Build wide settings:
  override lazy val settings = super.settings ++ Seq(
    autoScalaLibrary := false,
    resolvers += Resolver.url(
      "Typesafe nightlies", 
      url("https://typesafe.artifactoryonline.com/typesafe/ivy-snapshots/")
    )(Resolver.ivyStylePatterns),
    resolvers ++= Seq(
      "junit interface repo" at "https://repository.jboss.org/nexus/content/repositories/scala-tools-releases",
      ScalaToolsSnapshots
    ),
    organization := "org.scala-lang",
    version := "2.10.0-SNAPSHOT",
    pomExtra := <xml:group>
      <inceptionYear>2002</inceptionYear>
        <licenses>
          <license>
            <name>BSD-like</name>
            <url>http://www.scala-lang.org/downloads/license.html</url>
          </license>
        </licenses>
        <scm>
          <connection>scm:svn:http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk</connection>
        </scm>
        <issueManagement>
          <system>jira</system>
          <url>http://issues.scala-lang.org</url>
        </issueManagement>
      </xml:group>,
    commands += Command.command("fix-uri-projects") { (state: State) =>
      if(state.get(buildFixed) getOrElse false) state
      else {
        // TODO -fix up scalacheck's dependencies!
        val extracted = Project.extract(state)
        import extracted._
        def fix(s: Setting[_]): Setting[_] = s match {
          case ScopedExternalSetting(`scalacheck`, scalaInstance.key, setting)    => fullQuickScalaReference mapKey Project.mapScope(_ => s.key.scope)
          case s                                                                  => s
         }
         val transformed = session.mergeSettings map ( s => fix(s) )
         val scopes = transformed collect { case ScopedExternalSetting(`scalacheck`, _, s) => s.key.scope } toSet
         // Create some fixers so we don't download scala or rely on it.
         val fixers = for { scope <- scopes
                            setting <- Seq(autoScalaLibrary := false, crossPaths := false)
                      } yield setting mapKey Project.mapScope(_ => scope)
         val newStructure = Load.reapply(transformed ++ fixers, structure)
         Project.setProject(session, newStructure, state).put(buildFixed, true)
      }
    },
    onLoad in Global <<= (onLoad in Global) apply (_ andThen { (state: State) =>
      "fix-uri-projects" :: state
    })
  )

  // Collections of projects to run 'compile' on.
  lazy val compiledProjects = Seq(quickLib, quickComp, continuationsLibrary, actors, swing, dbc, forkjoin, fjbg)
  // Collection of projects to 'package' and 'publish' together.
  lazy val packagedBinaryProjects = Seq(scalaLibrary, scalaCompiler, swing, dbc, continuationsPlugin, jline, scalap)
  lazy val partestRunProjects = Seq(testsuite, continuationsTestsuite)
  
  private def epflPomExtra = (
    <xml:group>
      <inceptionYear>2002</inceptionYear>
      <licenses>
        <license>
          <name>BSD-like</name>
          <url>http://www.scala-lang.org/downloads/license.html</url>
        </license>
      </licenses>
      <scm>
        <connection>scm:svn:http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk</connection>
      </scm>
      <issueManagement>
        <system>jira</system>
        <url>http://issues.scala-lang.org</url>
      </issueManagement>
    </xml:group>
  )
    
  // Settings used to make sure publishing goes smoothly.
  def publishSettings: Seq[Setting[_]] = Seq(
    ivyScala ~= ((is: Option[IvyScala]) => is.map(_.copy(checkExplicit = false))),
    pomIncludeRepository := (_ => false),
    publishMavenStyle := true,
    makePomConfiguration <<= makePomConfiguration apply (_.copy(configurations = Some(Seq(Compile, Default)))),
    pomExtra := epflPomExtra
  )

  // Settings for root project.  These are aggregate tasks against the rest of the build.
  def projectSettings: Seq[Setting[_]] = publishSettings ++ Seq(
    doc in Compile <<= (doc in documentation in Compile).identity,
    // These next two aggregate commands on several projects and return results that are to be ignored by remaining tasks.
    compile in Compile <<= compiledProjects.map(p => compile in p in Compile).join.map(_.head),
    // TODO - just clean target? i.e. target map IO.deleteRecursively
    clean <<= (compiledProjects ++ partestRunProjects).map(p => clean in p).dependOn,
    packageBin in Compile <<= packagedBinaryProjects.map(p => packageBin in p in Compile).join.map(_.head),
    // TODO - Make sure scalaLibrary has packageDoc + packageSrc from documentation attached...
    publish <<= packagedBinaryProjects.map(p => publish in p).join.map(_.head),
    publishLocal <<= packagedBinaryProjects.map(p => publishLocal in p).join.map(_.head),
    packageDoc in Compile <<= (packageDoc in documentation in Compile).identity,
    packageSrc in Compile <<= (packageSrc in documentation in Compile).identity,
    test in Test <<= (runPartest in testsuite, runPartest in continuationsTestsuite, checkSame in testsuite) map { (a,b,c) => () },
    lockerLock <<= (lockFile in lockerLib, lockFile in lockerComp, compile in Compile in lockerLib, compile in Compile in lockerComp) map { (lib, comp, _, _) =>
      Seq(lib,comp).foreach(f => IO.touch(f))
    },
    lockerUnlock <<= (lockFile in lockerLib, lockFile in lockerComp) map { (lib, comp) =>
      Seq(lib,comp).foreach(IO.delete)
    },
    genBinQuick <<= (genBinQuick in scaladist).identity,
    makeDist <<= (makeDist in scaladist).identity,
    makeExplodedDist <<= (makeExplodedDist in scaladist).identity,
    // Note: We override unmanagedSources so that ~ compile will look at all these sources, then run our aggregated compile...
    unmanagedSourceDirectories in Compile <<= baseDirectory apply (_ / "src") apply { dir =>
      Seq("library/scala","actors","compiler","fjbg","swing","continuations/library","forkjoin") map (dir / _)
    },
    // TODO - Make exported products == makeDist so we can use this when creating a *real* distribution.
    commands += Release.pushStarr
    //commands += Release.setStarrHome
  )
  // Note: Root project is determined by lowest-alphabetical project that has baseDirectory as file(".").  we use aaa_ to 'win'.
  lazy val aaa_root = Project("scala", file(".")) settings(projectSettings: _*) settings(ShaResolve.settings: _*)

  // External dependencies used for various projects
  lazy val externalDeps: Setting[_] = libraryDependencies <<= (sbtVersion)(v => 
    Seq(
      "org.apache.ant" % "ant" % "1.8.2",
      "org.scala-tools.sbt" % "compiler-interface" % v % "provided"
    )
  )

  // These are setting overrides for most artifacts in the Scala build file.
  def settingOverrides: Seq[Setting[_]] = publishSettings ++ Seq(
                             crossPaths := false,
                             publishArtifact in packageDoc := false,
                             publishArtifact in packageSrc := false,
                             target <<= (baseDirectory, name) apply (_ / "target" / _),
                             (classDirectory in Compile) <<= target(_ / "classes"),
                             javacOptions ++= Seq("-target", "1.5", "-source", "1.5"),
                             scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _),
                             javaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _),
                             autoScalaLibrary := false,
                             unmanagedJars in Compile := Seq(),
                             // Most libs in the compiler use this order to build.
                             compileOrder in Compile := CompileOrder.JavaThenScala,
                             lockFile <<= target(_ / "compile.lock"),
                             skip in Compile <<= lockFile.map(_  exists),
                             lock <<= lockFile map { f => IO.touch(f) },
                             unlock <<= lockFile map IO.delete
                            )

  // --------------------------------------------------------------
  //  Libraries used by Scalac that change infrequently
  //  (or hopefully so).
  // --------------------------------------------------------------

  // Jline nested project.   Compile this sucker once and be done.
  lazy val jline = Project("jline", file("src/jline"))
  // Fast Java Bytecode Generator (nested in every scala-compiler.jar)
  lazy val fjbg = Project("fjbg", file(".")) settings(settingOverrides : _*)
  // Forkjoin backport
  lazy val forkjoin = Project("forkjoin", file(".")) settings(settingOverrides : _*)

  // --------------------------------------------------------------
  //  The magic kingdom.
  //  Layered compilation of Scala.
  //   Stable Reference -> Locker ('Lockable' dev version) -> Quick -> Strap (Binary compatibility testing)
  // --------------------------------------------------------------

  // Need a report on this...
  // TODO - Resolve STARR from a repo..
  lazy val STARR = scalaInstance <<= (appConfiguration, ShaResolve.pullBinaryLibs in ThisBuild) map { (app, _) =>
    val launcher = app.provider.scalaProvider.launcher
    val library  = file("lib/scala-library.jar")
    val compiler = file("lib/scala-compiler.jar")
    val libJars  = (file("lib") * "*.jar").get filterNot Set(library, compiler)
    ScalaInstance("starr", library, compiler, launcher, libJars: _*)
  }

  // Locker is a lockable Scala compiler that can be built of 'current' source to perform rapid development.
  lazy val (lockerLib, lockerComp) = makeLayer("locker", STARR)
  lazy val locker = Project("locker", file(".")) aggregate(lockerLib, lockerComp)

  // Quick is the general purpose project layer for the Scala compiler.
  lazy val (quickLib, quickComp) = makeLayer("quick", makeScalaReference("locker", lockerLib, lockerComp, fjbg))
  lazy val quick = Project("quick", file(".")) aggregate(quickLib, quickComp)

  // Reference to quick scala instance.
  lazy val quickScalaInstance = makeScalaReference("quick", quickLib, quickComp, fjbg)
  def quickScalaLibraryDependency = unmanagedClasspath in Compile <++= (exportedProducts in quickLib in Compile).identity
  def quickScalaCompilerDependency = unmanagedClasspath in Compile <++= (exportedProducts in quickComp in Compile).identity

  // Strapp is used to test binary 'sameness' between things built with locker and things built with quick.
  lazy val (strappLib, strappComp) = makeLayer("strapp", quickScalaInstance)

  // --------------------------------------------------------------
  //  Projects dependent on layered compilation (quick)
  // --------------------------------------------------------------
  def addCheaterDependency(projectName: String): Setting[_] = 
    pomPostProcess <<= (version, organization, pomPostProcess) apply { (v,o,k) => 
      val dependency: scala.xml.Node = 
        <dependency>
          <groupId>{o}</groupId>
          <artifactid>{projectName}</artifactid>
          <version>{v}</version>
        </dependency>
      def fixDependencies(node: scala.xml.Node): scala.xml.Node = node match {
         case <dependencies>{nested@_*}</dependencies> => <dependencies>{dependency}{nested}</dependencies>
         case x                                        => x
      }
      // This is a hack to get around issues where \ and \\ don't work if any of the children are `scala.xml.Group`.
      def hasDependencies(root: scala.xml.Node): Boolean =
        (root.child collectFirst {
          case n: scala.xml.Elem if n.label == "dependencies" => n
        } isEmpty)
      // TODO - Keep namespace on project...
      k andThen { 
        case n @ <project>{ nested@_*}</project> if hasDependencies(n)   =>
          <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">{nested}<dependencies>{dependency}</dependencies></project>
        case <project>{ nested@_*}</project>                                       => 
          <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">{ nested map fixDependencies }</project>
      }
    }

  // TODO - in sabbus, these all use locker to build...  I think tihs way is better, but let's farm this idea around.
  // TODO - Actors + swing separate jars...
  lazy val dependentProjectSettings = settingOverrides ++ Seq(quickScalaInstance, quickScalaLibraryDependency, addCheaterDependency("scala-library"))
  lazy val actors = Project("actors", file(".")) settings(dependentProjectSettings:_*) dependsOn(forkjoin % "provided")
  lazy val dbc = Project("dbc", file(".")) settings(dependentProjectSettings:_*)
  // TODO - Remove actors dependency from pom...
  lazy val swing = Project("swing", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided")
  // This project will generate man pages (in man1 and html) for scala.    
  lazy val manmakerSettings: Seq[Setting[_]] = dependentProjectSettings :+ externalDeps
  lazy val manmaker = Project("manual", file(".")) settings(manmakerSettings:_*)

  // Things that compile against the compiler.
  lazy val compilerDependentProjectSettings = dependentProjectSettings ++ Seq(quickScalaCompilerDependency, addCheaterDependency("scala-compiler"))
  lazy val partestSettings = compilerDependentProjectSettings :+ externalDeps
  lazy val partest = Project("partest", file(".")) settings(partestSettings:_*)  dependsOn(actors,forkjoin,scalap)
  lazy val scalapSettings = compilerDependentProjectSettings ++ Seq(
    name := "scalap",
    exportJars := true
  )
  lazy val scalap = Project("scalap", file(".")) settings(scalapSettings:_*)

  // --------------------------------------------------------------
  //  Continuations plugin + library
  // --------------------------------------------------------------
  lazy val continuationsPluginSettings = compilerDependentProjectSettings ++ Seq(
    scalaSource in Compile <<= baseDirectory(_ / "src/continuations/plugin/"),
    resourceDirectory in Compile <<= baseDirectory(_ / "src/continuations/plugin/"),
    exportJars := true,
    name := "continuations"  // Note: This artifact is directly exported.

  )
  lazy val continuationsPlugin = Project("continuations-plugin", file(".")) settings(continuationsPluginSettings:_*)
  lazy val continuationsLibrarySettings = dependentProjectSettings ++ Seq(
    scalaSource in Compile <<= baseDirectory(_ / "src/continuations/library/"),
    scalacOptions in Compile <++= (exportedProducts in Compile in continuationsPlugin) map { 
     case Seq(cpDir) => Seq("-Xplugin-require:continuations", "-P:continuations:enable", "-Xplugin:"+cpDir.data.getAbsolutePath)
    }
  )
  lazy val continuationsLibrary = Project("continuations-library", file(".")) settings(continuationsLibrarySettings:_*)

  // TODO - OSGi Manifest

  // --------------------------------------------------------------
  //  Real Library Artifact
  // --------------------------------------------------------------
  val allSubpathsCopy = (dir: File) => (dir.*** --- dir) x (relativeTo(dir)|flat)
  def productTaskToMapping(products : Seq[File]) = products flatMap { p => allSubpathsCopy(p) }
  lazy val packageScalaLibBinTask = Seq(quickLib, continuationsLibrary, forkjoin, actors).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping)
  lazy val scalaLibArtifactSettings: Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaLibBinTask)) ++ Seq(
    name := "scala-library",
    crossPaths := false,
    exportJars := true,
    autoScalaLibrary := false,
    unmanagedJars in Compile := Seq(),
    packageDoc in Compile <<= (packageDoc in documentation in Compile).identity,
    packageSrc in Compile <<= (packageSrc in documentation in Compile).identity,
    fullClasspath in Runtime <<= (exportedProducts in Compile).identity,
    quickScalaInstance,
    target <<= (baseDirectory, name) apply (_ / "target" / _)
  )
  lazy val scalaLibrary = Project("scala-library", file(".")) settings(publishSettings:_*) settings(scalaLibArtifactSettings:_*)

  // --------------------------------------------------------------
  //  Real Compiler Artifact
  // --------------------------------------------------------------
  lazy val packageScalaBinTask = Seq(quickComp, fjbg).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping)
  lazy val scalaBinArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaBinTask)) ++ Seq(
    name := "scala-compiler",
    crossPaths := false,
    exportJars := true,
    autoScalaLibrary := false,
    unmanagedJars in Compile := Seq(),
    fullClasspath in Runtime <<= (exportedProducts in Compile).identity,
    quickScalaInstance,
    target <<= (baseDirectory, name) apply (_ / "target" / _)
  )
  lazy val scalaCompiler = Project("scala-compiler", file(".")) settings(publishSettings:_*) settings(scalaBinArtifactSettings:_*) dependsOn(scalaLibrary)
  lazy val fullQuickScalaReference = makeScalaReference("pack", scalaLibrary, scalaCompiler, fjbg)

  // --------------------------------------------------------------
  //  Testing
  // --------------------------------------------------------------
  /* lazy val scalacheckSettings: Seq[Setting[_]] = Seq(fullQuickScalaReference, crossPaths := false)*/
  lazy val scalacheck = uri("git://github.com/rickynils/scalacheck.git")

  lazy val testsuiteSettings: Seq[Setting[_]] = compilerDependentProjectSettings ++ partestTaskSettings ++ VerifyClassLoad.settings ++ Seq(
    unmanagedBase <<= baseDirectory / "test/files/lib",
    fullClasspath in VerifyClassLoad.checkClassLoad <<= (fullClasspath in scalaLibrary in Runtime).identity,
    autoScalaLibrary := false,
    checkSameLibrary <<= checkSameBinaryProjects(quickLib, strappLib),
    checkSameCompiler <<= checkSameBinaryProjects(quickComp, strappComp),
    checkSame <<= (checkSameLibrary, checkSameCompiler) map ((a,b) => ()),
    autoScalaLibrary := false
  )
  lazy val continuationsTestsuiteSettings: Seq[Setting[_]] = testsuiteSettings ++ Seq(
    scalacOptions in Test <++= (exportedProducts in Compile in continuationsPlugin) map { 
     case Seq(cpDir) => Seq("-Xplugin-require:continuations", "-P:continuations:enable", "-Xplugin:"+cpDir.data.getAbsolutePath)
    },
    partestDirs <<= baseDirectory apply { bd =>
      def mkFile(name: String) = bd / "test" / "files" / name
      def mkTestType(name: String) = name.drop("continuations-".length).toString
      Seq("continuations-neg", "continuations-run") map (t => mkTestType(t) -> mkFile(t)) toMap
    }
  )
  val testsuite = (
    Project("testsuite", file(".")) 
    settings (testsuiteSettings:_*)
    dependsOn (swing, scalaLibrary, scalaCompiler, fjbg, partest, scalacheck)
  )
  val continuationsTestsuite = (
    Project("continuations-testsuite", file("."))
    settings (continuationsTestsuiteSettings:_*) 
    dependsOn (partest, swing, scalaLibrary, scalaCompiler, fjbg)
  )

  // --------------------------------------------------------------
  //  Generating Documentation.
  // --------------------------------------------------------------
  
  // TODO - Migrate this into the dist project.
  // Scaladocs
  def distScalaInstance = makeScalaReference("dist", scalaLibrary, scalaCompiler, fjbg)
  lazy val documentationSettings: Seq[Setting[_]] = dependentProjectSettings ++ Seq(
    // TODO - Make these work for realz.
    defaultExcludes in unmanagedSources in Compile := ((".*"  - ".") || HiddenFileFilter ||
      "reflect/Print.scala" ||
      "reflect/Symbol.scala" ||
      "reflect/Tree.scala" ||
      "reflect/Type.scala" ||
      "runtime/*$.scala" ||
      "runtime/ScalaRuntime.scala" ||
      "runtime/StringAdd.scala" ||
      "scala/swing/test/*"),
    sourceFilter in Compile := ("*.scala"),
    unmanagedSourceDirectories in Compile <<= baseDirectory apply { dir =>
      Seq(dir / "src" / "library" / "scala", dir / "src" / "actors", dir / "src" / "swing", dir / "src" / "continuations" / "library")
    },
    compile := inc.Analysis.Empty,
    scaladocOptions in Compile in doc <++= (baseDirectory) map (bd => 
      Seq("-sourcepath", (bd / "src" / "library").getAbsolutePath,
          "-doc-no-compile", (bd / "src" / "library-aux").getAbsolutePath,
          "-doc-source-url", """https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/€{FILE_PATH}.scala#L1""",
          "-doc-root-content", (bd / "compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt").getAbsolutePath
      )),
    classpathOptions in Compile := ClasspathOptions.manual
  )
  lazy val documentation = (
    Project("documentation", file("."))
    settings (documentationSettings: _*)
    dependsOn(quickLib, quickComp, actors, fjbg, forkjoin, swing, continuationsLibrary)
  )

  // --------------------------------------------------------------
  //  Packaging a distro
  // --------------------------------------------------------------

  class ScalaToolRunner(classpath: Classpath) {
    // TODO - Don't use the ant task directly...
    lazy val classLoader        = new java.net.URLClassLoader(classpath.map(_.data.toURI.toURL).toArray, null)
    lazy val mainClass          = classLoader.loadClass("scala.tools.ant.ScalaTool")
    lazy val executeMethod      = mainClass.getMethod("execute")
    lazy val setFileMethod      = mainClass.getMethod("setFile", classOf[java.io.File])
    lazy val setClassMethod     = mainClass.getMethod("setClass", classOf[String])
    lazy val setClasspathMethod = mainClass.getMethod("setClassPath", classOf[String])
    lazy val instance           = mainClass.newInstance()
    
    def setClass(cls: String): Unit    = setClassMethod.invoke(instance, cls)
    def setFile(file: File): Unit      = setFileMethod.invoke(instance, file)
    def setClasspath(cp: String): Unit = setClasspathMethod.invoke(instance, cp)
    def execute(): Unit                = executeMethod.invoke(instance)
  }

  def genBinTask(
    runner: ScopedTask[ScalaToolRunner], 
    outputDir: ScopedSetting[File], 
    classpath: ScopedTask[Classpath], 
    useClasspath: Boolean
  ): Project.Initialize[sbt.Task[Map[File,String]]] = {
    (runner, outputDir, classpath, streams) map { (runner, outDir, cp, s) =>
      IO.createDirectory(outDir)
      val classToFilename = Map(
        "scala.tools.nsc.MainGenericRunner" -> "scala",
        "scala.tools.nsc.Main"              -> "scalac",
        "scala.tools.nsc.ScalaDoc"          -> "scaladoc",
        "scala.tools.nsc.CompileClient"     -> "fsc",
        "scala.tools.scalap.Main"           -> "scalap"
      )
      if (useClasspath) { 
        val classpath = Build.data(cp).map(_.getCanonicalPath).distinct.mkString(",")
        s.log.debug("Setting classpath = " + classpath)
        runner setClasspath classpath
      }
      def genBinFiles(cls: String, dest: File) = {
        runner.setClass(cls)
        runner.setFile(dest)
        runner.execute()
        // TODO - Mark generated files as executable (755 or a+x) that is *not* JDK6 specific...
        dest.setExecutable(true)
      }
      def makeBinMappings(cls: String, binName: String): Map[File,String] = {
        val file       = outDir / binName
        val winBinName = binName + ".bat"
        genBinFiles(cls, file)
        Map( file -> ("bin/"+binName), outDir / winBinName -> ("bin/"+winBinName) )
      }
      classToFilename.flatMap((makeBinMappings _).tupled).toMap
    }
  }
  def runManmakerTask(classpath: ScopedTask[Classpath], scalaRun: ScopedTask[ScalaRun], mainClass: String, dir: String, ext: String): Project.Initialize[Task[Map[File,String]]] =
    (classpath, scalaRun, streams, target) map { (cp, runner, s, target) =>
      val binaries = Seq("fsc", "scala", "scalac", "scaladoc", "scalap")
      binaries map { bin =>
        val file = target / "man" / dir / (bin + ext)
        val classname = "scala.man1." + bin
        IO.createDirectory(file.getParentFile)
        toError(runner.run(mainClass, Build.data(cp), Seq(classname, file.getAbsolutePath), s.log))   
        file -> ("man/" + dir + "/" + bin + ext)
      } toMap
    }

  val genBinRunner = TaskKey[ScalaToolRunner]("gen-bin-runner", 
    "Creates a utility to generate script files for Scala.")  
  val genBin = TaskKey[Map[File,String]]("gen-bin",
    "Creates script files for Scala distribution.")
  val binDir = SettingKey[File]("binaries-directory",
    "Directory where binary scripts will be located.")
  val genBinQuick = TaskKey[Map[File,String]]("gen-quick-bin",
    "Creates script files for testing against current Scala build classfiles (not local dist).")
  val runManmakerMan = TaskKey[Map[File,String]]("make-man",
    "Runs the man maker project to generate man pages")
  val runManmakerHtml = TaskKey[Map[File,String]]("make-html",
    "Runs the man maker project to generate html pages")

  lazy val scalaDistSettings: Seq[Setting[_]] = Seq(
    crossPaths := false,
    target <<= (baseDirectory, name) apply (_ / "target" / _),
    scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _),
    autoScalaLibrary := false,
    unmanagedJars in Compile := Seq(),
    genBinRunner <<= (fullClasspath in quickComp in Runtime) map (new ScalaToolRunner(_)),
    binDir <<= target(_/"bin"),
    genBin <<= genBinTask(genBinRunner, binDir, fullClasspath in Runtime, false),
    binDir in genBinQuick <<= baseDirectory apply (_ / "target" / "bin"),
    // Configure the classpath this way to avoid having .jar files and previous layers on the classpath.
    fullClasspath in Runtime in genBinQuick <<= Seq(quickComp,quickLib,scalap,actors,swing,dbc,fjbg,jline,forkjoin).map(classDirectory in Compile in _).join.map(Attributed.blankSeq),
    fullClasspath in Runtime in genBinQuick <++= (fullClasspath in Compile in jline),
    genBinQuick <<= genBinTask(genBinRunner, binDir in genBinQuick, fullClasspath in Runtime in genBinQuick, true),
    runManmakerMan <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitManPage", "man1", ".1"),
    runManmakerHtml <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitHtml", "doc", ".html"),
    // TODO - We could *really* clean this up in many ways.   Let's look into making a a Seq of "direct jars" (scalaLibrary, scalaCompiler, jline, scalap)
    // a seq of "plugin jars" (continuationsPlugin) and "binaries" (genBin) and "documentation" mappings (genBin) that this can aggregate.
    // really need to figure out a better way to pull jline + jansi.
    makeDistMappings <<= (genBin, 
                          runManmakerMan,
                          runManmakerHtml,
                          packageBin in scalaLibrary in Compile, 
                          packageBin in scalaCompiler in Compile,
                          packageBin in jline in Compile,
                          packageBin in continuationsPlugin in Compile,
                          managedClasspath in jline in Compile,
                          packageBin in scalap in Compile) map {
      (binaries, man, html, lib, comp, jline, continuations, jlineDeps, scalap) =>
        val jlineDepMap: Seq[(File, String)] = jlineDeps.map(_.data).flatMap(_ x Path.flat) map { case(a,b) => a -> ("lib/"+b) }
        binaries ++ man ++ html ++ jlineDepMap ++ Seq(
          lib           -> "lib/scala-library.jar",
          comp          -> "lib/scala-compiler.jar",
          jline         -> "lib/jline.jar",
          continuations -> "misc/scala-devel/plugins/continuations.jar",
          scalap        -> "lib/scalap.jar"
        ) toMap
    },
    // Add in some more dependencies
    makeDistMappings <<= (makeDistMappings, 
                          packageBin in swing in Compile,
                          packageBin in dbc in Compile) map {
      (dist, s, d) =>
        dist ++ Seq(s -> "lib/scala-swing.jar", d -> "lib/scala-dbc.jar")
    },
    makeDist <<= (makeDistMappings, baseDirectory, streams) map { (maps, dir, s) => 
      s.log.debug("Map = " + maps.mkString("\n")) 
      val file = dir / "target" / "scala-dist.zip"
      IO.zip(maps, file)
      s.log.info("Created " + file.getAbsolutePath)
      file
    },
    makeExplodedDist <<= (makeDistMappings, target, streams) map { (maps, dir, s) => 
      def sameFile(f: File, f2: File) = f.getCanonicalPath == f2.getCanonicalPath
      IO.createDirectory(dir)
      IO.copy(for {
       (file, name) <- maps
       val file2 = dir / name
       if !sameFile(file,file2)
      } yield (file, file2))
      // Hack to make binaries be executable.  TODO - Fix for JDK 5 and below...
      maps.values filter (_ startsWith "bin/") foreach (dir / _ setExecutable true)
      dir
    }
  )
  lazy val scaladist = (
    Project("dist", file("."))
    settings (scalaDistSettings: _*)
  )
}

/** Matcher to make updated remote project references easier. */
object ScopedExternalSetting {
  def unapply[T](s: Setting[_]): Option[(URI, AttributeKey[_], Setting[_])] =
    s.key.scope.project match {
      case Select(p @ ProjectRef(uri, _)) => Some((uri, s.key.key, s))
      case _                              => None
    }
}