diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 68c7d4e561..76d5dd1705 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -24,10 +24,11 @@ object AkkaBuild extends Build { lazy val akka = Project( id = "akka", base = file("."), - settings = parentSettings ++ Release.settings ++ Unidoc.settings ++ Rstdoc.settings ++ Publish.versionSettings ++ Seq( + settings = parentSettings ++ Release.settings ++ Unidoc.settings ++ Rstdoc.settings ++ Publish.versionSettings ++ Dist.settings ++ Seq( parallelExecution in GlobalScope := false, Publish.defaultPublishTo in ThisBuild <<= crossTarget / "repository", - Unidoc.unidocExclude := Seq(samples.id, tutorials.id) + Unidoc.unidocExclude := Seq(samples.id, tutorials.id), + Dist.distExclude := Seq(actorTests.id, akkaSbtPlugin.id, docs.id) ), aggregate = Seq(actor, testkit, actorTests, stm, remote, slf4j, amqp, mailboxes, akkaSbtPlugin, samples, tutorials, docs) ) diff --git a/project/Dist.scala b/project/Dist.scala new file mode 100644 index 0000000000..1e3e7a2245 --- /dev/null +++ b/project/Dist.scala @@ -0,0 +1,98 @@ +package akka + +import sbt._ +import sbt.Keys._ +import sbt.classpath.ClasspathUtilities +import sbt.Project.Initialize +import java.io.File + +object Dist { + case class DistSources(depJars: Seq[File], libJars: Seq[File], srcJars: Seq[File], docJars: Seq[File], api: File, docs: File) + + val distExclude = SettingKey[Seq[String]]("dist-exclude") + val distAllClasspaths = TaskKey[Seq[Classpath]]("dist-all-classpaths") + val distDependencies = TaskKey[Seq[File]]("dist-dependencies") + val distLibJars = TaskKey[Seq[File]]("dist-lib-jars") + val distSrcJars = TaskKey[Seq[File]]("dist-src-jars") + val distDocJars = TaskKey[Seq[File]]("dist-doc-jars") + val distSources = TaskKey[DistSources]("dist-sources") + val distDirectory = SettingKey[File]("dist-directory") + val distUnzipped = SettingKey[File]("dist-unzipped") + val distFile = SettingKey[File]("dist-file") + val dist = TaskKey[File]("dist", "Create a zipped distribution of everything.") + + lazy val settings: Seq[Setting[_]] = Seq( + distExclude := Seq.empty, + distAllClasspaths <<= (thisProjectRef, buildStructure, distExclude) flatMap aggregated(dependencyClasspath.task in Compile), + distDependencies <<= distAllClasspaths map { _.flatten.map(_.data).filter(ClasspathUtilities.isArchive).distinct }, + distLibJars <<= (thisProjectRef, buildStructure, distExclude) flatMap aggregated(packageBin.task in Compile), + distSrcJars <<= (thisProjectRef, buildStructure, distExclude) flatMap aggregated(packageSrc.task in Compile), + distDocJars <<= (thisProjectRef, buildStructure, distExclude) flatMap aggregated(packageDoc.task in Compile), + distSources <<= (distDependencies, distLibJars, distSrcJars, distDocJars, Unidoc.unidoc, Rstdoc.rstdoc) map DistSources, + distDirectory <<= crossTarget / "dist", + distUnzipped <<= distDirectory / "unzipped", + distFile <<= (distDirectory, version) { (dir, v) => dir / ("akka-" + v + ".zip") }, + dist <<= distTask + ) + + def aggregated[T](task: SettingKey[Task[T]])(projectRef: ProjectRef, structure: Load.BuildStructure, exclude: Seq[String]): Task[Seq[T]] = { + val projects = aggregatedProjects(projectRef, structure, exclude) + projects flatMap { task in LocalProject(_) get structure.data } join + } + + def aggregatedProjects(projectRef: ProjectRef, structure: Load.BuildStructure, exclude: Seq[String]): Seq[String] = { + val aggregate = Project.getProject(projectRef, structure).toSeq.flatMap(_.aggregate) + aggregate flatMap { ref => + if (exclude contains ref.project) Seq.empty + else ref.project +: aggregatedProjects(ref, structure, exclude) + } + } + + def distTask: Initialize[Task[File]] = { + (baseDirectory, distSources, distUnzipped, version, distFile, streams) map { + (projectBase, allSources, unzipped, version, zipFile, s) => { + val base = unzipped / ("akka-" + version) + val scripts = (projectBase / "scripts" / "microkernel" * "*").get + val bin = base / "bin" + val configSources = projectBase / "config" + val config = base / "config" + val doc = base / "doc" / "akka" + val api = doc / "api" + val docs = doc / "docs" + val docJars = doc / "jars" + val libs = allSources.depJars ++ allSources.libJars + val (scalaLibs, akkaLibs) = libs partition (_.name.contains("scala-library")) + val lib = base / "lib" + val libAkka = lib / "akka" + val src = base / "src" / "akka" + IO.delete(unzipped) + //createStructure(doc, docJars, lib, src) + copyFilesTo(scripts, bin, setExecutable = true) + IO.copyDirectory(configSources, config) + IO.copyDirectory(allSources.api, api) + IO.copyDirectory(allSources.docs, docs) + copyFilesTo(allSources.docJars, docJars) + copyFilesTo(scalaLibs, lib) + copyFilesTo(akkaLibs, libAkka) + copyFilesTo(allSources.srcJars, src) + val files = unzipped ** -DirectoryFilter + val sources = files x relativeTo(unzipped) + IO.zip(sources, zipFile) + zipFile + } + } + } + + def createStructure(dirs: File*): Unit = { + dirs foreach IO.createDirectory + } + + def copyFilesTo(files: Seq[File], dir: File, setExecutable: Boolean = false): Unit = { + IO.createDirectory(dir) + for (file <- files) { + val target = dir / file.name + IO.copyFile(file, target) + if (setExecutable) target.setExecutable(file.canExecute, false) + } + } +} diff --git a/project/Release.scala b/project/Release.scala index 6b6f5643bc..e30a5663ce 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -24,11 +24,13 @@ object Release { val state1 = extracted.runAggregated(publish in projectRef, state) val (state2, api) = extracted.runTask(Unidoc.unidoc, state1) val (state3, docs) = extracted.runTask(Rstdoc.rstdoc, state2) + val (state4, dist) = extracted.runTask(Dist.dist, state3) IO.delete(release) IO.createDirectory(release) IO.copyDirectory(repo, release / "releases") IO.copyDirectory(api, release / "api" / "akka" / releaseVersion) IO.copyDirectory(docs, release / "docs" / "akka" / releaseVersion) - state3 + IO.copyFile(dist, release / "downloads" / dist.name) + state4 } } diff --git a/project/Rstdoc.scala b/project/Rstdoc.scala index fb38f756ac..12c4ad72d5 100644 --- a/project/Rstdoc.scala +++ b/project/Rstdoc.scala @@ -15,19 +15,30 @@ object Rstdoc { rstdoc <<= rstdocTask ) - def rstdocTask = (rstdocDirectory, rstdocTarget, streams) map { - (dir, target, s) => { - s.log.info("Building reStructuredText documentation...") - val logger = new ProcessLogger { - def info(o: => String): Unit = s.log.debug(o) - def error(e: => String): Unit = s.log.debug(e) - def buffer[T](f: => T): T = f + def rstdocTask = (cacheDirectory, rstdocDirectory, rstdocTarget, streams) map { + (cacheDir, dir, target, s) => { + val cache = cacheDir / "rstdoc" + val cached = FileFunction.cached(cache)(FilesInfo.hash, FilesInfo.exists) { (in, out) => + val changes = in.modified + if (!changes.isEmpty) { + s.log.info("Building reStructuredText documentation...") + changes.foreach(file => s.log.debug("Changed documentation source: " + file)) + val logger = new ProcessLogger { + def info(o: => String): Unit = s.log.debug(o) + def error(e: => String): Unit = s.log.debug(e) + def buffer[T](f: => T): T = f + } + val exitCode = Process(List("make", "clean", "html", "pdf"), dir) ! logger + if (exitCode != 0) sys.error("Failed to build docs.") + s.log.info("Creating reStructuredText documentation successful.") + IO.copyDirectory(dir / "_build" / "html", target) + IO.copyFile(dir / "_build" / "latex" / "Akka.pdf", target / "Akka.pdf") + target.descendentsExcept("*", "").get.toSet + } else Set.empty } - val exitCode = Process(List("make", "clean", "html", "pdf"), dir) ! logger - if (exitCode != 0) sys.error("Failed to build docs.") - s.log.info("Creating reStructuredText documentation successful.") - IO.copyDirectory(dir / "_build" / "html", target) - IO.copyFile(dir / "_build" / "latex" / "Akka.pdf", target / "Akka.pdf") + val toplevel = dir * ("*" - ".*" - "_sphinx" - "_build" - "disabled" - "target") + val inputs = toplevel.descendentsExcept("*", "").get.toSet + cached(inputs) target } }