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) =>
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
val plugins = build.unit.plugins.plugins
val (pluginSettings, pluginProjectSettings, pluginBuildSettings) = extractSettings(plugins)
val (pluginThisProject, pluginNotThis) = pluginSettings partition isProjectThis
val pluginBuildSettings = plugins.flatMap(_.buildSettings)
val pluginNotThis = plugins.flatMap(_.settings) filterNot isProjectThis
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 settings =
(thisProject :== project) +:
(thisProjectRef :== ref) +:
(defineConfig ++ project.settings ++ injectSettings.projectLoaded(loader) ++ pluginThisProject ++
pluginProjectSettings ++ configurations(srcs, eval, build.imports)(loader) ++ injectSettings.project)
lazy val defaultSbtFiles = configurationSources(project.base)
import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence}
def expand(auto: AddSettings): Seq[Setting[_]] = auto match {
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)
transformSettings(projectScope(ref), uri, rootProject, settings)
}
@ -221,8 +227,11 @@ object Load
loaded.units.toSeq flatMap { case (_, build) =>
build.unit.plugins.plugins flatMap { _.globalSettings }
}
@deprecated("No longer used.", "0.13.0")
def extractSettings(plugins: Seq[Plugin]): (Seq[Setting[_]], Seq[Setting[_]], Seq[Setting[_]]) =
(plugins.flatMap(_.settings), plugins.flatMap(_.projectSettings), plugins.flatMap(_.buildSettings))
def transformProjectOnly(uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
Project.transform(Scope.resolveProject(uri, rootProject), settings)
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 uses: Seq[PR] = aggregate ++ dependencies.map(_.project)
def referenced: Seq[PR] = delegates ++ uses
def auto: AddSettings
override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode
override final def equals(o: Any) = o match {
@ -34,23 +35,24 @@ sealed trait ProjectDefinition[PR <: 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,
settings: => Seq[Project.Setting[_]] = settings, configurations: Seq[Configuration] = configurations): Project =
Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations)
def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies,
delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Project.Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
auto: AddSettings = auto): Project =
Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto)
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
{
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
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 resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
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))
@ -60,6 +62,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
def aggregate(refs: ProjectReference*): Project = copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs)
def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs)
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]
@ -138,8 +141,8 @@ object Project extends Init[Scope] with ProjectExtra
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],
settings0: => Seq[Setting[_]], val configurations: Seq[Configuration]) extends ProjectDefinition[PR]
private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]],
delegates0: => Seq[PR], settings0: => Seq[Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings) extends ProjectDefinition[PR]
{
lazy val aggregate = aggregate0
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
}
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil, delegates: => Seq[ProjectReference] = Nil,
settings: => Seq[Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default): Project =
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil,
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))
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],
settings: Seq[Setting[_]], configurations: Seq[Configuration]): ResolvedProject =
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations) with ResolvedProject
settings: Seq[Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with ResolvedProject
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