{publish,deliver}{,-local} and package tasks

This commit is contained in:
Mark Harrah 2010-12-02 20:22:18 -05:00
parent 8df5cbabf5
commit e68f133c7f
4 changed files with 216 additions and 22 deletions

View File

@ -8,9 +8,9 @@ package sbt
import TaskExtra._
import ClasspathProject._
import java.io.File
import Path._
import Path._
import Types._
import scala.xml.NodeSeq
import scala.xml.{Node => XNode,NodeSeq}
import scala.collection.mutable.{LinkedHashMap, LinkedHashSet}
trait ClasspathProject
@ -55,7 +55,6 @@ trait BasicClasspathProject extends ClasspathProject
val unmanagedBase: Task[File]
def cacheDirectory: File
val updateConfig: Task[UpdateConfiguration]
lazy val ivySbt: Task[IvySbt] =
@ -84,9 +83,26 @@ trait BasicClasspathProject extends ClasspathProject
lazy val update = (ivyModule, updateConfig) map cachedUpdate(cacheDirectory / "update", configurationMap)
}
trait DefaultClasspathProject extends BasicClasspathProject with Project
trait PublishProject extends BasicClasspathProject
{
val publishConfig: Task[PublishConfiguration]
val publishLocalConfig: Task[PublishConfiguration]
val makePomConfig: Task[MakePomConfiguration]
def packageToPublish: Seq[Task[_]]
def publishMavenStyle = true
def deliverDepends = if(publishMavenStyle) makePom :: Nil else packageToPublish
lazy val makePom = (ivyModule, makePomConfig) map(IvyActions.makePom) dependsOn( packageToPublish : _*)
lazy val deliver = (ivyModule, publishConfig) map cachedPublish(cacheDirectory / "deliver")(IvyActions.deliver) dependsOn(deliverDepends : _*)
lazy val deliverLocal = (ivyModule, publishLocalConfig) map cachedPublish(cacheDirectory / "deliver-local")(IvyActions.deliver) dependsOn(deliverDepends : _*)
lazy val publish = (ivyModule, publishConfig) map cachedPublish(cacheDirectory / "publish")(IvyActions.publish) dependsOn(deliver)
lazy val publishLocal = (ivyModule, publishLocalConfig) map cachedPublish(cacheDirectory / "publish-local")(IvyActions.publish) dependsOn(deliverLocal)
}
trait DefaultClasspathProject extends BasicClasspathProject with PublishProject with Project
{
def outputDirectory: Path
def projectID: ModuleID
def baseResolvers: Seq[Resolver]
lazy val resolvers: Task[Seq[Resolver]] = task { baseResolvers }
@ -119,6 +135,10 @@ trait DefaultClasspathProject extends BasicClasspathProject with Project
def defaultConfiguration: Option[Configuration] = None
def ivyScala: Option[IvyScala] = None
def ivyValidate: Boolean = false
def moduleID = normalizedName
def pomFile: File
def publishTo: Resolver = error("Repository for publishing is not specified.")
lazy val internalDependencyClasspath: Classpath = internalDependencies(this)
@ -127,6 +147,10 @@ trait DefaultClasspathProject extends BasicClasspathProject with Project
lazy val moduleSettings: Task[ModuleSettings] = task {
new InlineConfiguration(projectID, libraryDependencies, ivyXML, configurations, defaultConfiguration, ivyScala, ivyValidate)
}
lazy val publishConfig = task { publishConfiguration( publishPatterns(outputDirectory), resolverName = publishTo.name ) }
lazy val publishLocalConfig = task { publishConfiguration( publishPatterns(outputDirectory, true) ) }
lazy val makePomConfig = task { makePomConfiguration(pomFile) }
}
trait MultiClasspathProject extends DefaultClasspathProject
{
@ -134,6 +158,8 @@ trait MultiClasspathProject extends DefaultClasspathProject
def name: String
def organization: String
def version: String
def pomFile: File = outputDirectory / (moduleID + "-" + version + ".pom")
def artifacts: Seq[Artifact] = Nil
def projectDependencies: Seq[ModuleID] =
resolvedDependencies(this) collect { case (p: DefaultClasspathProject, conf) => p.projectID.copy(configurations = conf) }
@ -143,7 +169,7 @@ trait MultiClasspathProject extends DefaultClasspathProject
new RawRepository(new ProjectResolver("inter-project", m))
}
override def projectID = ModuleID(organization, name, version)
override def projectID = ModuleID(organization, moduleID, version).cross(true).artifacts(artifacts.toSeq : _*)
override def libraryDependencies: Seq[ModuleID] = super.libraryDependencies ++ projectDependencies
override lazy val resolvers: Task[Seq[Resolver]] = projectResolver map { _ +: baseResolvers }
@ -274,10 +300,29 @@ object ClasspathProject
case _ => Configurations.Default
}
def makePomConfiguration(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 publishConfiguration(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)
}
import Cache._
import Types._
import CacheIvy.{classpathFormat, updateIC}
import CacheIvy.{classpathFormat, publishIC, updateIC}
def cachedUpdate(cacheFile: File, configMap: Map[String, Configuration]): (IvySbt#Module :+: UpdateConfiguration :+: HNil) => Map[Configuration, Seq[File]] =
{ case module :+: config :+: HNil =>
@ -293,4 +338,13 @@ object ClasspathProject
classpaths map { case (key, value) => (confMap(key), value) } toMap;
}
// 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): (IvySbt#Module :+: PublishConfiguration :+: HNil) => 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)*/
}
}

View File

@ -22,10 +22,10 @@ class DefaultProject(val info: ProjectInfo) extends BasicProject
}
trait IntegrationTest extends BasicProject
{
override def productsTask(conf: Configuration): Task[Seq[Attributed[File]]] =
override def directoryProductsTask(conf: Configuration): Task[Seq[Attributed[File]]] =
conf match {
case ITestConfig => makeProducts(integrationTestCompile.compile, integrationTestCompile.compileInputs, name, "it-")
case _ => super.productsTask(conf)
case _ => super.directoryProductsTask(conf)
}
override def configurations: Seq[Configuration] = super.configurations :+ Configurations.IntegrationTest
@ -39,6 +39,9 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
// easier to demo for now
override def organization = "org.example"
override def version = "1.0"
def artifactID = normalizedName
override def artifacts: Seq[Artifact] = Artifact(artifactID) :: pomArtifact
def pomArtifact = (if(publishMavenStyle) Artifact(artifactID, "pom", "pom") :: Nil else Nil)
override def watchPaths: PathFinder = (info.projectDirectory: Path) * sourceFilter +++ descendents("src","*")
@ -46,6 +49,7 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
def scalacOptions: Seq[String] = Nil
def consoleOptions: Seq[String] = scalacOptions
def initialCommands: String = ""
def maximumErrors: Int = 100
def outputDirectory = "target": Path
def cacheDirectory = outputDirectory / "cache"
@ -53,21 +57,53 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
def testResources = descendents("src" / "test" / "resources" ###, "*")
def classesDirectory(configuration: Configuration): File =
configuration match {
case CompileConfig => outputDirectory / "classes"
case c => outputDirectory / (c.name + "-classes")
}
outputDirectory / (configString(configuration, "", "-") + "classes")
lazy val products: Classpath = TaskMap(productsTask)
def packageToPublish: Seq[Task[_]] = configurations map packages.apply
// TODO: include resources, perhaps handle jars v. directories
def productsTask(conf: Configuration): Task[Seq[Attributed[File]]] =
lazy val products: Classpath = directoryProducts //TaskMap(productsTask)
lazy val directoryProducts = TaskMap(directoryProductsTask)
lazy val packages = TaskMap(packageTask)
lazy val pkgMainClass = TaskMap(mainClassTask)
lazy val jarPath = TaskMap(jarPathTask)
lazy val `package` = packages(CompileConfig)
lazy val testPackage = packages(TestConfig)
def directoryProductsTask(conf: Configuration): Task[Seq[Attributed[File]]] =
conf match {
case CompileConfig | DefaultConfig => makeProducts(compile.compile, compile.compileInputs, name, "")
case TestConfig => makeProducts(testCompile.compile, testCompile.compileInputs, name, "test-")
case x => task { Nil }
}
def mainClassesTask(conf: Configuration): Task[Seq[String]] = conf match {
case CompileConfig => discoveredMainClasses
case TestConfig => test.testDiscoveredMainClasses
case _ => task { Nil }
}
def mainClassTask(conf: Configuration): Task[Option[String]] = mainClassesTask(conf) map { classes => SelectMainClass(None, classes) }
def configString(conf: Configuration, pre: String, post: String): String = conf match {
case CompileConfig | DefaultConfig => ""
case _ => pre + conf.name + post
}
def jarPathTask(conf: Configuration): Task[File] = task { outputDirectory / jarName(conf) }
def jarName(conf: Configuration): String = artifactID + "-" + version + configString(conf, "-", "") + ".jar"
def packageConfigTask(conf: Configuration): Task[Package.Configuration] =
pkgMainClass(conf) :^: directoryProductsTask(conf) :^: jarPath(conf) :^: KNil map { case main :+: in :+: jar :+: HNil =>
val srcs = data(in) flatMap { dir => descendents(dir ###, "*").xx }
new Package.Configuration(srcs, jar, main.map(Package.MainClass.apply).toList)
}
def packageTask(conf: Configuration): Task[Seq[Attributed[File]]] =
streams :^: packageConfigTask(conf) :^: KNil map { case s :+: config :+: HNil =>
Package(config, cacheDirectory / conf.name / "package", s.log)
List(config.jar)
}
lazy val buildScalaVersions: Task[String] = task { info.app.scalaProvider.version }//cross(MultiProject.ScalaVersion)(info.app.scalaProvider.version)
lazy val buildScalaInstance: Task[ScalaInstance] =
buildScalaVersions map { version => ScalaInstance(version, info.app.scalaProvider) }
@ -78,9 +114,6 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
lazy val discoveredMainClasses: Task[Seq[String]] =
discoverMain map { _ collect { case (definition, discovered) if(discovered.hasMain) => definition.name } }
lazy val pkgMainClass: Task[Option[String]] =
discoveredMainClasses map { classes => SelectMainClass(None, classes) }
lazy val runner: Task[ScalaRun] =
buildScalaInstance map { si => new Run(si) }
@ -108,7 +141,7 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
lazy val clean = task { IO.delete(outputDirectory) }
// lazy val doc, test-only, test-quick, test-failed, publish(-local), deliver(-local), make-pom, package-*, javap, copy-resources
// lazy val test-only, test-quick, test-failed, package-src, package-test, package-doc, javap
lazy val set = input map { in =>
val Seq(name, value) = in.splitArgs.take(2)
@ -138,7 +171,7 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
val classpath = classes +: data(cp)
val analysis = analysisMap(cp)
val cache = cacheDirectory / "compile" / configuration.toString
Compile.inputs(classpath, sources.getFiles.toSeq, classes, scalacOptions, javacOptions, allBases.getFiles.toSeq, analysis, cache, 100)(compilers, log)
Compile.inputs(classpath, sources.getFiles.toSeq, classes, scalacOptions, javacOptions, allBases.getFiles.toSeq, analysis, cache, maximumErrors)(compilers, log)
}
}
@ -239,3 +272,9 @@ class TestTasks(val prefix: Option[String], val project: ClasspathProject with P
}
lazy val test = (project.streams, executeTests) map { case s :+: results :+: HNil => Test.showResults(s.log, results) }
}
/*class PackageTasks
/* def zipTask(sources: PathFinder, outputDirectory: Path, zipName: => String): Task =
zipTask(sources, outputDirectory / zipName)
def zipTask(sources: PathFinder, zipPath: => Path): Task =
fileTask("zip", zipPath from sources) { FileUtilities.zip(sources.get, zipPath, false, log) }*/
}*/

View File

@ -167,6 +167,7 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named
def settings: Settings = Settings.empty
def name: String = info.name getOrElse error("'name' not overridden")
def normalizedName: String = StringUtilities.normalize(name)
def base = info.projectDirectory
def outputRootPath = base / "target"

100
main/Package.scala Normal file
View File

@ -0,0 +1,100 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Predef.{conforms => _, _}
import java.io.File
import java.util.jar.{Attributes, Manifest}
import collection.JavaConversions._
import Types.:+:
import Path._
import sbinary.{DefaultProtocol,Format}
import DefaultProtocol.{FileFormat, immutableMapFormat, StringFormat, UnitFormat}
import Cache.{defaultEquiv, hConsCache, hNilCache, streamFormat, wrapIn}
import Tracked.{inputChanged, outputChanged}
import FileInfo.{exists, existsInputCache}
import FilesInfo.lastModified
import lastModified.infosInputCache
sealed trait PackageOption
object Package
{
final case class JarManifest(m: Manifest) extends PackageOption
{
assert(m != null)
}
final case class MainClass(mainClassName: String) extends PackageOption
final case class ManifestAttributes(attributes: (Attributes.Name, String)*) extends PackageOption
def ManifestAttributes(attributes: (String, String)*): ManifestAttributes =
{
val converted = for( (name,value) <- attributes ) yield (new Attributes.Name(name), value)
new ManifestAttributes(converted : _*)
}
def mergeAttributes(a1: Attributes, a2: Attributes) = a1 ++= a2
// merges m2 into m1 (mutating m1 in the process)
def mergeManifests(manifest: Manifest, mergeManifest: Manifest)
{
mergeAttributes(manifest.getMainAttributes, mergeManifest.getMainAttributes)
val entryMap = asScalaMap(manifest.getEntries)
for((key, value) <- mergeManifest.getEntries)
{
entryMap.get(key) match
{
case Some(attributes) => mergeAttributes(attributes, value)
case None => entryMap put (key, value)
}
}
}
final class Configuration(val sources: Seq[(File, String)], val jar: File, val options: Seq[PackageOption])
def apply(conf: Configuration, cacheFile: File, log: Logger)
{
import conf._
val manifest = new Manifest
val main = manifest.getMainAttributes
for(option <- options)
{
option match
{
case JarManifest(mergeManifest) => mergeManifests(manifest, mergeManifest)
case MainClass(mainClassName) => main.put(Attributes.Name.MAIN_CLASS, mainClassName)
case ManifestAttributes(attributes @ _*) => main ++= attributes
case _ => log.warn("Ignored unknown package option " + option)
}
}
setVersion(main)
val cachedMakeJar = inputChanged(cacheFile / "inputs") { (inChanged, inputs: Map[File, String] :+: FilesInfo[ModifiedFileInfo] :+: Manifest :+: HNil) =>
val sources :+: _ :+: manifest :+: HNil = inputs
outputChanged(cacheFile / "output") { (outChanged, jar: PlainFileInfo) =>
if(inChanged || outChanged)
makeJar(sources.toSeq, jar.file, manifest)
}
}
val map = conf.sources.toMap
val inputs = map :+: lastModified(map.keySet.toSet) :+: manifest :+: HNil
cachedMakeJar(inputs)(() => exists(conf.jar))
}
def setVersion(main: Attributes)
{
val version = Attributes.Name.MANIFEST_VERSION
if(main.getValue(version) eq null)
main.put(version, "1.0")
}
def makeJar(sources: Seq[(File, String)], jar: File, manifest: Manifest)
{
println("Packaging...")
IO.delete(jar)
IO.jar(sources, jar, manifest)
println("Done packaging.")
}
implicit def manifestEquiv: Equiv[Manifest] = defaultEquiv
implicit def manifestFormat: Format[Manifest] = streamFormat( _ write _, in => new Manifest(in))
implicit def stringMapEquiv: Equiv[Map[File, String]] = defaultEquiv
}