diff --git a/main/AddSettings.scala b/main/AddSettings.scala new file mode 100644 index 000000000..bf509255a --- /dev/null +++ b/main/AddSettings.scala @@ -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) +} + diff --git a/main/Load.scala b/main/Load.scala index d7186bfe6..4c127d4cf 100755 --- a/main/Load.scala +++ b/main/Load.scala @@ -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[_]] = diff --git a/main/Project.scala b/main/Project.scala index 1a40ca79a..f87eff749 100755 --- a/main/Project.scala +++ b/main/Project.scala @@ -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 diff --git a/sbt/src/sbt-test/project/auto-settings/b/build.sbt b/sbt/src/sbt-test/project/auto-settings/b/build.sbt new file mode 100644 index 000000000..e864169d1 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/b/build.sbt @@ -0,0 +1 @@ +version := "ignored" \ No newline at end of file diff --git a/sbt/src/sbt-test/project/auto-settings/d/build.sbt b/sbt/src/sbt-test/project/auto-settings/d/build.sbt new file mode 100644 index 000000000..7e86dd0fb --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/d/build.sbt @@ -0,0 +1 @@ +version := "1.3" \ No newline at end of file diff --git a/sbt/src/sbt-test/project/auto-settings/e/build.sbt b/sbt/src/sbt-test/project/auto-settings/e/build.sbt new file mode 100644 index 000000000..e864169d1 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/e/build.sbt @@ -0,0 +1 @@ +version := "ignored" \ No newline at end of file diff --git a/sbt/src/sbt-test/project/auto-settings/explicit/a.txt b/sbt/src/sbt-test/project/auto-settings/explicit/a.txt new file mode 100644 index 000000000..873fd02b0 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/explicit/a.txt @@ -0,0 +1 @@ +version := "1.4" diff --git a/sbt/src/sbt-test/project/auto-settings/global/user.sbt b/sbt/src/sbt-test/project/auto-settings/global/user.sbt new file mode 100644 index 000000000..99be66402 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/global/user.sbt @@ -0,0 +1 @@ +version := "1.1" \ No newline at end of file diff --git a/sbt/src/sbt-test/project/auto-settings/project/P.scala b/sbt/src/sbt-test/project/auto-settings/project/P.scala new file mode 100644 index 000000000..eddc49a76 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/project/P.scala @@ -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) + } + ) +} diff --git a/sbt/src/sbt-test/project/auto-settings/root.sbt b/sbt/src/sbt-test/project/auto-settings/root.sbt new file mode 100644 index 000000000..7ec0d973f --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/root.sbt @@ -0,0 +1 @@ +version := "1.2" \ No newline at end of file diff --git a/sbt/src/sbt-test/project/auto-settings/test b/sbt/src/sbt-test/project/auto-settings/test new file mode 100644 index 000000000..249b0b2f9 --- /dev/null +++ b/sbt/src/sbt-test/project/auto-settings/test @@ -0,0 +1,11 @@ +> root/check + +> a/check + +> b/check + +> c/check + +> d/check + +> e/check \ No newline at end of file