Control over automatically added settings.

Project.autoSettings accepts a sequence of AddSettings, instances of which
are constructed from methods in AddSettings.  The configurable settings
are per-user settings (from ~/.sbt, for example), settings from .sbt files,
and plugin settings (project-level only).  The order in which these instances
are provided to autoSettings determines the order in which they are appended
to the settings explicitly provided in Project.settings.

For .sbt files, defaultSbtFiles adds the settings from all .sbt files in the
project's base directory as usual.  AddSettings.sbtFiles accepts a sequence
of Files that will be loaded according to the standard .sbt format.  Relative
Files are resolved against the project's base directory.

Plugin settings may be included on a per-Plugin basis by using the plugins
method and passing a Plugin => Boolean.  The settings controlled here are
only the automatic per-project settings.  Per-build and global settings will
always be included.
This commit is contained in:
Mark Harrah 2012-11-16 09:56:49 -05:00
parent 1bc26fa488
commit f99ba44703
11 changed files with 122 additions and 23 deletions

38
main/AddSettings.scala Normal file
View File

@ -0,0 +1,38 @@
package sbt
import Types.const
import java.io.File
/** Represents how settings from various sources are automatically merged into a Project's settings.
* This only configures per-project settings and not global or per-build settings. */
sealed abstract class AddSettings
object AddSettings
{
private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings
private[sbt] final object User extends AddSettings
private[sbt] final class Plugins(val include: Plugin => Boolean) extends AddSettings
private[sbt] final class DefaultSbtFiles(val include: File => Boolean) extends AddSettings
private[sbt] final class SbtFiles(val files: Seq[File]) extends AddSettings
/** Adds all settings from a plugin to a project. */
val allPlugins: AddSettings = plugins(const(true))
/** Allows the plugins whose names match the `names` filter to automatically add settings to a project. */
def plugins(include: Plugin => Boolean): AddSettings = new Plugins(include)
/** Includes user settings in the project. */
val userSettings: AddSettings = User
/** Includes the settings from all .sbt files in the project's base directory. */
val defaultSbtFiles: AddSettings = new DefaultSbtFiles(const(true))
/** Includes the settings from the .sbt files given by `files`. */
def sbtFiles(files: File*): AddSettings = new SbtFiles(files)
/** Includes settings automatically*/
def seq(autos: AddSettings*): AddSettings = new Sequence(autos)
val allDefaults: AddSettings = seq(userSettings, allPlugins, defaultSbtFiles)
}

View File

@ -195,19 +195,25 @@ object Load
loaded.units.toSeq.flatMap { case (uri, build) => loaded.units.toSeq.flatMap { case (uri, build) =>
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit) val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
val plugins = build.unit.plugins.plugins val plugins = build.unit.plugins.plugins
val (pluginSettings, pluginProjectSettings, pluginBuildSettings) = extractSettings(plugins) val pluginBuildSettings = plugins.flatMap(_.buildSettings)
val (pluginThisProject, pluginNotThis) = pluginSettings partition isProjectThis val pluginNotThis = plugins.flatMap(_.settings) filterNot isProjectThis
val projectSettings = build.defined flatMap { case (id, project) => val projectSettings = build.defined flatMap { case (id, project) =>
val srcs = configurationSources(project.base)
val ref = ProjectRef(uri, id)
val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
val loader = build.unit.definitions.loader val loader = build.unit.definitions.loader
val settings = lazy val defaultSbtFiles = configurationSources(project.base)
(thisProject :== project) +:
(thisProjectRef :== ref) +: import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence}
(defineConfig ++ project.settings ++ injectSettings.projectLoaded(loader) ++ pluginThisProject ++ def expand(auto: AddSettings): Seq[Setting[_]] = auto match {
pluginProjectSettings ++ configurations(srcs, eval, build.imports)(loader) ++ injectSettings.project) case User => injectSettings.projectLoaded(loader)
case sf: SbtFiles => configurations( sf.files.map(f => IO.resolve(project.base, f)), eval, build.imports )(loader)
case sf: DefaultSbtFiles => configurations( defaultSbtFiles.filter(sf.include), eval, build.imports )(loader)
case f: Plugins => plugins.filter(f.include).flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings)
case q: Sequence => q.sequence.flatMap(expand)
}
val ref = ProjectRef(uri, id)
val defineConfig: Seq[Setting[_]] = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
val builtin: Seq[Setting[_]] = (thisProject :== project) +: (thisProjectRef :== ref) +: defineConfig
val settings = builtin ++ project.settings ++ expand(project.auto) ++ injectSettings.project
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p) // map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
transformSettings(projectScope(ref), uri, rootProject, settings) transformSettings(projectScope(ref), uri, rootProject, settings)
} }
@ -221,8 +227,11 @@ object Load
loaded.units.toSeq flatMap { case (_, build) => loaded.units.toSeq flatMap { case (_, build) =>
build.unit.plugins.plugins flatMap { _.globalSettings } build.unit.plugins.plugins flatMap { _.globalSettings }
} }
@deprecated("No longer used.", "0.13.0")
def extractSettings(plugins: Seq[Plugin]): (Seq[Setting[_]], Seq[Setting[_]], Seq[Setting[_]]) = def extractSettings(plugins: Seq[Plugin]): (Seq[Setting[_]], Seq[Setting[_]], Seq[Setting[_]]) =
(plugins.flatMap(_.settings), plugins.flatMap(_.projectSettings), plugins.flatMap(_.buildSettings)) (plugins.flatMap(_.settings), plugins.flatMap(_.projectSettings), plugins.flatMap(_.buildSettings))
def transformProjectOnly(uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] = def transformProjectOnly(uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
Project.transform(Scope.resolveProject(uri, rootProject), settings) Project.transform(Scope.resolveProject(uri, rootProject), settings)
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] = def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =

View File

@ -24,6 +24,7 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
def dependencies: Seq[ClasspathDep[PR]] def dependencies: Seq[ClasspathDep[PR]]
def uses: Seq[PR] = aggregate ++ dependencies.map(_.project) def uses: Seq[PR] = aggregate ++ dependencies.map(_.project)
def referenced: Seq[PR] = delegates ++ uses def referenced: Seq[PR] = delegates ++ uses
def auto: AddSettings
override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode
override final def equals(o: Any) = o match { override final def equals(o: Any) = o match {
@ -34,23 +35,24 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
} }
sealed trait Project extends ProjectDefinition[ProjectReference] sealed trait Project extends ProjectDefinition[ProjectReference]
{ {
def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies, delegates: => Seq[ProjectReference] = delegates, def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies,
settings: => Seq[Project.Setting[_]] = settings, configurations: Seq[Configuration] = configurations): Project = delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Project.Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations) auto: AddSettings = auto): Project =
Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto)
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject = def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
{ {
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
def resolveDep(d: ClasspathDep[ProjectReference]) = ResolvedClasspathDependency(resolveRef(d.project), d.configuration) def resolveDep(d: ClasspathDep[ProjectReference]) = ResolvedClasspathDependency(resolveRef(d.project), d.configuration)
resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations) resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations, auto)
} }
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project = def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
{ {
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
def resolveDep(d: ClasspathDep[ProjectReference]) = ClasspathDependency(resolveRef(d.project), d.configuration) def resolveDep(d: ClasspathDep[ProjectReference]) = ClasspathDependency(resolveRef(d.project), d.configuration)
apply(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations) apply(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations, auto)
} }
def overrideConfigs(cs: Configuration*): Project = copy(configurations = Defaults.overrideConfigs(cs : _*)(configurations)) def overrideConfigs(cs: Configuration*): Project = copy(configurations = Defaults.overrideConfigs(cs : _*)(configurations))
@ -60,6 +62,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
def aggregate(refs: ProjectReference*): Project = copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs) def aggregate(refs: ProjectReference*): Project = copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs)
def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs) def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs)
def settings(ss: Project.Setting[_]*): Project = copy(settings = (settings: Seq[Project.Setting[_]]) ++ ss) def settings(ss: Project.Setting[_]*): Project = copy(settings = (settings: Seq[Project.Setting[_]]) ++ ss)
def autoSettings(select: AddSettings*): Project = copy(auto = AddSettings.seq(select : _*))
} }
sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] sealed trait ResolvedProject extends ProjectDefinition[ProjectRef]
@ -138,8 +141,8 @@ object Project extends Init[Scope] with ProjectExtra
case _ => display(project) + "/" case _ => display(project) + "/"
} }
private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]], delegates0: => Seq[PR], private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]],
settings0: => Seq[Setting[_]], val configurations: Seq[Configuration]) extends ProjectDefinition[PR] delegates0: => Seq[PR], settings0: => Seq[Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings) extends ProjectDefinition[PR]
{ {
lazy val aggregate = aggregate0 lazy val aggregate = aggregate0
lazy val dependencies = dependencies0 lazy val dependencies = dependencies0
@ -149,16 +152,17 @@ object Project extends Init[Scope] with ProjectExtra
Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates
} }
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil, delegates: => Seq[ProjectReference] = Nil, def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil,
settings: => Seq[Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default): Project = delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
auto: AddSettings = AddSettings.allDefaults): Project =
{ {
DefaultParsers.parse(id, DefaultParsers.ID).left.foreach(errMsg => error("Invalid project ID: " + errMsg)) DefaultParsers.parse(id, DefaultParsers.ID).left.foreach(errMsg => error("Invalid project ID: " + errMsg))
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations) with Project new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with Project
} }
def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ResolvedClasspathDependency], delegates: => Seq[ProjectRef], def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ResolvedClasspathDependency], delegates: => Seq[ProjectRef],
settings: Seq[Setting[_]], configurations: Seq[Configuration]): ResolvedProject = settings: Seq[Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations) with ResolvedProject new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with ResolvedProject
def defaultSettings: Seq[Setting[_]] = Defaults.defaultSettings def defaultSettings: Seq[Setting[_]] = Defaults.defaultSettings

View File

@ -0,0 +1 @@
version := "ignored"

View File

@ -0,0 +1 @@
version := "1.3"

View File

@ -0,0 +1 @@
version := "ignored"

View File

@ -0,0 +1 @@
version := "1.4"

View File

@ -0,0 +1 @@
version := "1.1"

View File

@ -0,0 +1,31 @@
import sbt._
import Keys._
import AddSettings._
object B extends Build
{
// version should be from explicit/a.txt
lazy val root = project("root", "1.4") autoSettings( userSettings, sbtFiles(file("explicit/a.txt")) )
// version should be from global/user.sbt
lazy val a = project("a", "1.1") autoSettings( userSettings )
// version should be the default 0.1-SNAPSHOT
lazy val b = project("b", "0.1-SNAPSHOT") autoSettings()
// version should be from the explicit settings call
lazy val c = project("c", "0.9") settings(version := "0.9") autoSettings()
// version should be from d/build.sbt
lazy val d = project("d", "1.3") settings(version := "0.9") autoSettings( defaultSbtFiles )
// version should be from global/user.sbt
lazy val e = project("e", "1.1") settings(version := "0.9") autoSettings( defaultSbtFiles, sbtFiles(file("../explicit/a.txt")), userSettings )
def project(id: String, expectedVersion: String): Project = Project(id, if(id == "root") file(".") else file(id)) settings(
TaskKey[Unit]("check") <<= version map { v =>
assert(v == expectedVersion, "Expected version '" + expectedVersion + "', got: " + v)
}
)
}

View File

@ -0,0 +1 @@
version := "1.2"

View File

@ -0,0 +1,11 @@
> root/check
> a/check
> b/check
> c/check
> d/check
> e/check