Permalink
Browse files

more flexible inter-project dependencies

  • Loading branch information...
1 parent c2ebcba commit 5fbe6e9d97776938b020d088485610086be7a453 @harrah committed Oct 26, 2011
Showing with 68 additions and 23 deletions.
  1. +32 −0 main/BuildDependencies.scala
  2. +35 −23 main/Defaults.scala
  3. +1 −0 main/Keys.scala
@@ -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
@@ -48,6 +48,7 @@ object Defaults extends BuildCommon
managedDirectory <<= baseDirectory(_ / "lib_managed")
))
def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq(
+ buildDependencies <<= buildDependencies or Classpaths.constructBuildDependencies,
taskTemporaryDirectory := IO.createTemporaryDirectory,
onComplete <<= taskTemporaryDirectory { dir => () => IO.delete(dir); IO.createDirectory(dir) },
parallelExecution :== true,
@@ -839,20 +840,19 @@ object Classpaths
def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath
- def projectDependenciesTask =
- (thisProject, settings) map { (p, data) =>
- p.dependencies flatMap { dep => (projectID in dep.project) get data map { _.copy(configurations = dep.configuration) } }
+ def projectDependenciesTask: Initialize[Task[Seq[ModuleID]]] =
+ (thisProjectRef, settings, buildDependencies) map { (ref, data, deps) =>
+ deps.classpath(ref) flatMap { dep => (projectID in dep.project) get data map { _.copy(configurations = dep.configuration) } }
}
def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
- (thisProject, thisProjectRef, settings, streams) flatMap { (root, rootRef, data, s) =>
- val dependencies = (p: (ProjectRef, ResolvedProject)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) })
- depMap(Dag.topologicalSort((rootRef,root))(dependencies).dropRight(1), data, s.log)
+ (thisProjectRef, settings, buildDependencies, streams) flatMap { (root, data, deps, s) =>
+ depMap(deps classpathTransitiveRefs root, data, s.log)
}
- def depMap(projects: Seq[(ProjectRef,ResolvedProject)], data: Settings[Scope], log: Logger): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
- projects.flatMap { case (p,_) => ivyModule in p get data }.join.map { mods =>
- mods map { _.dependencyMapping(log) } toMap ;
+ def depMap(projects: Seq[ProjectRef], data: Settings[Scope], log: Logger): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
+ projects.flatMap( ivyModule in _ get data).join.map { mod =>
+ mod map { _.dependencyMapping(log) } toMap ;
}
def projectResolverTask: Initialize[Task[Resolver]] =
@@ -868,52 +868,64 @@ object Classpaths
(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]] =
- (thisProjectRef, thisProject, classpathConfiguration, configuration, settings) flatMap internalDependencies0
+ (thisProjectRef, classpathConfiguration, configuration, settings, buildDependencies) flatMap internalDependencies0
def unmanagedDependencies: Initialize[Task[Classpath]] =
- (thisProjectRef, thisProject, configuration, settings) flatMap unmanagedDependencies0
+ (thisProjectRef, configuration, settings, buildDependencies) flatMap unmanagedDependencies0
def mkIvyConfiguration: Initialize[Task[IvyConfiguration]] =
(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)
}
import java.util.LinkedHashSet
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)])
- def visit(p: ProjectRef, project: ResolvedProject, c: Configuration)
+ def visit(p: ProjectRef, c: Configuration)
{
val applicableConfigs = allConfigs(c)
for(ac <- applicableConfigs) // add all configurations in this project
visited add (p, ac.name)
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 mapping = mapped(confMapping, masterConfs, names(configurations), "compile", "*->compile")
// map master configuration 'c' and all extended configurations to the appropriate dependency configuration
for(ac <- applicableConfigs; depConfName <- mapping(ac.name))
{
for(depConf <- confOpt(configurations, depConfName) )
if( ! visited( (dep, depConfName) ) )
- visit(dep, depProject, depConf)
+ visit(dep, depConf)
}
}
}
- visit(projectRef, project, conf)
+ visit(projectRef, conf)
visited.toSeq
}
- def unmanagedDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
- interDependencies(projectRef, project, conf, conf, data, true, unmanagedLibs)
- def internalDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, self: Configuration, data: Settings[Scope]): Task[Classpath] =
- interDependencies(projectRef, project, conf, self, data, false, productsTask)
- def interDependencies(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, self: Configuration, data: Settings[Scope], includeSelf: Boolean,
+ def unmanagedDependencies0(projectRef: ProjectRef, conf: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
+ interDependencies(projectRef, deps, conf, conf, data, true, unmanagedLibs)
+ def internalDependencies0(projectRef: ProjectRef, conf: Configuration, self: Configuration, data: Settings[Scope], deps: BuildDependencies): Task[Classpath] =
+ interDependencies(projectRef, deps, conf, self, data, false, productsTask)
+ def interDependencies(projectRef: ProjectRef, deps: BuildDependencies, conf: Configuration, self: Configuration, data: Settings[Scope], includeSelf: Boolean,
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]])
for( (dep, c) <- visited )
if(includeSelf || (dep != projectRef) || (conf.name != c && self.name != c))
View
@@ -39,6 +39,7 @@ object Keys
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 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 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.")

0 comments on commit 5fbe6e9

Please sign in to comment.