retrieve to build, update-classifiers action

set retrieve := true to have dependencies retrieved to the build
the location is by default shared by all projects in a build
  (<built-root>/lib_managed/), but can be per-project
update-classifiers and update-sbt-classifiers retrieves artifacts with classifiers
  for project dependencies and for sbt, respectively
The default setting is classifiers := Seq("javadoc", "sources")
This commit is contained in:
Mark Harrah 2011-03-15 20:35:43 -04:00
parent 39a6475b2e
commit 4e55503d64
4 changed files with 100 additions and 29 deletions

View File

@ -283,13 +283,16 @@ private object IvySbt
configurations.foreach(artifact.addConfiguration)
artifact
}
private def extra(artifact: Artifact) =
private[sbt] def extra(artifact: Artifact, unqualify: Boolean = false): java.util.Map[String, String] =
{
val ea = artifact.classifier match { case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact }
javaMap(ea.extraAttributes)
javaMap(ea.extraAttributes, unqualify)
}
private def javaMap(map: Map[String,String]) =
private[sbt] def javaMap(m: Map[String,String], unqualify: Boolean = false) =
{
val map = m map { case (k, v) => (k.stripPrefix("e:"), v) }
if(map.isEmpty) null else scala.collection.JavaConversions.asJavaMap(map)
}
private object javaMap
{

View File

@ -23,8 +23,8 @@ import plugins.parser.m2.{PomModuleDescriptorParser,PomModuleDescriptorWriter}
final class PublishPatterns(val deliverIvyPattern: Option[String], val srcArtifactPatterns: Seq[String])
final class PublishConfiguration(val patterns: PublishPatterns, val status: String, val resolverName: String, val configurations: Option[Seq[Configuration]], val logging: UpdateLogging.Value)
final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val logging: UpdateLogging.Value)
final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String, val synchronize: Boolean)
final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value)
final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String)
final class MakePomConfiguration(val file: File, val configurations: Option[Iterable[Configuration]] = None, val extra: NodeSeq = NodeSeq.Empty, val process: Node => Node = n => n, val filterRepositories: MavenRepository => Boolean = _ => true)
/** Configures logging during an 'update'. `level` determines the amount of other information logged.
@ -81,11 +81,7 @@ object IvyActions
ivy.deliver(revID, revID.getRevision, getDeliverIvyPattern(patterns), options)
}
}
// because Ivy.deliver does not provide the delivered File location, we duplicate the logic here
def deliverFile(module: IvySbt#Module, configuration: PublishConfiguration): File =
module.withModule { (ivy,md,_) =>
ivy.getSettings.resolveFile(IvyPatternHelper.substitute(getDeliverIvyPattern(configuration.patterns), md.getResolvedModuleRevisionId))
}
def getDeliverIvyPattern(patterns: PublishPatterns) = patterns.deliverIvyPattern.getOrElse(error("No Ivy pattern specified"))
// todo: map configurations, extra dependencies
@ -106,21 +102,72 @@ object IvyActions
/** Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration.
* 'updateConfig' configures the actual resolution and retrieval process. */
def update(module: IvySbt#Module, configuration: UpdateConfiguration): UpdateReport =
{
module.withModule { case (ivy, md, default) =>
import configuration.{retrieve => rConf, logging}
val report = resolve(logging)(ivy, md, default)
IvyRetrieve.updateReport(report)
val (report, err) = resolve(configuration.logging)(ivy, md, default)
err match
{
case Some(x) if !configuration.missingOk => throw x
case _ =>
val uReport = IvyRetrieve updateReport report
configuration.retrieve match
{
case Some(rConf) => retrieve(ivy, uReport, rConf)
case None => uReport
}
}
}
def transitiveScratch(ivySbt: IvySbt, id: ModuleID, label: String, deps: Seq[ModuleID], classifiers: Seq[String], c: UpdateConfiguration, ivyScala: Option[IvyScala]): UpdateReport =
{
val base = id.copy(name = id.name + "$" + label)
val module = new ivySbt.Module(InlineConfiguration(base, deps).copy(ivyScala = ivyScala))
val report = update(module, c)
transitive(ivySbt, id, report, classifiers, c, ivyScala)
}
private def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): ResolveReport =
def transitive(ivySbt: IvySbt, module: ModuleID, report: UpdateReport, classifiers: Seq[String], c: UpdateConfiguration, ivyScala: Option[IvyScala]): UpdateReport =
updateClassifiers(ivySbt, module, report.allModules, classifiers, new UpdateConfiguration(c.retrieve, true, c.logging), ivyScala)
def updateClassifiers(ivySbt: IvySbt, id: ModuleID, modules: Seq[ModuleID], classifiers: Seq[String], configuration: UpdateConfiguration, ivyScala: Option[IvyScala]): UpdateReport =
{
assert(!classifiers.isEmpty, "classifiers cannot be empty")
val baseModules = modules map { m => ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion) }
val deps = baseModules.distinct map { m => m.copy(explicitArtifacts = classifiers map { c => Artifact(m.name, c) }) }
val base = id.copy(name = id.name + classifiers.mkString("$","_",""))
val module = new ivySbt.Module(InlineConfiguration(base, deps).copy(ivyScala = ivyScala))
update(module, configuration)
}
private def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) =
{
val resolveOptions = new ResolveOptions
resolveOptions.setLog(ivyLogLevel(logging))
val resolveReport = ivy.resolve(module, resolveOptions)
if(resolveReport.hasError)
throw new ResolveException(resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct)
resolveReport
val err =
if(resolveReport.hasError)
Some(new ResolveException(resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct))
else None
(resolveReport, err)
}
private def retrieve(ivy: Ivy, report: UpdateReport, config: RetrieveConfiguration): UpdateReport =
retrieve(ivy, report, config.retrieveDirectory, config.outputPattern)
private def retrieve(ivy: Ivy, report: UpdateReport, base: File, pattern: String): UpdateReport =
{
val toCopy = new collection.mutable.HashSet[(File,File)]
val retReport = report retrieve { (conf, mid, art, cached) =>
val to = retrieveTarget(conf, mid, art, base, pattern)
toCopy += ((cached, to))
to
}
IO.copy( toCopy )
retReport
}
private def retrieveTarget(conf: String, mid: ModuleID, art: Artifact, base: File, pattern: String): File =
new File(base, substitute(conf, mid, art, pattern))
private def substitute(conf: String, mid: ModuleID, art: Artifact, pattern: String): String =
{
val mextra = IvySbt.javaMap(mid.extraAttributes, true)
val aextra = IvySbt.extra(art, true)
IvyPatternHelper.substitute(pattern, mid.organization, mid.name, mid.revision, art.name, art.`type`, art.extension, conf, mextra, aextra)
}
import UpdateLogging.{Quiet, Full, DownloadOnly}

View File

@ -11,7 +11,7 @@ import org.apache.ivy.util.url.CredentialsStore
final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, explicitArtifacts: Seq[Artifact] = Nil, extraAttributes: Map[String,String] = Map.empty, crossVersion: Boolean = false)
{
override def toString = organization + ":" + name + ":" + revision
override def toString = organization + ":" + name + ":" + revision + (configurations match { case Some(s) => ":" + s; case None => "" })
def cross(v: Boolean) = copy(crossVersion = v)
// () required for chaining
def notTransitive() = intransitive()

View File

@ -4,6 +4,7 @@
package sbt
import java.io.File
import collection.mutable
import org.apache.ivy.core.{module, report}
import module.descriptor.{Artifact => IvyArtifact}
@ -16,7 +17,7 @@ object IvyRetrieve
( for( conf <- report.getConfigurations) yield (conf, report.getConfigurationReport(conf)) ).toMap
def moduleReports(confReport: ConfigurationResolveReport): Map[ModuleID, ModuleReport] =
moduleReportMap(confReport) map { case (mid, arts) => (mid, new ModuleReport(mid, artifactReports(arts)) ) }
moduleReportMap(confReport) map { case (mid, arts) => (mid, artifactReports(mid, arts) ) }
def moduleReportMap(confReport: ConfigurationResolveReport): Map[ModuleID, Seq[ArtifactDownloadReport]] =
{
@ -25,13 +26,20 @@ object IvyRetrieve
(toModuleID(revId), (confReport getDownloadReports revId).toSeq)
modules.toMap
}
def artifactReports(artReport: Seq[ArtifactDownloadReport]): Map[Artifact, File] =
artReport map { r =>
val art = r.getArtifact
val file0 = r.getLocalFile
val file = if(file0 eq null) error("No file for " + art) else file0
(toArtifact(art), file)
} toMap;
def artifactReports(mid: ModuleID, artReport: Seq[ArtifactDownloadReport]): ModuleReport =
{
val missing = new mutable.ListBuffer[Artifact]
val resolved = new mutable.ListBuffer[(Artifact,File)]
for(r <- artReport) {
val file = r.getLocalFile
val art = toArtifact(r.getArtifact)
if(file eq null)
missing += art
else
resolved += ((art,file))
}
new ModuleReport(mid, resolved.toMap, missing.toSet)
}
def toModuleID(revID: ModuleRevisionId): ModuleID =
ModuleID(revID.getOrganisation, revID.getName, revID.getRevision)
@ -52,12 +60,25 @@ object IvyRetrieve
final class UpdateReport(val configurations: Map[String, ConfigurationReport])
{
override def toString = "Update report:\n" + configurations.values.mkString
def allModules: Seq[ModuleID] = configurations.values.toSeq.flatMap(_.allModules).distinct
def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport =
new UpdateReport(configurations map { case (k,v) => (k, v retrieve f)} )
}
final class ConfigurationReport(val configuration: String, val modules: Map[ModuleID, ModuleReport])
{
override def toString = "\t" + configuration + ":\n" + modules.values.mkString
def allModules: Seq[ModuleID] = modules.keys.toSeq
def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport =
new ConfigurationReport(configuration, modules map { case (k,v) => (k, v.retrieve( (mid,art,file) => f(configuration, mid, art, file)) ) })
}
final class ModuleReport(val module: ModuleID, val artifacts: Map[Artifact, File])
final class ModuleReport(val module: ModuleID, val artifacts: Map[Artifact, File], val missingArtifacts: Set[Artifact])
{
override def toString = "\t\t" + module + ": " + (if(artifacts.size <= 1) "" else "\n") + artifacts.mkString("\n\t\t\t") + "\n"
override def toString =
{
val arts = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art)
"\t\t" + module + ": " +
(if(arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n"
}
def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport =
new ModuleReport(module, artifacts.map { case (art,file) => (art, f(module, art, file)) }, missingArtifacts)
}