Merge pull request #1454 from sbt/wip/resolve-consolidation

Consolidated resolution
This commit is contained in:
eugene yokota 2014-07-24 02:13:01 -04:00
commit 5b070b9dcc
14 changed files with 308 additions and 21 deletions

View File

@ -4,6 +4,7 @@
package sbt package sbt
import Resolver.PluginPattern import Resolver.PluginPattern
import ivyint.{ ConsolidatedResolveEngine, ConsolidatedResolveCache }
import java.io.File import java.io.File
import java.net.URI import java.net.URI
@ -14,12 +15,14 @@ import CS.singleton
import org.apache.ivy.Ivy import org.apache.ivy.Ivy
import org.apache.ivy.core.{ IvyPatternHelper, LogOptions } import org.apache.ivy.core.{ IvyPatternHelper, LogOptions }
import org.apache.ivy.core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter } import org.apache.ivy.core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter }
import org.apache.ivy.core.event.EventManager
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact } import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact }
import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License } import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License }
import org.apache.ivy.core.module.descriptor.{ OverrideDependencyDescriptorMediator } import org.apache.ivy.core.module.descriptor.{ OverrideDependencyDescriptorMediator }
import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId }
import org.apache.ivy.core.resolve.{ IvyNode, ResolveData, ResolvedModuleRevision } import org.apache.ivy.core.resolve.{ IvyNode, ResolveData, ResolvedModuleRevision, ResolveEngine }
import org.apache.ivy.core.settings.IvySettings import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.core.sort.SortEngine
import org.apache.ivy.plugins.latest.LatestRevisionStrategy import org.apache.ivy.plugins.latest.LatestRevisionStrategy
import org.apache.ivy.plugins.matcher.PatternMatcher import org.apache.ivy.plugins.matcher.PatternMatcher
import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser
@ -28,6 +31,7 @@ import org.apache.ivy.util.{ Message, MessageLogger }
import org.apache.ivy.util.extendable.ExtendableItem import org.apache.ivy.util.extendable.ExtendableItem
import scala.xml.{ NodeSeq, Text } import scala.xml.{ NodeSeq, Text }
import scala.collection.mutable
final class IvySbt(val configuration: IvyConfiguration) { final class IvySbt(val configuration: IvyConfiguration) {
import configuration.baseDirectory import configuration.baseDirectory
@ -76,7 +80,23 @@ final class IvySbt(val configuration: IvyConfiguration) {
} }
private lazy val ivy: Ivy = private lazy val ivy: Ivy =
{ {
val i = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine } val i = new Ivy() {
private val loggerEngine = new SbtMessageLoggerEngine
override def getLoggerEngine = loggerEngine
override def bind(): Unit = {
val prOpt = Option(getSettings.getResolver(ProjectResolver.InterProject)) map { case pr: ProjectResolver => pr }
// We inject the deps we need before we can hook our resolve engine.
setSortEngine(new SortEngine(getSettings))
setEventManager(new EventManager())
if (configuration.updateOptions.consolidatedResolution) {
setResolveEngine(new ResolveEngine(getSettings, getEventManager, getSortEngine) with ConsolidatedResolveEngine {
val consolidatedResolveCache = IvySbt.consolidatedResolveCache
val projectResolver = prOpt
})
} else setResolveEngine(new ResolveEngine(getSettings, getEventManager, getSortEngine))
super.bind()
}
}
i.setSettings(settings) i.setSettings(settings)
i.bind() i.bind()
i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log)) i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log))
@ -103,6 +123,18 @@ final class IvySbt(val configuration: IvyConfiguration) {
} }
} }
/**
* Cleans consolidated resolution cache.
* @param md - module descriptor of the original Ivy graph.
*/
private[sbt] def cleanConsolidatedResolutionCache(md: ModuleDescriptor, log: Logger): Unit =
withIvy(log) { i =>
val prOpt = Option(i.getSettings.getResolver(ProjectResolver.InterProject)) map { case pr: ProjectResolver => pr }
if (configuration.updateOptions.consolidatedResolution) {
IvySbt.consolidatedResolveCache.clean(md, prOpt)
}
}
final class Module(rawModuleSettings: ModuleSettings) { final class Module(rawModuleSettings: ModuleSettings) {
val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings) val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings)
def owner = IvySbt.this def owner = IvySbt.this
@ -204,6 +236,7 @@ private object IvySbt {
val DefaultIvyFilename = "ivy.xml" val DefaultIvyFilename = "ivy.xml"
val DefaultMavenFilename = "pom.xml" val DefaultMavenFilename = "pom.xml"
val DefaultChecksums = Seq("sha1", "md5") val DefaultChecksums = Seq("sha1", "md5")
private[sbt] val consolidatedResolveCache: ConsolidatedResolveCache = new ConsolidatedResolveCache()
def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename) def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename)
def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename)

View File

@ -59,6 +59,15 @@ object IvyActions {
iv.getSettings.getRepositoryCacheManagers.foreach(_.clean()) iv.getSettings.getRepositoryCacheManagers.foreach(_.clean())
} }
/**
* Cleans the consolidated resolution cache, if any.
* This is called by clean.
*/
private[sbt] def cleanConsolidatedResolutionCache(module: IvySbt#Module, log: Logger): Unit =
module.withModule(log) { (ivy, md, default) =>
module.owner.cleanConsolidatedResolutionCache(md, log)
}
/** Creates a Maven pom from the given Ivy configuration*/ /** Creates a Maven pom from the given Ivy configuration*/
def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger) { def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger) {
import configuration.{ allRepositories, moduleInfo, configurations, extra, file, filterRepositories, process, includeTypes } import configuration.{ allRepositories, moduleInfo, configurations, extra, file, filterRepositories, process, includeTypes }

View File

@ -16,27 +16,42 @@ sealed trait IvyConfiguration {
def baseDirectory: File def baseDirectory: File
def log: Logger def log: Logger
def withBase(newBaseDirectory: File): This def withBase(newBaseDirectory: File): This
def updateOptions: UpdateOptions
} }
final class InlineIvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver], val otherResolvers: Seq[Resolver], final class InlineIvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver], val otherResolvers: Seq[Resolver],
val moduleConfigurations: Seq[ModuleConfiguration], val localOnly: Boolean, val lock: Option[xsbti.GlobalLock], val moduleConfigurations: Seq[ModuleConfiguration], val localOnly: Boolean, val lock: Option[xsbti.GlobalLock],
val checksums: Seq[String], val resolutionCacheDir: Option[File], val log: Logger) extends IvyConfiguration { val checksums: Seq[String], val resolutionCacheDir: Option[File], val updateOptions: UpdateOptions,
@deprecated("Use the variant that accepts the resolution cache location.", "0.13.0") val log: Logger) extends IvyConfiguration {
@deprecated("Use the variant that accepts resolutionCacheDir and updateOptions.", "0.13.0")
def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver], def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver],
moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock], moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock],
checksums: Seq[String], log: Logger) = checksums: Seq[String], log: Logger) =
this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, None, log) this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, None, UpdateOptions(), log)
@deprecated("Use the variant that accepts updateOptions.", "0.13.6")
def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver],
moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock],
checksums: Seq[String], resolutionCacheDir: Option[File], log: Logger) =
this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, UpdateOptions(), log)
type This = InlineIvyConfiguration type This = InlineIvyConfiguration
def baseDirectory = paths.baseDirectory def baseDirectory = paths.baseDirectory
def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log) def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums,
def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log) resolutionCacheDir, updateOptions, log)
def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums,
resolutionCacheDir, updateOptions, log)
} }
final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock], val extraResolvers: Seq[Resolver], val log: Logger) extends IvyConfiguration { final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock],
val extraResolvers: Seq[Resolver], val updateOptions: UpdateOptions, val log: Logger) extends IvyConfiguration {
@deprecated("Use the variant that accepts updateOptions.", "0.13.6")
def this(baseDirectory: File, uri: URI, lock: Option[xsbti.GlobalLock], extraResolvers: Seq[Resolver], log: Logger) =
this(baseDirectory, uri, lock, extraResolvers, UpdateOptions(), log)
type This = ExternalIvyConfiguration type This = ExternalIvyConfiguration
def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log) def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log)
} }
object ExternalIvyConfiguration { object ExternalIvyConfiguration {
def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, log) def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, UpdateOptions(), log)
} }
object IvyConfiguration { object IvyConfiguration {

View File

@ -34,6 +34,8 @@ class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]
map get revisionId map constructResult map get revisionId map constructResult
} }
private[sbt] def getModuleDescriptor(revisionId: ModuleRevisionId): Option[ModuleDescriptor] = map.get(revisionId)
def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport = def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport =
{ {
val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date) val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date)
@ -87,3 +89,7 @@ class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]
def setSettings(settings: ResolverSettings) { this.settings = Some(settings) } def setSettings(settings: ResolverSettings) { this.settings = Some(settings) }
def getRepositoryCacheManager = settings match { case Some(s) => s.getDefaultRepositoryCacheManager; case None => sys.error("No settings defined for ProjectResolver") } def getRepositoryCacheManager = settings match { case Some(s) => s.getDefaultRepositoryCacheManager; case None => sys.error("No settings defined for ProjectResolver") }
} }
object ProjectResolver {
private[sbt] val InterProject = "inter-project"
}

View File

@ -0,0 +1,27 @@
package sbt
import java.io.File
/**
* Represents configurable options for update task.
* While UpdateConfiguration is passed into update at runtime,
* UpdateOption is intended to be used while setting up the Ivy object.
*
* See also UpdateConfiguration in IvyActions.scala.
*/
final class UpdateOptions(
/** If set to true, use consolidated resolution. */
val consolidatedResolution: Boolean) {
def withConsolidatedResolution(consolidatedResolution: Boolean): UpdateOptions =
copy(consolidatedResolution = consolidatedResolution)
private[sbt] def copy(
consolidatedResolution: Boolean = this.consolidatedResolution): UpdateOptions =
new UpdateOptions(consolidatedResolution)
}
object UpdateOptions {
def apply(): UpdateOptions =
new UpdateOptions(false)
}

View File

@ -0,0 +1,120 @@
package sbt
package ivyint
import java.io.File
import collection.concurrent
import org.apache.ivy.core
import core.resolve._
import core.module.id.ModuleRevisionId
import core.report.ResolveReport
import core.module.descriptor.{ DefaultModuleDescriptor, ModuleDescriptor, DependencyDescriptor }
import core.{ IvyPatternHelper, LogOptions }
import org.apache.ivy.util.Message
private[sbt] object ConsolidatedResolveCache {
def createID(organization: String, name: String, revision: String) =
ModuleRevisionId.newInstance(organization, name, revision)
def sbtOrgTemp = "org.scala-sbt.temp"
}
private[sbt] class ConsolidatedResolveCache() {
import ConsolidatedResolveCache._
val resolveReportCache: concurrent.Map[ModuleRevisionId, ResolveReport] = concurrent.TrieMap()
val resolvePropertiesCache: concurrent.Map[ModuleRevisionId, String] = concurrent.TrieMap()
val directDependencyCache: concurrent.Map[ModuleDescriptor, Vector[DependencyDescriptor]] = concurrent.TrieMap()
def clean(md0: ModuleDescriptor, prOpt: Option[ProjectResolver]): Unit = {
val mrid0 = md0.getModuleRevisionId
val md1 = if (mrid0.getOrganisation == sbtOrgTemp) md0
else buildConsolidatedModuleDescriptor(md0, prOpt)
val mrid1 = md1.getModuleRevisionId
resolveReportCache.remove(mrid1)
resolvePropertiesCache.remove(mrid1)
}
def directDependencies(md0: ModuleDescriptor): Vector[DependencyDescriptor] =
directDependencyCache.getOrElseUpdate(md0, md0.getDependencies.toVector)
def buildConsolidatedModuleDescriptor(md0: ModuleDescriptor, prOpt: Option[ProjectResolver]): DefaultModuleDescriptor = {
def expandInternalDeps(dep: DependencyDescriptor): Vector[DependencyDescriptor] =
prOpt map {
_.getModuleDescriptor(dep.getDependencyRevisionId) match {
case Some(internal) => directDependencies(internal) flatMap expandInternalDeps
case _ => Vector(dep)
}
} getOrElse Vector(dep)
val expanded = directDependencies(md0) flatMap expandInternalDeps
val depStrings = expanded map { dep =>
val mrid = dep.getDependencyRevisionId
val confMap = (dep.getModuleConfigurations map { conf =>
conf + "->(" + dep.getDependencyConfigurations(conf).mkString(",") + ")"
})
mrid.toString + ";" + confMap.mkString(";")
}
val depsString = depStrings.distinct.sorted.mkString("\n")
val sha1 = Hash.toHex(Hash(depsString))
// println("sha1: " + sha1)
val md1 = new DefaultModuleDescriptor(createID(sbtOrgTemp, "temp-resolve-" + sha1, "1.0"), "release", null, false)
md1
}
}
private[sbt] trait ConsolidatedResolveEngine extends ResolveEngine {
import ConsolidatedResolveCache._
private[sbt] def consolidatedResolveCache: ConsolidatedResolveCache
private[sbt] def projectResolver: Option[ProjectResolver]
/**
* Resolve dependencies of a module described by a module descriptor.
*/
override def resolve(md0: ModuleDescriptor, options0: ResolveOptions): ResolveReport = {
val cache = consolidatedResolveCache
val cacheManager = getSettings.getResolutionCacheManager
val md1 = cache.buildConsolidatedModuleDescriptor(md0, projectResolver)
val md1mrid = md1.getModuleRevisionId
def doWork: (ResolveReport, String) = {
if (options0.getLog != LogOptions.LOG_QUIET) {
Message.info("Consolidating managed dependencies to " + md1mrid.toString + " ...")
}
md1.setLastModified(System.currentTimeMillis)
for {
x <- md0.getConfigurations
} yield md1.addConfiguration(x)
for {
x <- md0.getDependencies
} yield md1.addDependency(x)
val options1 = new ResolveOptions(options0)
options1.setOutputReport(false)
val report0 = super.resolve(md1, options1)
val ivyPropertiesInCache1 = cacheManager.getResolvedIvyPropertiesInCache(md1.getResolvedModuleRevisionId)
val prop0 =
if (ivyPropertiesInCache1.exists) IO.read(ivyPropertiesInCache1)
else ""
if (options0.isOutputReport) {
this.outputReport(report0, cacheManager, options0)
}
cache.resolveReportCache(md1mrid) = report0
cache.resolvePropertiesCache(md1mrid) = prop0
(report0, prop0)
}
val (report0, prop0) = (cache.resolveReportCache.get(md1mrid), cache.resolvePropertiesCache.get(md1mrid)) match {
case (Some(report), Some(prop)) =>
if (options0.getLog != LogOptions.LOG_QUIET) {
Message.info("Found consolidated dependency " + md1mrid.toString + " ...")
}
(report, prop)
case _ => doWork
}
cacheManager.saveResolvedModuleDescriptor(md0)
if (prop0 != "") {
val ivyPropertiesInCache0 = cacheManager.getResolvedIvyPropertiesInCache(md0.getResolvedModuleRevisionId)
IO.write(ivyPropertiesInCache0, prop0)
}
report0
}
}

View File

@ -108,7 +108,8 @@ object Defaults extends BuildCommon {
pomExtra :== NodeSeq.Empty, pomExtra :== NodeSeq.Empty,
pomPostProcess :== idFun, pomPostProcess :== idFun,
pomAllRepositories :== false, pomAllRepositories :== false,
pomIncludeRepository :== Classpaths.defaultRepositoryFilter pomIncludeRepository :== Classpaths.defaultRepositoryFilter,
updateOptions := UpdateOptions()
) )
/** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */ /** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */
@ -218,7 +219,11 @@ object Defaults extends BuildCommon {
sbt.inc.ClassfileManager.transactional(crossTarget.value / "classes.bak", sbt.Logger.Null)), sbt.inc.ClassfileManager.transactional(crossTarget.value / "classes.bak", sbt.Logger.Null)),
scalaInstance <<= scalaInstanceTask, scalaInstance <<= scalaInstanceTask,
crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.Disabled), crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.Disabled),
crossTarget := makeCrossTarget(target.value, scalaBinaryVersion.value, sbtBinaryVersion.value, sbtPlugin.value, crossPaths.value) crossTarget := makeCrossTarget(target.value, scalaBinaryVersion.value, sbtBinaryVersion.value, sbtPlugin.value, crossPaths.value),
clean := {
val _ = clean.value
IvyActions.cleanConsolidatedResolutionCache(ivyModule.value, streams.value.log)
}
) )
// must be a val: duplication detected by object identity // must be a val: duplication detected by object identity
private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq( private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq(
@ -1075,6 +1080,7 @@ object Classpaths {
projectID <<= pluginProjectID, projectID <<= pluginProjectID,
projectDescriptors <<= depMap, projectDescriptors <<= depMap,
updateConfiguration := new UpdateConfiguration(retrieveConfiguration.value, false, ivyLoggingLevel.value), updateConfiguration := new UpdateConfiguration(retrieveConfiguration.value, false, ivyLoggingLevel.value),
updateOptions := (updateOptions in Global).value,
retrieveConfiguration := { if (retrieveManaged.value) Some(new RetrieveConfiguration(managedDirectory.value, retrievePattern.value)) else None }, retrieveConfiguration := { if (retrieveManaged.value) Some(new RetrieveConfiguration(managedDirectory.value, retrievePattern.value)) else None },
ivyConfiguration <<= mkIvyConfiguration, ivyConfiguration <<= mkIvyConfiguration,
ivyConfigurations := { ivyConfigurations := {
@ -1162,7 +1168,8 @@ object Classpaths {
val explicit = buildStructure.value.units(thisProjectRef.value.build).unit.plugins.pluginData.resolvers val explicit = buildStructure.value.units(thisProjectRef.value.build).unit.plugins.pluginData.resolvers
explicit orElse bootRepositories(appConfiguration.value) getOrElse externalResolvers.value explicit orElse bootRepositories(appConfiguration.value) getOrElse externalResolvers.value
}, },
ivyConfiguration := new InlineIvyConfiguration(ivyPaths.value, externalResolvers.value, Nil, Nil, offline.value, Option(lock(appConfiguration.value)), checksums.value, Some(target.value / "resolution-cache"), streams.value.log), ivyConfiguration := new InlineIvyConfiguration(ivyPaths.value, externalResolvers.value, Nil, Nil, offline.value, Option(lock(appConfiguration.value)),
checksums.value, Some(target.value / "resolution-cache"), UpdateOptions(), streams.value.log),
ivySbt <<= ivySbt0, ivySbt <<= ivySbt0,
classifiersModule <<= (projectID, sbtDependency, transitiveClassifiers, loadedBuild, thisProjectRef) map { (pid, sbtDep, classifiers, lb, ref) => classifiersModule <<= (projectID, sbtDependency, transitiveClassifiers, loadedBuild, thisProjectRef) map { (pid, sbtDep, classifiers, lb, ref) =>
val pluginClasspath = lb.units(ref.build).unit.plugins.fullClasspath val pluginClasspath = lb.units(ref.build).unit.plugins.fullClasspath
@ -1312,7 +1319,7 @@ object Classpaths {
def projectResolverTask: Initialize[Task[Resolver]] = def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors map { m => projectDescriptors map { m =>
new RawRepository(new ProjectResolver("inter-project", m)) new RawRepository(new ProjectResolver(ProjectResolver.InterProject, m))
} }
def analyzed[T](data: T, analysis: inc.Analysis) = Attributed.blank(data).put(Keys.analysis, analysis) def analyzed[T](data: T, analysis: inc.Analysis) = Attributed.blank(data).put(Keys.analysis, analysis)
@ -1340,11 +1347,13 @@ object Classpaths {
def unmanagedDependencies: Initialize[Task[Classpath]] = def unmanagedDependencies: Initialize[Task[Classpath]] =
(thisProjectRef, configuration, settingsData, buildDependencies) flatMap unmanagedDependencies0 (thisProjectRef, configuration, settingsData, buildDependencies) flatMap unmanagedDependencies0
def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] = def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] =
(fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, checksums in update, appConfiguration, target, streams) map { (rs, paths, other, moduleConfs, off, check, app, t, s) => (fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, checksums in update, appConfiguration,
warnResolversConflict(rs ++: other, s.log) target, updateOptions, streams) map { (rs, paths, other, moduleConfs, off, check, app, t, uo, s) =>
val resCacheDir = t / "resolution-cache" warnResolversConflict(rs ++: other, s.log)
new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Option(lock(app)), check, Some(resCacheDir), s.log) val resCacheDir = t / "resolution-cache"
}
new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Option(lock(app)), check, Some(resCacheDir), uo, s.log)
}
import java.util.LinkedHashSet import java.util.LinkedHashSet
import collection.JavaConversions.asScalaSet import collection.JavaConversions.asScalaSet
@ -1648,13 +1657,13 @@ trait BuildExtra extends BuildCommon {
externalIvySettingsURI(Def.value(url.toURI), addMultiResolver) externalIvySettingsURI(Def.value(url.toURI), addMultiResolver)
def externalIvySettingsURI(uri: Initialize[URI], addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = def externalIvySettingsURI(uri: Initialize[URI], addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] =
{ {
val other = (baseDirectory, appConfiguration, projectResolver, streams).identityMap val other = (baseDirectory, appConfiguration, projectResolver, updateOptions, streams).identityMap
ivyConfiguration <<= (uri zipWith other) { ivyConfiguration <<= (uri zipWith other) {
case (u, otherTask) => case (u, otherTask) =>
otherTask map { otherTask map {
case (base, app, pr, s) => case (base, app, pr, uo, s) =>
val extraResolvers = if (addMultiResolver) pr :: Nil else Nil val extraResolvers = if (addMultiResolver) pr :: Nil else Nil
new ExternalIvyConfiguration(base, u, Option(lock(app)), extraResolvers, s.log) new ExternalIvyConfiguration(base, u, Option(lock(app)), extraResolvers, uo, s.log)
} }
} }
} }

View File

@ -242,6 +242,7 @@ object Keys {
val moduleSettings = TaskKey[ModuleSettings]("module-settings", "Module settings, which configure dependency management for a specific module, such as a project.", DTask) val moduleSettings = TaskKey[ModuleSettings]("module-settings", "Module settings, which configure dependency management for a specific module, such as a project.", DTask)
val unmanagedBase = SettingKey[File]("unmanaged-base", "The default directory for manually managed libraries.", ASetting) val unmanagedBase = SettingKey[File]("unmanaged-base", "The default directory for manually managed libraries.", ASetting)
val updateConfiguration = SettingKey[UpdateConfiguration]("update-configuration", "Configuration for resolving and retrieving managed dependencies.", DSetting) val updateConfiguration = SettingKey[UpdateConfiguration]("update-configuration", "Configuration for resolving and retrieving managed dependencies.", DSetting)
val updateOptions = SettingKey[UpdateOptions]("update-options", "Options for resolving managed dependencies.", DSetting)
val ivySbt = TaskKey[IvySbt]("ivy-sbt", "Provides the sbt interface to Ivy.", CTask) val ivySbt = TaskKey[IvySbt]("ivy-sbt", "Provides the sbt interface to Ivy.", CTask)
val ivyModule = TaskKey[IvySbt#Module]("ivy-module", "Provides the sbt interface to a configured Ivy module.", CTask) val ivyModule = TaskKey[IvySbt#Module]("ivy-module", "Provides the sbt interface to a configured Ivy module.", CTask)
val updateCacheName = TaskKey[String]("updateCacheName", "Defines the directory name used to store the update cache files (inside the streams cacheDirectory).", DTask) val updateCacheName = TaskKey[String]("updateCacheName", "Defines the directory name used to store the update cache files (inside the streams cacheDirectory).", DTask)

10
notes/0.13.6.md Normal file
View File

@ -0,0 +1,10 @@
[413]: https://github.com/sbt/sbt/issues/413
[1454]: https://github.com/sbt/sbt/pull/1454
### Consolidated resolution
sbt 0.13.6 adds a new setting key called `updateOptions`, which can be used to enable consolidated resolution for `update` task.
updateOptions := updateOptions.value.withConsolidatedResolution(true)
This feature is specifically targeted to address [Ivy resolution is beging slow for multi-module projects #413][413]. Consolidated resolution aims to fix this issue by artificially constructing an Ivy dependency graph for the unique managed dependencies. If two subprojects introduce identical external dependencies, both subprojects should consolidate to the same graph, and therefore resolve immediately for the second `update`. [#1454][1454]

1
notes/about.md Normal file
View File

@ -0,0 +1 @@
[sbt](http://www.scala-sbt.org/) is the interactive build tool.

View File

@ -0,0 +1,3 @@
public class A {
public static final int x = 3;
}

View File

@ -0,0 +1,5 @@
public final class C {
public static void main(String[] args) {
System.out.println(A.x);
}
}

View File

@ -0,0 +1,43 @@
lazy val check = taskKey[Unit]("Runs the check")
def commonSettings: Seq[Def.Setting[_]] =
Seq(
ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((target in LocalRootProject).value / "ivy-cache")),
libraryDependencies := Seq("net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive()),
autoScalaLibrary := false, // avoid downloading fresh scala-library/scala-compiler
managedScalaInstance := false,
updateOptions := updateOptions.value.withConsolidatedResolution(true)
)
lazy val root = (project in file(".")).settings(
organization in ThisBuild := "org.example",
version in ThisBuild := "1.0"
)
lazy val a = project.
settings(commonSettings: _*).
settings(
artifact in (Compile, packageBin) := Artifact("demo")
)
lazy val b = project.
settings(commonSettings: _*).
settings(
check := {
val report = update.value
val configurationReport = (report.configurations find {_.configuration == "compile"}).head
val x = configurationReport.modules match {
case Seq(moduleReport) =>
moduleReport.module match {
case ModuleID("net.sf.json-lib", "json-lib", "2.4", _, _, _, _, _, _, _, _) => ()
case x => sys.error("Unexpected module: " + x.toString)
}
case x => sys.error("Unexpected modules: " + x.toString)
}
}
)
lazy val c = project.
settings(commonSettings: _*).
settings(
libraryDependencies := Seq(organization.value %% "a" % version.value)
)

View File

@ -0,0 +1,5 @@
> a/publishLocal
> b/check
> c/run