more flexible inter-project dependencies

This commit is contained in:
Mark Harrah 2011-10-26 19:35:29 -04:00
parent c2ebcba73b
commit 5fbe6e9d97
3 changed files with 68 additions and 23 deletions

View File

@ -0,0 +1,32 @@
package sbt
import Types.idFun
import BuildDependencies._
final class BuildDependencies private(val classpath: DependencyMap[ClasspathDep[ProjectRef]], val aggregate: DependencyMap[ProjectRef])
{
def classpathRefs(ref: ProjectRef): Seq[ProjectRef] = classpath(ref) map getID
def classpathTransitiveRefs(ref: ProjectRef): Seq[ProjectRef] = classpathTransitive(ref)
lazy val classpathTransitive: DependencyMap[ProjectRef] = transitive(classpath, getID)
lazy val aggregateTransitive: DependencyMap[ProjectRef] = transitive(aggregate, idFun[ProjectRef])
def addClasspath(ref: ProjectRef, deps: ClasspathDep[ProjectRef]*): BuildDependencies =
new BuildDependencies( classpath.updated(ref, deps ++ classpath.getOrElse(ref, Nil)), aggregate)
def addAggregate(ref: ProjectRef, deps: ProjectRef*): BuildDependencies =
new BuildDependencies(classpath, aggregate.updated(ref, deps ++ aggregate.getOrElse(ref, Nil)))
}
object BuildDependencies
{
def apply(classpath: DependencyMap[ClasspathDep[ProjectRef]], aggregate: DependencyMap[ProjectRef]): BuildDependencies =
new BuildDependencies(classpath, aggregate)
type DependencyMap[D] = Map[ProjectRef, Seq[D]]
def transitive[D](deps: DependencyMap[D], extract: D => ProjectRef): DependencyMap[ProjectRef] =
for( (ref, _) <- deps ) yield
{
val sorted = Dag.topologicalSort(ref)(d => deps(d) map extract)
(ref, sorted dropRight 1)
}
val getID: ClasspathDep[ProjectRef] => ProjectRef = _.project
}

View File

@ -48,6 +48,7 @@ object Defaults extends BuildCommon
managedDirectory <<= baseDirectory(_ / "lib_managed") managedDirectory <<= baseDirectory(_ / "lib_managed")
)) ))
def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq( def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
buildDependencies <<= buildDependencies or Classpaths.constructBuildDependencies,
taskTemporaryDirectory := IO.createTemporaryDirectory, taskTemporaryDirectory := IO.createTemporaryDirectory,
onComplete <<= taskTemporaryDirectory { dir => () => IO.delete(dir); IO.createDirectory(dir) }, onComplete <<= taskTemporaryDirectory { dir => () => IO.delete(dir); IO.createDirectory(dir) },
parallelExecution :== true, parallelExecution :== true,
@ -839,20 +840,19 @@ object Classpaths
def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath
def projectDependenciesTask = def projectDependenciesTask: Initialize[Task[Seq[ModuleID]]] =
(thisProject, settings) map { (p, data) => (thisProjectRef, settings, buildDependencies) map { (ref, data, deps) =>
p.dependencies flatMap { dep => (projectID in dep.project) get data map { _.copy(configurations = dep.configuration) } } deps.classpath(ref) flatMap { dep => (projectID in dep.project) get data map { _.copy(configurations = dep.configuration) } }
} }
def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] = def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
(thisProject, thisProjectRef, settings, streams) flatMap { (root, rootRef, data, s) => (thisProjectRef, settings, buildDependencies, streams) flatMap { (root, data, deps, s) =>
val dependencies = (p: (ProjectRef, ResolvedProject)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) }) depMap(deps classpathTransitiveRefs root, data, s.log)
depMap(Dag.topologicalSort((rootRef,root))(dependencies).dropRight(1), data, s.log)
} }
def depMap(projects: Seq[(ProjectRef,ResolvedProject)], data: Settings[Scope], log: Logger): Task[Map[ModuleRevisionId, ModuleDescriptor]] = def depMap(projects: Seq[ProjectRef], data: Settings[Scope], log: Logger): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
projects.flatMap { case (p,_) => ivyModule in p get data }.join.map { mods => projects.flatMap( ivyModule in _ get data).join.map { mod =>
mods map { _.dependencyMapping(log) } toMap ; mod map { _.dependencyMapping(log) } toMap ;
} }
def projectResolverTask: Initialize[Task[Resolver]] = def projectResolverTask: Initialize[Task[Resolver]] =
@ -868,10 +868,23 @@ object Classpaths
(if(useJars) Seq(pkgTask).join else psTask) map { _ map { f => analyzed(f, analysis) } } (if(useJars) Seq(pkgTask).join else psTask) map { _ map { f => analyzed(f, analysis) } }
} }
def constructBuildDependencies: Initialize[BuildDependencies] =
loadedBuild { lb =>
import collection.mutable.HashMap
val agg = new HashMap[ProjectRef, Seq[ProjectRef]]
val cp = new HashMap[ProjectRef, Seq[ClasspathDep[ProjectRef]]]
for(lbu <- lb.units.values; rp <- lbu.defined.values)
{
val ref = ProjectRef(lbu.unit.uri, rp.id)
cp(ref) = rp.dependencies
agg(ref) = rp.aggregate
}
BuildDependencies(cp.toMap, agg.toMap)
}
def internalDependencies: Initialize[Task[Classpath]] = def internalDependencies: Initialize[Task[Classpath]] =
(thisProjectRef, thisProject, classpathConfiguration, configuration, settings) flatMap internalDependencies0 (thisProjectRef, classpathConfiguration, configuration, settings, buildDependencies) flatMap internalDependencies0
def unmanagedDependencies: Initialize[Task[Classpath]] = def unmanagedDependencies: Initialize[Task[Classpath]] =
(thisProjectRef, thisProject, configuration, settings) flatMap unmanagedDependencies0 (thisProjectRef, configuration, settings, buildDependencies) flatMap unmanagedDependencies0
def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] = def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] =
(fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, checksums in update, appConfiguration, streams) map { (rs, paths, other, moduleConfs, off, check, app, s) => (fullResolvers, ivyPaths, otherResolvers, moduleConfigurations, offline, checksums in update, appConfiguration, streams) map { (rs, paths, other, moduleConfs, off, check, app, s) =>
new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Some(lock(app)), check, s.log) new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, Some(lock(app)), check, s.log)
@ -879,19 +892,18 @@ object Classpaths
import java.util.LinkedHashSet import java.util.LinkedHashSet
import collection.JavaConversions.asScalaSet import collection.JavaConversions.asScalaSet
def interSort(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Seq[(ProjectRef,String)] = def interSort(projectRef: ProjectRef, conf: Configuration, data: Settings[Scope], deps: BuildDependencies): Seq[(ProjectRef,String)] =
{ {
val visited = asScalaSet(new LinkedHashSet[(ProjectRef,String)]) val visited = asScalaSet(new LinkedHashSet[(ProjectRef,String)])
def visit(p: ProjectRef, project: ResolvedProject, c: Configuration) def visit(p: ProjectRef, c: Configuration)
{ {
val applicableConfigs = allConfigs(c) val applicableConfigs = allConfigs(c)
for(ac <- applicableConfigs) // add all configurations in this project for(ac <- applicableConfigs) // add all configurations in this project
visited add (p, ac.name) visited add (p, ac.name)
val masterConfs = names(getConfigurations(projectRef, data)) val masterConfs = names(getConfigurations(projectRef, data))
for( ResolvedClasspathDependency(dep, confMapping) <- project.dependencies) for( ResolvedClasspathDependency(dep, confMapping) <- deps.classpath(p))
{ {
val depProject = thisProject in dep get data getOrElse error("Invalid project: " + dep)
val configurations = getConfigurations(dep, data) val configurations = getConfigurations(dep, data)
val mapping = mapped(confMapping, masterConfs, names(configurations), "compile", "*->compile") val mapping = mapped(confMapping, masterConfs, names(configurations), "compile", "*->compile")
// map master configuration 'c' and all extended configurations to the appropriate dependency configuration // map master configuration 'c' and all extended configurations to the appropriate dependency configuration
@ -899,21 +911,21 @@ object Classpaths
{ {
for(depConf <- confOpt(configurations, depConfName) ) for(depConf <- confOpt(configurations, depConfName) )
if( ! visited( (dep, depConfName) ) ) if( ! visited( (dep, depConfName) ) )
visit(dep, depProject, depConf) visit(dep, depConf)
} }
} }
} }
visit(projectRef, project, conf) visit(projectRef, conf)
visited.toSeq visited.toSeq
} }
def unmanagedDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] = def unmanagedDependencies0(projectRef: ProjectRef, conf: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
interDependencies(projectRef, project, conf, conf, data, true, unmanagedLibs) interDependencies(projectRef, deps, conf, conf, data, true, unmanagedLibs)
def internalDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, self: Configuration, data: Settings[Scope]): Task[Classpath] = def internalDependencies0(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
interDependencies(projectRef, project, conf, self, data, false, productsTask) interDependencies(projectRef, deps, conf, self, data, false, productsTask)
def interDependencies(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, self: Configuration, data: Settings[Scope], includeSelf: Boolean, def interDependencies(projectRef: ProjectRef, deps: BuildDependencies, conf: Configuration, self: Configuration, data: Settings[Scope], includeSelf: Boolean,
f: (ProjectRef, String, Settings[Scope]) => Task[Classpath]): Task[Classpath] = f: (ProjectRef, String, Settings[Scope]) => Task[Classpath]): Task[Classpath] =
{ {
val visited = interSort(projectRef, project, conf, data) val visited = interSort(projectRef, conf, data, deps)
val tasks = asScalaSet(new LinkedHashSet[Task[Classpath]]) val tasks = asScalaSet(new LinkedHashSet[Task[Classpath]])
for( (dep, c) <- visited ) for( (dep, c) <- visited )
if(includeSelf || (dep != projectRef) || (conf.name != c && self.name != c)) if(includeSelf || (dep != projectRef) || (conf.name != c && self.name != c))

View File

@ -39,6 +39,7 @@ object Keys
val stateBuildStructure = AttributeKey[Load.BuildStructure]("build-structure", "Data structure containing all information about the build definition.") val stateBuildStructure = AttributeKey[Load.BuildStructure]("build-structure", "Data structure containing all information about the build definition.")
val buildStructure = TaskKey[Load.BuildStructure]("build-structure", "Provides access to the build structure, settings, and streams manager.") val buildStructure = TaskKey[Load.BuildStructure]("build-structure", "Provides access to the build structure, settings, and streams manager.")
val loadedBuild = SettingKey[Load.LoadedBuild]("loaded-build", "Provides access to the loaded project structure. This is the information available before settings are evaluated.") val loadedBuild = SettingKey[Load.LoadedBuild]("loaded-build", "Provides access to the loaded project structure. This is the information available before settings are evaluated.")
val buildDependencies = SettingKey[BuildDependencies]("build-dependencies", "Definitive source of inter-project dependencies for compilation and dependency management.\n\tThis is populated by default by the dependencies declared on Project instances, but may be modified.\n\tThe main restriction is that new builds may not be introduced.")
val appConfiguration = SettingKey[xsbti.AppConfiguration]("app-configuration", "Provides access to the launched sbt configuration, including the ScalaProvider, Launcher, and GlobalLock.") val appConfiguration = SettingKey[xsbti.AppConfiguration]("app-configuration", "Provides access to the launched sbt configuration, including the ScalaProvider, Launcher, and GlobalLock.")
val thisProject = SettingKey[ResolvedProject]("this-project", "Provides the current project for the referencing scope.") val thisProject = SettingKey[ResolvedProject]("this-project", "Provides the current project for the referencing scope.")
val thisProjectRef = SettingKey[ProjectRef]("this-project-ref", "Provides a fully-resolved reference to the current project for the referencing scope.") val thisProjectRef = SettingKey[ProjectRef]("this-project-ref", "Provides a fully-resolved reference to the current project for the referencing scope.")