mirror of https://github.com/sbt/sbt.git
Merge pull request #1165 from sbt/wip/auto-plugins-defaults
Use AutoPlugins for default project settings.
This commit is contained in:
commit
37cc659518
|
|
@ -12,11 +12,28 @@ object AddSettings
|
||||||
private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings
|
private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings
|
||||||
private[sbt] final object User extends AddSettings
|
private[sbt] final object User extends AddSettings
|
||||||
private[sbt] final class Plugins(val include: Plugin => Boolean) extends AddSettings
|
private[sbt] final class Plugins(val include: Plugin => Boolean) extends AddSettings
|
||||||
|
private[sbt] final class AutoPlugins(val include: AutoPlugin => Boolean) extends AddSettings
|
||||||
private[sbt] final class DefaultSbtFiles(val include: File => 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
|
private[sbt] final class SbtFiles(val files: Seq[File]) extends AddSettings
|
||||||
|
// Settings created with the Project().settings() commands in build.scala files.
|
||||||
|
private[sbt] final object ProjectSettings extends AddSettings
|
||||||
|
|
||||||
|
/** Adds all settings from autoplugins. */
|
||||||
|
val autoPlugins: AddSettings = new AutoPlugins(const(true)) // Note: We do not expose fine-grained autoplugins because
|
||||||
|
// it's dangerous to control at that level right now.
|
||||||
|
// Leaving the hook in place in case we need to expose
|
||||||
|
// it, but most likely it will remain locked out
|
||||||
|
// for users with an alternative ordering feature
|
||||||
|
// in place.
|
||||||
|
|
||||||
|
/** Settings specified in Build.scala `Project` constructors. */
|
||||||
|
val projectSettings: AddSettings = ProjectSettings
|
||||||
|
|
||||||
|
/** All plugins that aren't auto plugins. */
|
||||||
|
val nonAutoPlugins: AddSettings = plugins(const(true))
|
||||||
|
|
||||||
/** Adds all settings from a plugin to a project. */
|
/** Adds all settings from a plugin to a project. */
|
||||||
val allPlugins: AddSettings = plugins(const(true))
|
val allPlugins: AddSettings = seq(autoPlugins, nonAutoPlugins)
|
||||||
|
|
||||||
/** Allows the plugins whose names match the `names` filter to automatically add settings to a project. */
|
/** 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)
|
def plugins(include: Plugin => Boolean): AddSettings = new Plugins(include)
|
||||||
|
|
@ -33,7 +50,8 @@ object AddSettings
|
||||||
/** Includes settings automatically*/
|
/** Includes settings automatically*/
|
||||||
def seq(autos: AddSettings*): AddSettings = new Sequence(autos)
|
def seq(autos: AddSettings*): AddSettings = new Sequence(autos)
|
||||||
|
|
||||||
val allDefaults: AddSettings = seq(userSettings, allPlugins, defaultSbtFiles)
|
/** The default inclusion of settings. */
|
||||||
|
val allDefaults: AddSettings = seq(autoPlugins, projectSettings, userSettings, nonAutoPlugins, defaultSbtFiles)
|
||||||
|
|
||||||
/** Combines two automatic setting configurations. */
|
/** Combines two automatic setting configurations. */
|
||||||
def append(a: AddSettings, b: AddSettings): AddSettings = (a,b) match {
|
def append(a: AddSettings, b: AddSettings): AddSettings = (a,b) match {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ trait Build
|
||||||
{
|
{
|
||||||
def projectDefinitions(baseDirectory: File): Seq[Project] = projects
|
def projectDefinitions(baseDirectory: File): Seq[Project] = projects
|
||||||
def projects: Seq[Project] = ReflectUtilities.allVals[Project](this).values.toSeq
|
def projects: Seq[Project] = ReflectUtilities.allVals[Project](this).values.toSeq
|
||||||
|
// TODO: Should we grab the build core setting shere or in a plugin?
|
||||||
def settings: Seq[Setting[_]] = Defaults.buildCore
|
def settings: Seq[Setting[_]] = Defaults.buildCore
|
||||||
def buildLoaders: Seq[BuildLoader.Components] = Nil
|
def buildLoaders: Seq[BuildLoader.Components] = Nil
|
||||||
/** Explicitly defines the root project.
|
/** Explicitly defines the root project.
|
||||||
|
|
@ -46,8 +47,16 @@ object Build
|
||||||
@deprecated("Explicitly specify the ID", "0.13.0")
|
@deprecated("Explicitly specify the ID", "0.13.0")
|
||||||
def defaultProject(base: File): Project = defaultProject(defaultID(base), base)
|
def defaultProject(base: File): Project = defaultProject(defaultID(base), base)
|
||||||
def defaultProject(id: String, base: File): Project = Project(id, base).settings(
|
def defaultProject(id: String, base: File): Project = Project(id, base).settings(
|
||||||
|
// TODO - Can we move this somewhere else? ordering of settings is causing this to get borked.
|
||||||
// if the user has overridden the name, use the normal organization that is derived from the name.
|
// if the user has overridden the name, use the normal organization that is derived from the name.
|
||||||
organization <<= (thisProject, organization, name) { (p, o, n) => if(p.id == n) "default" else o }
|
organization := {
|
||||||
|
val overridden = thisProject.value.id == name.value
|
||||||
|
organization.?.value match {
|
||||||
|
case Some(o) if !overridden => o
|
||||||
|
case _ => "default"
|
||||||
|
}
|
||||||
|
//(thisProject, organization, name) { (p, o, n) => if(p.id == n) "default" else o }
|
||||||
|
}
|
||||||
)
|
)
|
||||||
def defaultAggregatedProject(id: String, base: File, agg: Seq[ProjectRef]): Project =
|
def defaultAggregatedProject(id: String, base: File, agg: Seq[ProjectRef]): Project =
|
||||||
defaultProject(id, base).aggregate(agg : _*)
|
defaultProject(id, base).aggregate(agg : _*)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Resolv
|
||||||
* In addition to auto-discovered [[Build]]s, this includes any auto-generated default [[Build]]s.
|
* In addition to auto-discovered [[Build]]s, this includes any auto-generated default [[Build]]s.
|
||||||
* @param projects The list of all [[Project]]s from all [[Build]]s.
|
* @param projects The list of all [[Project]]s from all [[Build]]s.
|
||||||
* These projects have not yet been resolved, but they have had auto-plugins applied.
|
* These projects have not yet been resolved, but they have had auto-plugins applied.
|
||||||
* In particular, each [[Project]]'s `autoPlugins` field is populated according to their configured `natures`
|
* In particular, each [[Project]]'s `autoPlugins` field is populated according to their configured `plugins`
|
||||||
* and their `settings` and `configurations` updated as appropriate.
|
* and their `settings` and `configurations` updated as appropriate.
|
||||||
* @param buildNames No longer used and will be deprecated once feasible.
|
* @param buildNames No longer used and will be deprecated once feasible.
|
||||||
*/
|
*/
|
||||||
|
|
@ -99,8 +99,8 @@ final class DetectedPlugins(val plugins: DetectedModules[Plugin], val autoImport
|
||||||
/** Sequence of import expressions for the build definition. This includes the names of the [[Plugin]], [[Build]], and [[AutoImport]] modules, but not the [[AutoPlugin]] modules. */
|
/** Sequence of import expressions for the build definition. This includes the names of the [[Plugin]], [[Build]], and [[AutoImport]] modules, but not the [[AutoPlugin]] modules. */
|
||||||
lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names ++ autoImports.names)
|
lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names ++ autoImports.names)
|
||||||
|
|
||||||
/** A function to select the right [[AutoPlugin]]s from [[autoPlugins]] given the defined [[Natures]] for a [[Project]]. */
|
/** A function to select the right [[AutoPlugin]]s from [[autoPlugins]] for a [[Project]]. */
|
||||||
lazy val compileNatures: Natures => Seq[AutoPlugin] = Natures.compile(autoPlugins.values.toList)
|
lazy val compilePlugins: Plugins => Seq[AutoPlugin] = Plugins.compile(autoPlugins.values.toList)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The built and loaded build definition project.
|
/** The built and loaded build definition project.
|
||||||
|
|
|
||||||
|
|
@ -56,94 +56,107 @@ object Defaults extends BuildCommon
|
||||||
def thisBuildCore: Seq[Setting[_]] = inScope(GlobalScope.copy(project = Select(ThisBuild)))(Seq(
|
def thisBuildCore: Seq[Setting[_]] = inScope(GlobalScope.copy(project = Select(ThisBuild)))(Seq(
|
||||||
managedDirectory := baseDirectory.value / "lib_managed"
|
managedDirectory := baseDirectory.value / "lib_managed"
|
||||||
))
|
))
|
||||||
|
@deprecated("0.13.2", "Use AutoPlugins and globalSbtCore instead.")
|
||||||
lazy val globalCore: Seq[Setting[_]] = globalDefaults(defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq(
|
lazy val globalCore: Seq[Setting[_]] = globalDefaults(defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq(
|
||||||
|
excludeFilter :== HiddenFileFilter
|
||||||
|
) ++ globalIvyCore ++ globalJvmCore) ++ globalSbtCore
|
||||||
|
|
||||||
|
private[sbt] lazy val globalJvmCore: Seq[Setting[_]] =
|
||||||
|
Seq(
|
||||||
compilerCache := state.value get Keys.stateCompilerCache getOrElse compiler.CompilerCache.fresh,
|
compilerCache := state.value get Keys.stateCompilerCache getOrElse compiler.CompilerCache.fresh,
|
||||||
crossVersion :== CrossVersion.Disabled,
|
sourcesInBase :== true,
|
||||||
|
autoAPIMappings := false,
|
||||||
|
apiMappings := Map.empty,
|
||||||
|
autoScalaLibrary :== true,
|
||||||
|
managedScalaInstance :== true,
|
||||||
|
definesClass :== FileValueCache(Locate.definesClass _ ).get,
|
||||||
|
traceLevel in run :== 0,
|
||||||
|
traceLevel in runMain :== 0,
|
||||||
|
traceLevel in console :== Int.MaxValue,
|
||||||
|
traceLevel in consoleProject :== Int.MaxValue,
|
||||||
|
autoCompilerPlugins :== true,
|
||||||
|
scalaHome :== None,
|
||||||
|
apiURL := None,
|
||||||
|
javaHome :== None,
|
||||||
|
testForkedParallel :== false,
|
||||||
|
javaOptions :== Nil,
|
||||||
|
sbtPlugin :== false,
|
||||||
|
crossPaths :== true,
|
||||||
|
sourcePositionMappers :== Nil,
|
||||||
|
artifactClassifier in packageSrc :== Some(SourceClassifier),
|
||||||
|
artifactClassifier in packageDoc :== Some(DocClassifier),
|
||||||
|
includeFilter :== NothingFilter,
|
||||||
|
includeFilter in unmanagedSources :== "*.java" | "*.scala",
|
||||||
|
includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip",
|
||||||
|
includeFilter in unmanagedResources :== AllPassFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
private[sbt] lazy val globalIvyCore: Seq[Setting[_]] =
|
||||||
|
Seq(
|
||||||
|
internalConfigurationMap :== Configurations.internalMap _,
|
||||||
|
credentials :== Nil,
|
||||||
|
exportJars :== false,
|
||||||
|
retrieveManaged :== false,
|
||||||
scalaOrganization :== ScalaArtifacts.Organization,
|
scalaOrganization :== ScalaArtifacts.Organization,
|
||||||
|
sbtResolver := { if(sbtVersion.value endsWith "-SNAPSHOT") Classpaths.typesafeSnapshots else Classpaths.typesafeReleases },
|
||||||
|
crossVersion :== CrossVersion.Disabled,
|
||||||
buildDependencies <<= Classpaths.constructBuildDependencies,
|
buildDependencies <<= Classpaths.constructBuildDependencies,
|
||||||
|
version :== "0.1-SNAPSHOT",
|
||||||
|
classpathTypes :== Set("jar", "bundle") ++ CustomPomParser.JarPackagings,
|
||||||
|
artifactClassifier :== None,
|
||||||
|
checksums := Classpaths.bootChecksums(appConfiguration.value),
|
||||||
|
conflictManager := ConflictManager.default,
|
||||||
|
pomExtra :== NodeSeq.Empty,
|
||||||
|
pomPostProcess :== idFun,
|
||||||
|
pomAllRepositories :== false,
|
||||||
|
pomIncludeRepository :== Classpaths.defaultRepositoryFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */
|
||||||
|
private[sbt] lazy val globalSbtCore: Seq[Setting[_]] = globalDefaults(Seq(
|
||||||
|
outputStrategy :== None, // TODO - This might belong elsewhere.
|
||||||
|
buildStructure := Project.structure(state.value),
|
||||||
|
settingsData := buildStructure.value.data,
|
||||||
|
trapExit :== true,
|
||||||
|
connectInput :== false,
|
||||||
|
cancelable :== false,
|
||||||
|
envVars :== Map.empty,
|
||||||
|
sbtVersion := appConfiguration.value.provider.id.version,
|
||||||
|
sbtBinaryVersion := binarySbtVersion(sbtVersion.value),
|
||||||
|
watchingMessage := Watched.defaultWatchingMessage,
|
||||||
|
triggeredMessage := Watched.defaultTriggeredMessage,
|
||||||
|
onLoad := idFun[State],
|
||||||
|
onUnload := idFun[State],
|
||||||
|
onUnload := { s => try onUnload.value(s) finally IO.delete(taskTemporaryDirectory.value) },
|
||||||
|
extraLoggers :== { _ => Nil },
|
||||||
|
watchSources :== Nil,
|
||||||
|
skip :== false,
|
||||||
taskTemporaryDirectory := { val dir = IO.createTemporaryDirectory; dir.deleteOnExit(); dir },
|
taskTemporaryDirectory := { val dir = IO.createTemporaryDirectory; dir.deleteOnExit(); dir },
|
||||||
onComplete := { val dir = taskTemporaryDirectory.value; () => {IO.delete(dir); IO.createDirectory(dir) }},
|
onComplete := { val dir = taskTemporaryDirectory.value; () => {IO.delete(dir); IO.createDirectory(dir) }},
|
||||||
Previous.cache <<= Previous.cacheSetting,
|
Previous.cache <<= Previous.cacheSetting,
|
||||||
Previous.references :== new Previous.References,
|
Previous.references :== new Previous.References,
|
||||||
concurrentRestrictions <<= defaultRestrictions,
|
concurrentRestrictions <<= defaultRestrictions,
|
||||||
parallelExecution :== true,
|
parallelExecution :== true,
|
||||||
sbtVersion := appConfiguration.value.provider.id.version,
|
|
||||||
sbtBinaryVersion := binarySbtVersion(sbtVersion.value),
|
|
||||||
sbtResolver := { if(sbtVersion.value endsWith "-SNAPSHOT") Classpaths.typesafeSnapshots else Classpaths.typesafeReleases },
|
|
||||||
pollInterval :== 500,
|
pollInterval :== 500,
|
||||||
logBuffered :== false,
|
logBuffered :== false,
|
||||||
connectInput :== false,
|
commands :== Nil,
|
||||||
cancelable :== false,
|
showSuccess :== true,
|
||||||
envVars :== Map.empty,
|
|
||||||
sourcesInBase :== true,
|
|
||||||
autoAPIMappings := false,
|
|
||||||
apiMappings := Map.empty,
|
|
||||||
autoScalaLibrary :== true,
|
|
||||||
managedScalaInstance :== true,
|
|
||||||
onLoad := idFun[State],
|
|
||||||
onUnload := idFun[State],
|
|
||||||
onUnload := { s => try onUnload.value(s) finally IO.delete(taskTemporaryDirectory.value) },
|
|
||||||
watchingMessage := Watched.defaultWatchingMessage,
|
|
||||||
triggeredMessage := Watched.defaultTriggeredMessage,
|
|
||||||
definesClass :== FileValueCache(Locate.definesClass _ ).get,
|
|
||||||
trapExit :== true,
|
|
||||||
traceLevel in run :== 0,
|
|
||||||
traceLevel in runMain :== 0,
|
|
||||||
traceLevel in console :== Int.MaxValue,
|
|
||||||
traceLevel in consoleProject :== Int.MaxValue,
|
|
||||||
autoCompilerPlugins :== true,
|
|
||||||
internalConfigurationMap :== Configurations.internalMap _,
|
|
||||||
initialize :== {},
|
|
||||||
credentials :== Nil,
|
|
||||||
scalaHome :== None,
|
|
||||||
apiURL := None,
|
|
||||||
javaHome :== None,
|
|
||||||
extraLoggers :== { _ => Nil },
|
|
||||||
skip :== false,
|
|
||||||
watchSources :== Nil,
|
|
||||||
version :== "0.1-SNAPSHOT",
|
|
||||||
outputStrategy :== None,
|
|
||||||
exportJars :== false,
|
|
||||||
fork :== false,
|
|
||||||
testForkedParallel :== false,
|
|
||||||
javaOptions :== Nil,
|
|
||||||
sbtPlugin :== false,
|
|
||||||
crossPaths :== true,
|
|
||||||
classpathTypes :== Set("jar", "bundle") ++ CustomPomParser.JarPackagings,
|
|
||||||
aggregate :== true,
|
|
||||||
maxErrors :== 100,
|
|
||||||
sourcePositionMappers :== Nil,
|
|
||||||
showTiming :== true,
|
showTiming :== true,
|
||||||
timingFormat :== Aggregation.defaultFormat,
|
timingFormat :== Aggregation.defaultFormat,
|
||||||
showSuccess :== true,
|
aggregate :== true,
|
||||||
commands :== Nil,
|
maxErrors :== 100,
|
||||||
retrieveManaged :== false,
|
fork :== false,
|
||||||
buildStructure := Project.structure(state.value),
|
initialize :== {}
|
||||||
settingsData := buildStructure.value.data,
|
|
||||||
artifactClassifier :== None,
|
|
||||||
artifactClassifier in packageSrc :== Some(SourceClassifier),
|
|
||||||
artifactClassifier in packageDoc :== Some(DocClassifier),
|
|
||||||
checksums := Classpaths.bootChecksums(appConfiguration.value),
|
|
||||||
conflictManager := ConflictManager.default,
|
|
||||||
pomExtra :== NodeSeq.Empty,
|
|
||||||
pomPostProcess :== idFun,
|
|
||||||
pomAllRepositories :== false,
|
|
||||||
includeFilter :== NothingFilter,
|
|
||||||
includeFilter in unmanagedSources :== "*.java" | "*.scala",
|
|
||||||
includeFilter in unmanagedJars :== "*.jar" | "*.so" | "*.dll" | "*.jnilib" | "*.zip",
|
|
||||||
includeFilter in unmanagedResources :== AllPassFilter,
|
|
||||||
excludeFilter :== HiddenFileFilter,
|
|
||||||
pomIncludeRepository :== Classpaths.defaultRepositoryFilter
|
|
||||||
))
|
))
|
||||||
def defaultTestTasks(key: Scoped): Seq[Setting[_]] = inTask(key)(Seq(
|
def defaultTestTasks(key: Scoped): Seq[Setting[_]] = inTask(key)(Seq(
|
||||||
tags := Seq(Tags.Test -> 1),
|
tags := Seq(Tags.Test -> 1),
|
||||||
logBuffered := true
|
logBuffered := true
|
||||||
))
|
))
|
||||||
|
// TODO: This should be on the new default settings for a project.
|
||||||
def projectCore: Seq[Setting[_]] = Seq(
|
def projectCore: Seq[Setting[_]] = Seq(
|
||||||
name := thisProject.value.id,
|
name := thisProject.value.id,
|
||||||
logManager := LogManager.defaults(extraLoggers.value, StandardMain.console),
|
logManager := LogManager.defaults(extraLoggers.value, StandardMain.console),
|
||||||
onLoadMessage <<= onLoadMessage or (name, thisProjectRef)("Set current project to " + _ + " (in build " + _.build +")"),
|
onLoadMessage <<= onLoadMessage or (name, thisProjectRef)("Set current project to " + _ + " (in build " + _.build +")")
|
||||||
runnerTask
|
|
||||||
)
|
)
|
||||||
def paths = Seq(
|
def paths = Seq(
|
||||||
baseDirectory := thisProject.value.base,
|
baseDirectory := thisProject.value.base,
|
||||||
|
|
@ -852,6 +865,7 @@ object Defaults extends BuildCommon
|
||||||
lazy val disableAggregation = Defaults.globalDefaults( noAggregation map disableAggregate )
|
lazy val disableAggregation = Defaults.globalDefaults( noAggregation map disableAggregate )
|
||||||
def disableAggregate(k: Scoped) = aggregate in k :== false
|
def disableAggregate(k: Scoped) = aggregate in k :== false
|
||||||
|
|
||||||
|
lazy val runnerSettings: Seq[Setting[_]] = Seq(runnerTask)
|
||||||
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
|
lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ packageBase
|
||||||
|
|
||||||
lazy val baseClasspaths: Seq[Setting[_]] = Classpaths.publishSettings ++ Classpaths.baseSettings
|
lazy val baseClasspaths: Seq[Setting[_]] = Classpaths.publishSettings ++ Classpaths.baseSettings
|
||||||
|
|
@ -865,7 +879,12 @@ object Defaults extends BuildCommon
|
||||||
|
|
||||||
|
|
||||||
// settings that are not specific to a configuration
|
// settings that are not specific to a configuration
|
||||||
lazy val projectBaseSettings: Seq[Setting[_]] = projectCore ++ paths ++ baseClasspaths ++ baseTasks ++ compileBase ++ disableAggregation
|
@deprecated("0.13.2", "Settings now split into AutoPlugins.")
|
||||||
|
lazy val projectBaseSettings: Seq[Setting[_]] = projectCore ++ runnerSettings ++ paths ++ baseClasspaths ++ baseTasks ++ compileBase ++ disableAggregation
|
||||||
|
|
||||||
|
// These are project level settings that MUST be on every project.
|
||||||
|
lazy val coreDefaultSettings: Seq[Setting[_]] = projectCore ++ disableAggregation
|
||||||
|
@deprecated("0.13.2", "Default settings split into `coreDefaultSettings` and IvyModule/JvmModule plugins.")
|
||||||
lazy val defaultSettings: Seq[Setting[_]] = projectBaseSettings ++ defaultConfigs
|
lazy val defaultSettings: Seq[Setting[_]] = projectBaseSettings ++ defaultConfigs
|
||||||
}
|
}
|
||||||
object Classpaths
|
object Classpaths
|
||||||
|
|
@ -935,9 +954,14 @@ object Classpaths
|
||||||
publishArtifact in Test:== false
|
publishArtifact in Test:== false
|
||||||
))
|
))
|
||||||
|
|
||||||
val publishSettings: Seq[Setting[_]] = publishGlobalDefaults ++ Seq(
|
val jvmPublishSettings: Seq[Setting[_]] = Seq(
|
||||||
artifacts <<= artifactDefs(defaultArtifactTasks),
|
artifacts <<= artifactDefs(defaultArtifactTasks),
|
||||||
packagedArtifacts <<= packaged(defaultArtifactTasks),
|
packagedArtifacts <<= packaged(defaultArtifactTasks)
|
||||||
|
)
|
||||||
|
|
||||||
|
val ivyPublishSettings: Seq[Setting[_]] = publishGlobalDefaults ++ Seq(
|
||||||
|
artifacts :== Nil,
|
||||||
|
packagedArtifacts :== Map.empty,
|
||||||
makePom := { val config = makePomConfiguration.value; IvyActions.makePom(ivyModule.value, config, streams.value.log); config.file },
|
makePom := { val config = makePomConfiguration.value; IvyActions.makePom(ivyModule.value, config, streams.value.log); config.file },
|
||||||
packagedArtifact in makePom := (artifact in makePom value, makePom value),
|
packagedArtifact in makePom := (artifact in makePom value, makePom value),
|
||||||
deliver <<= deliverTask(deliverConfiguration),
|
deliver <<= deliverTask(deliverConfiguration),
|
||||||
|
|
@ -946,6 +970,8 @@ object Classpaths
|
||||||
publishLocal <<= publishTask(publishLocalConfiguration, deliverLocal),
|
publishLocal <<= publishTask(publishLocalConfiguration, deliverLocal),
|
||||||
publishM2 <<= publishTask(publishM2Configuration, deliverLocal)
|
publishM2 <<= publishTask(publishM2Configuration, deliverLocal)
|
||||||
)
|
)
|
||||||
|
@deprecated("0.13.2", "This has been split into jvmIvySettings and ivyPublishSettings.")
|
||||||
|
val publishSettings: Seq[Setting[_]] = jvmPublishSettings ++ ivyPublishSettings
|
||||||
|
|
||||||
private[this] def baseGlobalDefaults = Defaults.globalDefaults(Seq(
|
private[this] def baseGlobalDefaults = Defaults.globalDefaults(Seq(
|
||||||
conflictWarning :== ConflictWarning.default("global"),
|
conflictWarning :== ConflictWarning.default("global"),
|
||||||
|
|
@ -976,7 +1002,7 @@ object Classpaths
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
val baseSettings: Seq[Setting[_]] = baseGlobalDefaults ++ sbtClassifiersTasks ++ Seq(
|
val ivyBaseSettings: Seq[Setting[_]] = baseGlobalDefaults ++ sbtClassifiersTasks ++ Seq(
|
||||||
conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)),
|
conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)),
|
||||||
unmanagedBase := baseDirectory.value / "lib",
|
unmanagedBase := baseDirectory.value / "lib",
|
||||||
normalizedName := Project.normalizeModuleID(name.value),
|
normalizedName := Project.normalizeModuleID(name.value),
|
||||||
|
|
@ -1007,14 +1033,11 @@ object Classpaths
|
||||||
otherResolvers := Resolver.publishMavenLocal :: publishTo.value.toList,
|
otherResolvers := Resolver.publishMavenLocal :: publishTo.value.toList,
|
||||||
projectResolver <<= projectResolverTask,
|
projectResolver <<= projectResolverTask,
|
||||||
projectDependencies <<= projectDependenciesTask,
|
projectDependencies <<= projectDependenciesTask,
|
||||||
libraryDependencies ++= autoLibraryDependency(autoScalaLibrary.value && !scalaHome.value.isDefined && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, scalaVersion.value),
|
// TODO - Is this the appropriate split? Ivy defines this simply as
|
||||||
|
// just project + library, while the JVM plugin will define it as
|
||||||
|
// having the additional sbtPlugin + autoScala magikz.
|
||||||
allDependencies := {
|
allDependencies := {
|
||||||
val base = projectDependencies.value ++ libraryDependencies.value
|
projectDependencies.value ++ libraryDependencies.value
|
||||||
val pluginAdjust = if(sbtPlugin.value) sbtDependency.value.copy(configurations = Some(Provided.name)) +: base else base
|
|
||||||
if(scalaHome.value.isDefined || ivyScala.value.isEmpty || !managedScalaInstance.value)
|
|
||||||
pluginAdjust
|
|
||||||
else
|
|
||||||
ScalaArtifacts.toolDependencies(scalaOrganization.value, scalaVersion.value) ++ pluginAdjust
|
|
||||||
},
|
},
|
||||||
ivyScala <<= ivyScala or (scalaHome, scalaVersion in update, scalaBinaryVersion in update, scalaOrganization) { (sh,fv,bv,so) =>
|
ivyScala <<= ivyScala or (scalaHome, scalaVersion in update, scalaBinaryVersion in update, scalaOrganization) { (sh,fv,bv,so) =>
|
||||||
Some(new IvyScala(fv, bv, Nil, filterImplicit = false, checkExplicit = true, overrideScalaVersion = false, scalaOrganization = so))
|
Some(new IvyScala(fv, bv, Nil, filterImplicit = false, checkExplicit = true, overrideScalaVersion = false, scalaOrganization = so))
|
||||||
|
|
@ -1054,6 +1077,22 @@ object Classpaths
|
||||||
}
|
}
|
||||||
} tag(Tags.Update, Tags.Network)
|
} tag(Tags.Update, Tags.Network)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val jvmBaseSettings: Seq[Setting[_]] = Seq(
|
||||||
|
libraryDependencies ++= autoLibraryDependency(autoScalaLibrary.value && !scalaHome.value.isDefined && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, scalaVersion.value),
|
||||||
|
// Override the default to handle mixing in the sbtPlugin + scala dependencies.
|
||||||
|
allDependencies := {
|
||||||
|
val base = projectDependencies.value ++ libraryDependencies.value
|
||||||
|
val pluginAdjust = if(sbtPlugin.value) sbtDependency.value.copy(configurations = Some(Provided.name)) +: base else base
|
||||||
|
if(scalaHome.value.isDefined || ivyScala.value.isEmpty || !managedScalaInstance.value)
|
||||||
|
pluginAdjust
|
||||||
|
else
|
||||||
|
ScalaArtifacts.toolDependencies(scalaOrganization.value, scalaVersion.value) ++ pluginAdjust
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@deprecated("0.13.2", "Split into ivyBaseSettings and jvmBaseSettings.")
|
||||||
|
val baseSettings: Seq[Setting[_]] = ivyBaseSettings ++ jvmBaseSettings
|
||||||
|
|
||||||
def warnResolversConflict(ress: Seq[Resolver], log: Logger) {
|
def warnResolversConflict(ress: Seq[Resolver], log: Logger) {
|
||||||
val resset = ress.toSet
|
val resset = ress.toSet
|
||||||
for ((name, r) <- resset groupBy (_.name) if r.size > 1) {
|
for ((name, r) <- resset groupBy (_.name) if r.size > 1) {
|
||||||
|
|
|
||||||
|
|
@ -459,20 +459,19 @@ object Load
|
||||||
|
|
||||||
private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] =
|
private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] =
|
||||||
{
|
{
|
||||||
def loadSbtFiles(auto: AddSettings, base: File, autoPlugins: Seq[AutoPlugin]): LoadedSbtFile =
|
def loadSbtFiles(auto: AddSettings, base: File, autoPlugins: Seq[AutoPlugin], projectSettings: Seq[Setting[_]]): LoadedSbtFile =
|
||||||
loadSettings(auto, base, plugins, eval, injectSettings, memoSettings, autoPlugins)
|
loadSettings(auto, base, plugins, eval, injectSettings, memoSettings, autoPlugins, projectSettings)
|
||||||
def loadForProjects = newProjects map { project =>
|
def loadForProjects = newProjects map { project =>
|
||||||
val autoPlugins =
|
val autoPlugins =
|
||||||
try plugins.detected.compileNatures(project.natures)
|
try plugins.detected.compilePlugins(project.plugins)
|
||||||
catch { case e: AutoPluginException => throw translateAutoPluginException(e, project) }
|
catch { case e: AutoPluginException => throw translateAutoPluginException(e, project) }
|
||||||
val autoConfigs = autoPlugins.flatMap(_.projectConfigurations)
|
val autoConfigs = autoPlugins.flatMap(_.projectConfigurations)
|
||||||
val loadedSbtFiles = loadSbtFiles(project.auto, project.base, autoPlugins)
|
val loadedSbtFiles = loadSbtFiles(project.auto, project.base, autoPlugins, project.settings)
|
||||||
val newSettings = (project.settings: Seq[Setting[_]]) ++ loadedSbtFiles.settings
|
|
||||||
// add the automatically selected settings, record the selected AutoPlugins, and register the automatically selected configurations
|
// add the automatically selected settings, record the selected AutoPlugins, and register the automatically selected configurations
|
||||||
val transformed = project.copy(settings = newSettings).setAutoPlugins(autoPlugins).overrideConfigs(autoConfigs : _*)
|
val transformed = project.copy(settings = loadedSbtFiles.settings).setAutoPlugins(autoPlugins).overrideConfigs(autoConfigs : _*)
|
||||||
(transformed, loadedSbtFiles.projects)
|
(transformed, loadedSbtFiles.projects)
|
||||||
}
|
}
|
||||||
def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase, Nil).projects
|
def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase, Nil, Nil).projects
|
||||||
val (nextProjects, loadedProjects) =
|
val (nextProjects, loadedProjects) =
|
||||||
if(newProjects.isEmpty) // load the .sbt files in the root directory to look for Projects
|
if(newProjects.isEmpty) // load the .sbt files in the root directory to look for Projects
|
||||||
(defaultLoad, acc)
|
(defaultLoad, acc)
|
||||||
|
|
@ -489,7 +488,7 @@ object Load
|
||||||
private[this] def translateAutoPluginException(e: AutoPluginException, project: Project): AutoPluginException =
|
private[this] def translateAutoPluginException(e: AutoPluginException, project: Project): AutoPluginException =
|
||||||
e.withPrefix(s"Error determining plugins for project '${project.id}' in ${project.base}:\n")
|
e.withPrefix(s"Error determining plugins for project '${project.id}' in ${project.base}:\n")
|
||||||
|
|
||||||
private[this] def loadSettings(auto: AddSettings, projectBase: File, loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile], autoPlugins: Seq[AutoPlugin]): LoadedSbtFile =
|
private[this] def loadSettings(auto: AddSettings, projectBase: File, loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile], autoPlugins: Seq[AutoPlugin], projectSettings: Seq[Setting[_]]): LoadedSbtFile =
|
||||||
{
|
{
|
||||||
lazy val defaultSbtFiles = configurationSources(projectBase)
|
lazy val defaultSbtFiles = configurationSources(projectBase)
|
||||||
def settings(ss: Seq[Setting[_]]) = new LoadedSbtFile(ss, Nil, Nil)
|
def settings(ss: Seq[Setting[_]]) = new LoadedSbtFile(ss, Nil, Nil)
|
||||||
|
|
@ -506,18 +505,23 @@ object Load
|
||||||
def loadSettingsFile(src: File): LoadedSbtFile =
|
def loadSettingsFile(src: File): LoadedSbtFile =
|
||||||
EvaluateConfigurations.evaluateSbtFile(eval(), src, IO.readLines(src), loadedPlugins.detected.imports, 0)(loader)
|
EvaluateConfigurations.evaluateSbtFile(eval(), src, IO.readLines(src), loadedPlugins.detected.imports, 0)(loader)
|
||||||
|
|
||||||
import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence}
|
import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,AutoPlugins,Sequence, ProjectSettings}
|
||||||
def pluginSettings(f: Plugins) = {
|
def pluginSettings(f: Plugins) = {
|
||||||
val included = loadedPlugins.detected.plugins.values.filter(f.include) // don't apply the filter to AutoPlugins, only Plugins
|
val included = loadedPlugins.detected.plugins.values.filter(f.include) // don't apply the filter to AutoPlugins, only Plugins
|
||||||
val oldStyle = included.flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings)
|
included.flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings)
|
||||||
val autoStyle = autoPlugins.flatMap(_.projectSettings)
|
|
||||||
oldStyle ++ autoStyle
|
|
||||||
}
|
}
|
||||||
|
// Filter the AutoPlugin settings we included based on which ones are
|
||||||
|
// intended in the AddSettings.AutoPlugins filter.
|
||||||
|
def autoPluginSettings(f: AutoPlugins) =
|
||||||
|
autoPlugins.filter(f.include).flatMap(_.projectSettings)
|
||||||
|
|
||||||
def expand(auto: AddSettings): LoadedSbtFile = auto match {
|
def expand(auto: AddSettings): LoadedSbtFile = auto match {
|
||||||
|
case ProjectSettings => settings(projectSettings)
|
||||||
case User => settings(injectSettings.projectLoaded(loader))
|
case User => settings(injectSettings.projectLoaded(loader))
|
||||||
case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f)))
|
case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f)))
|
||||||
case sf: DefaultSbtFiles => loadSettings( defaultSbtFiles.filter(sf.include))
|
case sf: DefaultSbtFiles => loadSettings( defaultSbtFiles.filter(sf.include))
|
||||||
case p: Plugins => settings(pluginSettings(p))
|
case p: Plugins => settings(pluginSettings(p))
|
||||||
|
case p: AutoPlugins => settings(autoPluginSettings(p))
|
||||||
case q: Sequence => (LoadedSbtFile.empty /: q.sequence) { (b,add) => b.merge( expand(add) ) }
|
case q: Sequence => (LoadedSbtFile.empty /: q.sequence) { (b,add) => b.merge( expand(add) ) }
|
||||||
}
|
}
|
||||||
expand(auto)
|
expand(auto)
|
||||||
|
|
|
||||||
|
|
@ -376,16 +376,16 @@ object BuiltinCommands
|
||||||
else
|
else
|
||||||
Help.empty
|
Help.empty
|
||||||
def plugins = Command.command(PluginsCommand, pluginsBrief, pluginsDetailed) { s =>
|
def plugins = Command.command(PluginsCommand, pluginsBrief, pluginsDetailed) { s =>
|
||||||
val helpString = NaturesDebug.helpAll(s)
|
val helpString = PluginsDebug.helpAll(s)
|
||||||
System.out.println(helpString)
|
System.out.println(helpString)
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
val pluginParser: State => Parser[AutoPlugin] = s => {
|
val pluginParser: State => Parser[AutoPlugin] = s => {
|
||||||
val autoPlugins: Map[String, AutoPlugin] = NaturesDebug.autoPluginMap(s)
|
val autoPlugins: Map[String, AutoPlugin] = PluginsDebug.autoPluginMap(s)
|
||||||
token(Space) ~> Act.knownIDParser(autoPlugins, "plugin")
|
token(Space) ~> Act.knownIDParser(autoPlugins, "plugin")
|
||||||
}
|
}
|
||||||
def plugin = Command(PluginCommand)(pluginParser) { (s, plugin) =>
|
def plugin = Command(PluginCommand)(pluginParser) { (s, plugin) =>
|
||||||
val helpString = NaturesDebug.help(plugin, s)
|
val helpString = PluginsDebug.help(plugin, s)
|
||||||
System.out.println(helpString)
|
System.out.println(helpString)
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,15 @@ object PluginDiscovery
|
||||||
def discover[T](resource: String)(implicit mf: reflect.ClassManifest[T]) =
|
def discover[T](resource: String)(implicit mf: reflect.ClassManifest[T]) =
|
||||||
binarySourceModules[T](data, loader, resource)
|
binarySourceModules[T](data, loader, resource)
|
||||||
import Paths._
|
import Paths._
|
||||||
new DetectedPlugins(discover[Plugin](Plugins), discover[AutoImport](AutoImports), discover[AutoPlugin](AutoPlugins), discover[Build](Builds))
|
// TODO - Fix this once we can autodetect AutoPlugins defined by sbt itself.
|
||||||
|
val defaultAutoPlugins = Seq(
|
||||||
|
"sbt.plugins.IvyModule" -> sbt.plugins.IvyModule,
|
||||||
|
"sbt.plugins.JvmModule" -> sbt.plugins.JvmModule,
|
||||||
|
"sbt.plugins.GlobalModule" -> sbt.plugins.GlobalModule
|
||||||
|
)
|
||||||
|
val detectedAutoPugins = discover[AutoPlugin](AutoPlugins)
|
||||||
|
val allAutoPlugins = new DetectedModules(defaultAutoPlugins ++ detectedAutoPugins.modules)
|
||||||
|
new DetectedPlugins(discover[Plugin](Plugins), discover[AutoImport](AutoImports), allAutoPlugins, discover[Build](Builds))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Discovers the sbt-plugin-related top-level modules from the provided source `analysis`. */
|
/** Discovers the sbt-plugin-related top-level modules from the provided source `analysis`. */
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ TODO:
|
||||||
import logic.{Atom, Clause, Clauses, Formula, Literal, Logic, Negated}
|
import logic.{Atom, Clause, Clauses, Formula, Literal, Logic, Negated}
|
||||||
import Logic.{CyclicNegation, InitialContradictions, InitialOverlap, LogicException}
|
import Logic.{CyclicNegation, InitialContradictions, InitialOverlap, LogicException}
|
||||||
import Def.Setting
|
import Def.Setting
|
||||||
import Natures._
|
import Plugins._
|
||||||
|
|
||||||
/** Marks a top-level object so that sbt will wildcard import it for .sbt files, `consoleProject`, and `set`. */
|
/** Marks a top-level object so that sbt will wildcard import it for .sbt files, `consoleProject`, and `set`. */
|
||||||
trait AutoImport
|
trait AutoImport
|
||||||
|
|
@ -18,11 +18,11 @@ An AutoPlugin defines a group of settings and the conditions where the settings
|
||||||
The `select` method defines the conditions and a method like `projectSettings` defines the settings to add.
|
The `select` method defines the conditions and a method like `projectSettings` defines the settings to add.
|
||||||
|
|
||||||
Steps for plugin authors:
|
Steps for plugin authors:
|
||||||
1. Determine the [[Nature]]s that, when present (or absent), activate the AutoPlugin.
|
1. Determine the [[AutoPlugins]]s that, when present (or absent), activate the AutoPlugin.
|
||||||
2. Determine the settings/configurations to automatically inject when activated.
|
2. Determine the settings/configurations to automatically inject when activated.
|
||||||
|
|
||||||
For example, the following will automatically add the settings in `projectSettings`
|
For example, the following will automatically add the settings in `projectSettings`
|
||||||
to a project that has both the `Web` and `Javascript` natures enabled.
|
to a project that has both the `Web` and `Javascript` plugins enabled.
|
||||||
|
|
||||||
object MyPlugin extends AutoPlugin {
|
object MyPlugin extends AutoPlugin {
|
||||||
def select = Web && Javascript
|
def select = Web && Javascript
|
||||||
|
|
@ -30,28 +30,28 @@ For example, the following will automatically add the settings in `projectSettin
|
||||||
}
|
}
|
||||||
|
|
||||||
Steps for users:
|
Steps for users:
|
||||||
1. Add dependencies on plugins as usual with addSbtPlugin
|
1. Add dependencies on plugins in `project/plugins.sbt` as usual with `addSbtPlugin`
|
||||||
2. Add Natures to Projects, which will automatically select the plugin settings to add for those Projects.
|
2. Add key plugins to Projects, which will automatically select the plugin + dependent plugin settings to add for those Projects.
|
||||||
3. Exclude plugins, if desired.
|
3. Exclude plugins, if desired.
|
||||||
|
|
||||||
For example, given natures Web and Javascript (perhaps provided by plugins added with addSbtPlugin),
|
For example, given plugins Web and Javascript (perhaps provided by plugins added with addSbtPlugin),
|
||||||
|
|
||||||
<Project>.natures( Web && Javascript )
|
<Project>.plugins( Web && Javascript )
|
||||||
|
|
||||||
will activate `MyPlugin` defined above and have its settings automatically added. If the user instead defines
|
will activate `MyPlugin` defined above and have its settings automatically added. If the user instead defines
|
||||||
|
|
||||||
<Project>.natures( Web && Javascript && !MyPlugin)
|
<Project>.plugins( Web && Javascript && !MyPlugin)
|
||||||
|
|
||||||
then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added.
|
then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added.
|
||||||
*/
|
*/
|
||||||
abstract class AutoPlugin extends Natures.Basic
|
abstract class AutoPlugin extends Plugins.Basic
|
||||||
{
|
{
|
||||||
/** This AutoPlugin will be activated for a project when the [[Natures]] matcher returned by this method matches that project's natures
|
/** This AutoPlugin will be activated for a project when the [[Plugins]] matcher returned by this method matches that project's plugins
|
||||||
* AND the user does not explicitly exclude the Nature returned by `provides`.
|
* AND the user does not explicitly exclude the Plugin returned by `provides`.
|
||||||
*
|
*
|
||||||
* For example, if this method returns `Web && Javascript`, this plugin instance will only be added
|
* For example, if this method returns `Web && Javascript`, this plugin instance will only be added
|
||||||
* if the `Web` and `Javascript` natures are enabled. */
|
* if the `Web` and `Javascript` plugins are enabled. */
|
||||||
def select: Natures
|
def select: Plugins
|
||||||
|
|
||||||
val label: String = getClass.getName.stripSuffix("$")
|
val label: String = getClass.getName.stripSuffix("$")
|
||||||
|
|
||||||
|
|
@ -72,6 +72,24 @@ abstract class AutoPlugin extends Natures.Basic
|
||||||
// TODO?: def commands: Seq[Command]
|
// TODO?: def commands: Seq[Command]
|
||||||
|
|
||||||
def unary_! : Exclude = Exclude(this)
|
def unary_! : Exclude = Exclude(this)
|
||||||
|
|
||||||
|
|
||||||
|
/** If this plugin requries itself to be included, it means we're actually a nature,
|
||||||
|
* not a normal plugin. The user must specifically enable this plugin
|
||||||
|
* but other plugins can rely on its existence.
|
||||||
|
*/
|
||||||
|
final def isRoot: Boolean =
|
||||||
|
this match {
|
||||||
|
case _: RootAutoPlugin => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* A root AutoPlugin is a plugin which must be explicitly enabled by users in their `addPlugins` method
|
||||||
|
* on a project. However, RootAutoPlugins represent the "root" of a tree of dependent auto-plugins.
|
||||||
|
*/
|
||||||
|
abstract class RootAutoPlugin extends AutoPlugin {
|
||||||
|
final def select: Plugins = this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An error that occurs when auto-plugins aren't configured properly.
|
/** An error that occurs when auto-plugins aren't configured properly.
|
||||||
|
|
@ -84,26 +102,19 @@ final class AutoPluginException private(val message: String, val origin: Option[
|
||||||
object AutoPluginException
|
object AutoPluginException
|
||||||
{
|
{
|
||||||
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
|
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
|
||||||
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Natures.translateMessage(origin), Some(origin))
|
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An expression that matches `Nature`s. */
|
/** An expression that matches `AutoPlugin`s. */
|
||||||
sealed trait Natures {
|
sealed trait Plugins {
|
||||||
def && (o: Basic): Natures
|
def && (o: Basic): Plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents a feature or conceptual group of settings.
|
object Plugins
|
||||||
* `label` is the unique ID for this nature. */
|
|
||||||
final case class Nature(label: String) extends Basic {
|
|
||||||
/** Constructs a Natures matcher that excludes this Nature. */
|
|
||||||
override def toString = label
|
|
||||||
}
|
|
||||||
|
|
||||||
object Natures
|
|
||||||
{
|
{
|
||||||
/** Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[Nature]]s.
|
/** Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[AutoPlugin]]s.
|
||||||
* The [[AutoPlugin]]s are topologically sorted so that a selected [[AutoPlugin]] comes before its selecting [[AutoPlugin]].*/
|
* The [[AutoPlugin]]s are topologically sorted so that a selected [[AutoPlugin]] comes before its selecting [[AutoPlugin]].*/
|
||||||
def compile(defined: List[AutoPlugin]): Natures => Seq[AutoPlugin] =
|
def compile(defined: List[AutoPlugin]): Plugins => Seq[AutoPlugin] =
|
||||||
if(defined.isEmpty)
|
if(defined.isEmpty)
|
||||||
Types.const(Nil)
|
Types.const(Nil)
|
||||||
else
|
else
|
||||||
|
|
@ -111,9 +122,12 @@ object Natures
|
||||||
val byAtom = defined.map(x => (Atom(x.label), x))
|
val byAtom = defined.map(x => (Atom(x.label), x))
|
||||||
val byAtomMap = byAtom.toMap
|
val byAtomMap = byAtom.toMap
|
||||||
if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom)
|
if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom)
|
||||||
val clauses = Clauses( defined.map(d => asClause(d)) )
|
// Ignore clauses for plugins that just require themselves be specified.
|
||||||
requestedNatures =>
|
// Avoids the requirement for pure Nature strings *and* possible
|
||||||
Logic.reduce(clauses, flattenConvert(requestedNatures).toSet) match {
|
// circular dependencies in the logic.
|
||||||
|
val clauses = Clauses( defined.filterNot(_.isRoot).map(d => asClause(d)) )
|
||||||
|
requestedPlugins =>
|
||||||
|
Logic.reduce(clauses, flattenConvert(requestedPlugins).toSet) match {
|
||||||
case Left(problem) => throw AutoPluginException(problem)
|
case Left(problem) => throw AutoPluginException(problem)
|
||||||
case Right(results) =>
|
case Right(results) =>
|
||||||
// results includes the originally requested (positive) atoms,
|
// results includes the originally requested (positive) atoms,
|
||||||
|
|
@ -123,8 +137,8 @@ object Natures
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] def translateMessage(e: LogicException) = e match {
|
private[sbt] def translateMessage(e: LogicException) = e match {
|
||||||
case ic: InitialContradictions => s"Contradiction in selected natures. These natures were both included and excluded: ${literalsString(ic.literals.toSeq)}"
|
case ic: InitialContradictions => s"Contradiction in selected plugins. These plguins were both included and excluded: ${literalsString(ic.literals.toSeq)}"
|
||||||
case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required natures are satisifed. The directly selected plugins were: ${literalsString(io.literals.toSeq)}"
|
case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisifed. The directly selected plugins were: ${literalsString(io.literals.toSeq)}"
|
||||||
case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}"
|
case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}"
|
||||||
}
|
}
|
||||||
private[this] def literalsString(lits: Seq[Literal]): String =
|
private[this] def literalsString(lits: Seq[Literal]): String =
|
||||||
|
|
@ -135,34 +149,36 @@ object Natures
|
||||||
val dupStrings = for( (atom, dups) <- dupsByAtom if dups.size > 1 ) yield
|
val dupStrings = for( (atom, dups) <- dupsByAtom if dups.size > 1 ) yield
|
||||||
s"${atom.label} by ${dups.mkString(", ")}"
|
s"${atom.label} by ${dups.mkString(", ")}"
|
||||||
val (ns, nl) = if(dupStrings.size > 1) ("s", "\n\t") else ("", " ")
|
val (ns, nl) = if(dupStrings.size > 1) ("s", "\n\t") else ("", " ")
|
||||||
val message = s"Nature$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}"
|
val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}"
|
||||||
throw AutoPluginException(message)
|
throw AutoPluginException(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** [[Natures]] instance that doesn't require any [[Nature]]s. */
|
/** [[Plugins]] instance that doesn't require any [[Plugins]]s. */
|
||||||
def empty: Natures = Empty
|
def empty: Plugins = Empty
|
||||||
private[sbt] final object Empty extends Natures {
|
private[sbt] final object Empty extends Plugins {
|
||||||
def &&(o: Basic): Natures = o
|
def &&(o: Basic): Plugins = o
|
||||||
override def toString = "<none>"
|
override def toString = "<none>"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An included or excluded Nature. TODO: better name than Basic. */
|
/** An included or excluded Nature/Plugin. TODO: better name than Basic. Also, can we dump
|
||||||
sealed abstract class Basic extends Natures {
|
* this class.
|
||||||
def &&(o: Basic): Natures = And(this :: o :: Nil)
|
*/
|
||||||
|
sealed abstract class Basic extends Plugins {
|
||||||
|
def &&(o: Basic): Plugins = And(this :: o :: Nil)
|
||||||
}
|
}
|
||||||
private[sbt] final case class Exclude(n: AutoPlugin) extends Basic {
|
private[sbt] final case class Exclude(n: AutoPlugin) extends Basic {
|
||||||
override def toString = s"!$n"
|
override def toString = s"!$n"
|
||||||
}
|
}
|
||||||
private[sbt] final case class And(natures: List[Basic]) extends Natures {
|
private[sbt] final case class And(plugins: List[Basic]) extends Plugins {
|
||||||
def &&(o: Basic): Natures = And(o :: natures)
|
def &&(o: Basic): Plugins = And(o :: plugins)
|
||||||
override def toString = natures.mkString(", ")
|
override def toString = plugins.mkString(", ")
|
||||||
}
|
}
|
||||||
private[sbt] def and(a: Natures, b: Natures) = b match {
|
private[sbt] def and(a: Plugins, b: Plugins) = b match {
|
||||||
case Empty => a
|
case Empty => a
|
||||||
case And(ns) => (a /: ns)(_ && _)
|
case And(ns) => (a /: ns)(_ && _)
|
||||||
case b: Basic => a && b
|
case b: Basic => a && b
|
||||||
}
|
}
|
||||||
private[sbt] def remove(a: Natures, del: Set[Basic]): Natures = a match {
|
private[sbt] def remove(a: Plugins, del: Set[Basic]): Plugins = a match {
|
||||||
case b: Basic => if(del(b)) Empty else b
|
case b: Basic => if(del(b)) Empty else b
|
||||||
case Empty => Empty
|
case Empty => Empty
|
||||||
case And(ns) =>
|
case And(ns) =>
|
||||||
|
|
@ -170,38 +186,36 @@ object Natures
|
||||||
if(removed.isEmpty) Empty else And(removed)
|
if(removed.isEmpty) Empty else And(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Defines a clause for `ap` such that the [[Nature]] provided by `ap` is the head and the selector for `ap` is the body. */
|
/** Defines a clause for `ap` such that the [[AutPlugin]] provided by `ap` is the head and the selector for `ap` is the body. */
|
||||||
private[sbt] def asClause(ap: AutoPlugin): Clause =
|
private[sbt] def asClause(ap: AutoPlugin): Clause =
|
||||||
Clause( convert(ap.select), Set(Atom(ap.label)) )
|
Clause( convert(ap.select), Set(Atom(ap.label)) )
|
||||||
|
|
||||||
private[this] def flattenConvert(n: Natures): Seq[Literal] = n match {
|
private[this] def flattenConvert(n: Plugins): Seq[Literal] = n match {
|
||||||
case And(ns) => convertAll(ns)
|
case And(ns) => convertAll(ns)
|
||||||
case b: Basic => convertBasic(b) :: Nil
|
case b: Basic => convertBasic(b) :: Nil
|
||||||
case Empty => Nil
|
case Empty => Nil
|
||||||
}
|
}
|
||||||
private[sbt] def flatten(n: Natures): Seq[Basic] = n match {
|
private[sbt] def flatten(n: Plugins): Seq[Basic] = n match {
|
||||||
case And(ns) => ns
|
case And(ns) => ns
|
||||||
case b: Basic => b :: Nil
|
case b: Basic => b :: Nil
|
||||||
case Empty => Nil
|
case Empty => Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def convert(n: Natures): Formula = n match {
|
private[this] def convert(n: Plugins): Formula = n match {
|
||||||
case And(ns) => convertAll(ns).reduce[Formula](_ && _)
|
case And(ns) => convertAll(ns).reduce[Formula](_ && _)
|
||||||
case b: Basic => convertBasic(b)
|
case b: Basic => convertBasic(b)
|
||||||
case Empty => Formula.True
|
case Empty => Formula.True
|
||||||
}
|
}
|
||||||
private[this] def convertBasic(b: Basic): Literal = b match {
|
private[this] def convertBasic(b: Basic): Literal = b match {
|
||||||
case Exclude(n) => !convertBasic(n)
|
case Exclude(n) => !convertBasic(n)
|
||||||
case Nature(s) => Atom(s)
|
|
||||||
case a: AutoPlugin => Atom(a.label)
|
case a: AutoPlugin => Atom(a.label)
|
||||||
}
|
}
|
||||||
private[this] def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic
|
private[this] def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic
|
||||||
|
|
||||||
/** True if the select clause `n` is satisifed by `model`. */
|
/** True if the select clause `n` is satisifed by `model`. */
|
||||||
def satisfied(n: Natures, model: Set[AutoPlugin], natures: Set[Nature]): Boolean =
|
def satisfied(n: Plugins, model: Set[AutoPlugin]): Boolean =
|
||||||
flatten(n) forall {
|
flatten(n) forall {
|
||||||
case Exclude(a) => !model(a)
|
case Exclude(a) => !model(a)
|
||||||
case n: Nature => natures(n)
|
|
||||||
case ap: AutoPlugin => model(ap)
|
case ap: AutoPlugin => model(ap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import Def.Setting
|
import Def.Setting
|
||||||
import Natures._
|
import Plugins._
|
||||||
import NaturesDebug._
|
import PluginsDebug._
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
private[sbt] class NaturesDebug(val available: List[AutoPlugin], val nameToKey: Map[String, AttributeKey[_]], val provided: Relation[AutoPlugin, AttributeKey[_]])
|
private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey: Map[String, AttributeKey[_]], val provided: Relation[AutoPlugin, AttributeKey[_]])
|
||||||
{
|
{
|
||||||
/** The set of [[AutoPlugin]]s that might define a key named `keyName`.
|
/** The set of [[AutoPlugin]]s that might define a key named `keyName`.
|
||||||
* Because plugins can define keys in different scopes, this should only be used as a guideline. */
|
* Because plugins can define keys in different scopes, this should only be used as a guideline. */
|
||||||
|
|
@ -79,7 +79,7 @@ private[sbt] class NaturesDebug(val available: List[AutoPlugin], val nameToKey:
|
||||||
private[this] def multi(strs: Seq[String]): String = strs.mkString(if(strs.size > 4) "\n\t" else ", ")
|
private[this] def multi(strs: Seq[String]): String = strs.mkString(if(strs.size > 4) "\n\t" else ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
private[sbt] object NaturesDebug
|
private[sbt] object PluginsDebug
|
||||||
{
|
{
|
||||||
def helpAll(s: State): String =
|
def helpAll(s: State): String =
|
||||||
if(Project.isProjectLoaded(s))
|
if(Project.isProjectLoaded(s))
|
||||||
|
|
@ -118,8 +118,8 @@ private[sbt] object NaturesDebug
|
||||||
def projectForRef(ref: ProjectRef): ResolvedProject = get(Keys.thisProject in ref)
|
def projectForRef(ref: ProjectRef): ResolvedProject = get(Keys.thisProject in ref)
|
||||||
val perBuild: Map[URI, Set[AutoPlugin]] = structure.units.mapValues(unit => availableAutoPlugins(unit).toSet)
|
val perBuild: Map[URI, Set[AutoPlugin]] = structure.units.mapValues(unit => availableAutoPlugins(unit).toSet)
|
||||||
val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList
|
val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList
|
||||||
lazy val context = Context(currentProject.natures, currentProject.autoPlugins, Natures.compile(pluginsThisBuild), pluginsThisBuild)
|
lazy val context = Context(currentProject.plugins, currentProject.autoPlugins, Plugins.compile(pluginsThisBuild), pluginsThisBuild)
|
||||||
lazy val debug = NaturesDebug(context.available)
|
lazy val debug = PluginsDebug(context.available)
|
||||||
if(!pluginsThisBuild.contains(plugin)) {
|
if(!pluginsThisBuild.contains(plugin)) {
|
||||||
val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1)
|
val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1)
|
||||||
s"Plugin ${plugin.label} is only available in builds:\n\t${availableInBuilds.mkString("\n\t")}\nSwitch to a project in one of those builds using `project` and rerun this command for more information."
|
s"Plugin ${plugin.label} is only available in builds:\n\t${availableInBuilds.mkString("\n\t")}\nSwitch to a project in one of those builds using `project` and rerun this command for more information."
|
||||||
|
|
@ -141,20 +141,20 @@ private[sbt] object NaturesDebug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Precomputes information for debugging natures and plugins. */
|
/** Precomputes information for debugging plugins. */
|
||||||
def apply(available: List[AutoPlugin]): NaturesDebug =
|
def apply(available: List[AutoPlugin]): PluginsDebug =
|
||||||
{
|
{
|
||||||
val keyR = definedKeys(available)
|
val keyR = definedKeys(available)
|
||||||
val nameToKey: Map[String, AttributeKey[_]] = keyR._2s.toList.map(key => (key.label, key)).toMap
|
val nameToKey: Map[String, AttributeKey[_]] = keyR._2s.toList.map(key => (key.label, key)).toMap
|
||||||
new NaturesDebug(available, nameToKey, keyR)
|
new PluginsDebug(available, nameToKey, keyR)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The context for debugging a plugin (de)activation.
|
/** The context for debugging a plugin (de)activation.
|
||||||
* @param initial The initially defined [[Nature]]s.
|
* @param initial The initially defined [[AutoPlugin]]s.
|
||||||
* @param enabled The resulting model.
|
* @param enabled The resulting model.
|
||||||
* @param compile The function used to compute the model.
|
* @param compile The function used to compute the model.
|
||||||
* @param available All [[AutoPlugin]]s available for consideration. */
|
* @param available All [[AutoPlugin]]s available for consideration. */
|
||||||
final case class Context(initial: Natures, enabled: Seq[AutoPlugin], compile: Natures => Seq[AutoPlugin], available: List[AutoPlugin])
|
final case class Context(initial: Plugins, enabled: Seq[AutoPlugin], compile: Plugins => Seq[AutoPlugin], available: List[AutoPlugin])
|
||||||
|
|
||||||
/** Describes the steps to activate a plugin in some context. */
|
/** Describes the steps to activate a plugin in some context. */
|
||||||
sealed abstract class PluginEnable
|
sealed abstract class PluginEnable
|
||||||
|
|
@ -165,19 +165,19 @@ private[sbt] object NaturesDebug
|
||||||
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin]) extends EnableDeactivated
|
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin]) extends EnableDeactivated
|
||||||
|
|
||||||
/** Describes the requirements for activating [[plugin]] in [[context]].
|
/** Describes the requirements for activating [[plugin]] in [[context]].
|
||||||
* @param context The base natures, exclusions, and ultimately activated plugins
|
* @param context The base plugins, exclusions, and ultimately activated plugins
|
||||||
* @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped
|
* @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped
|
||||||
* @param enablingNatures [[Nature]]s that are not currently enabled, but need to be enabled for [[plugin]] to activate
|
* @param enablingPlugins [[AutoPlugin]]s that are not currently enabled, but need to be enabled for [[plugin]] to activate
|
||||||
* @param extraEnabledPlugins Plugins that will be enabled as a result of [[plugin]] activating, but are not required for [[plugin]] to activate
|
* @param extraEnabledPlugins Plugins that will be enabled as a result of [[plugin]] activating, but are not required for [[plugin]] to activate
|
||||||
* @param willRemove Plugins that will be deactivated as a result of [[plugin]] activating
|
* @param willRemove Plugins that will be deactivated as a result of [[plugin]] activating
|
||||||
* @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate. These require an explicit exclusion or dropping a transitive [[Nature]].*/
|
* @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate. These require an explicit exclusion or dropping a transitive [[AutoPlugin]].*/
|
||||||
final case class PluginRequirements(plugin: AutoPlugin, context: Context, blockingExcludes: Set[AutoPlugin], enablingNatures: Set[Nature], extraEnabledPlugins: Set[AutoPlugin], willRemove: Set[AutoPlugin], deactivate: List[DeactivatePlugin]) extends EnableDeactivated
|
final case class PluginRequirements(plugin: AutoPlugin, context: Context, blockingExcludes: Set[AutoPlugin], enablingPlugins: Set[AutoPlugin], extraEnabledPlugins: Set[AutoPlugin], willRemove: Set[AutoPlugin], deactivate: List[DeactivatePlugin]) extends EnableDeactivated
|
||||||
|
|
||||||
/** Describes a [[plugin]] that must be removed in order to activate another plugin in some context.
|
/** Describes a [[plugin]] that must be removed in order to activate another plugin in some context.
|
||||||
* The [[plugin]] can always be directly, explicitly excluded.
|
* The [[plugin]] can always be directly, explicitly excluded.
|
||||||
* @param removeOneOf If non-empty, removing one of these [[Nature]]s will deactivate [[plugin]] without affecting the other plugin. If empty, a direct exclusion is required.
|
* @param removeOneOf If non-empty, removing one of these [[AutoPlugin]]s will deactivate [[plugin]] without affecting the other plugin. If empty, a direct exclusion is required.
|
||||||
* @param newlySelected If false, this plugin was selected in the original context. */
|
* @param newlySelected If false, this plugin was selected in the original context. */
|
||||||
final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[Nature], newlySelected: Boolean)
|
final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[AutoPlugin], newlySelected: Boolean)
|
||||||
|
|
||||||
/** Determines how to enable [[plugin]] in [[context]]. */
|
/** Determines how to enable [[plugin]] in [[context]]. */
|
||||||
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||||
|
|
@ -191,7 +191,7 @@ private[sbt] object NaturesDebug
|
||||||
// deconstruct the context
|
// deconstruct the context
|
||||||
val initialModel = context.enabled.toSet
|
val initialModel = context.enabled.toSet
|
||||||
val initial = flatten(context.initial)
|
val initial = flatten(context.initial)
|
||||||
val initialNatures = natures(initial)
|
val initialPlugins = plugins(initial)
|
||||||
val initialExcludes = excludes(initial)
|
val initialExcludes = excludes(initial)
|
||||||
|
|
||||||
val minModel = minimalModel(plugin)
|
val minModel = minimalModel(plugin)
|
||||||
|
|
@ -212,13 +212,9 @@ private[sbt] object NaturesDebug
|
||||||
propose: B, exclude C
|
propose: B, exclude C
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// `plugin` will only be activated when all of these natures are activated
|
|
||||||
// Deactivating any one of these would deactivate `plugin`.
|
|
||||||
val minRequiredNatures = natures(minModel)
|
|
||||||
|
|
||||||
// `plugin` will only be activated when all of these plugins are activated
|
// `plugin` will only be activated when all of these plugins are activated
|
||||||
// Deactivating any one of these would deactivate `plugin`.
|
// Deactivating any one of these would deactivate `plugin`.
|
||||||
val minRequiredPlugins = minModel.collect{ case a: AutoPlugin => a }.toSet
|
val minRequiredPlugins = plugins(minModel)
|
||||||
|
|
||||||
// The presence of any one of these plugins would deactivate `plugin`
|
// The presence of any one of these plugins would deactivate `plugin`
|
||||||
val minAbsentPlugins = excludes(minModel).toSet
|
val minAbsentPlugins = excludes(minModel).toSet
|
||||||
|
|
@ -231,21 +227,21 @@ private[sbt] object NaturesDebug
|
||||||
PluginImpossible(plugin, context, contradictions)
|
PluginImpossible(plugin, context, contradictions)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Natures that the user has to add to the currently selected natures in order to enable `plugin`.
|
// Plguins that the user has to add to the currently selected plugins in order to enable `plugin`.
|
||||||
val addToExistingNatures = minRequiredNatures -- initialNatures
|
val addToExistingPlugins = minRequiredPlugins -- initialPlugins
|
||||||
|
|
||||||
// Plugins that are currently excluded that need to be allowed.
|
// Plugins that are currently excluded that need to be allowed.
|
||||||
val blockingExcludes = initialExcludes & minRequiredPlugins
|
val blockingExcludes = initialExcludes & minRequiredPlugins
|
||||||
|
|
||||||
// The model that results when the minimal natures are enabled and the minimal plugins are excluded.
|
// The model that results when the minimal plugins are enabled and the minimal plugins are excluded.
|
||||||
// This can include more plugins than just `minRequiredPlugins` because the natures required for `plugin`
|
// This can include more plugins than just `minRequiredPlugins` because the plguins required for `plugin`
|
||||||
// might activate other plugins as well.
|
// might activate other plugins as well.
|
||||||
val modelForMin = context.compile(and(includeAll(minRequiredNatures), excludeAll(minAbsentPlugins)))
|
val modelForMin = context.compile(and(includeAll(minRequiredPlugins), excludeAll(minAbsentPlugins)))
|
||||||
|
|
||||||
val incrementalInputs = and( includeAll(minRequiredNatures ++ initialNatures), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins))
|
val incrementalInputs = and( includeAll(minRequiredPlugins ++ initialPlugins), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins))
|
||||||
val incrementalModel = context.compile(incrementalInputs).toSet
|
val incrementalModel = context.compile(incrementalInputs).toSet
|
||||||
|
|
||||||
// Plugins that are newly enabled as a result of selecting the natures needed for `plugin`, but aren't strictly required for `plugin`.
|
// Plugins that are newly enabled as a result of selecting the plugins needed for `plugin`, but aren't strictly required for `plugin`.
|
||||||
// These could be excluded and `plugin` and the user's current plugins would still be activated.
|
// These could be excluded and `plugin` and the user's current plugins would still be activated.
|
||||||
val extraPlugins = incrementalModel.toSet -- minRequiredPlugins -- initialModel
|
val extraPlugins = incrementalModel.toSet -- minRequiredPlugins -- initialModel
|
||||||
|
|
||||||
|
|
@ -254,48 +250,48 @@ private[sbt] object NaturesDebug
|
||||||
|
|
||||||
// Determine the plugins that must be independently deactivated.
|
// Determine the plugins that must be independently deactivated.
|
||||||
// If both A and B must be deactivated, but A transitively depends on B, deactivating B will deactivate A.
|
// If both A and B must be deactivated, but A transitively depends on B, deactivating B will deactivate A.
|
||||||
// If A must be deactivated, but one if its (transitively) required natures isn't present, it won't be activated.
|
// If A must be deactivated, but one if its (transitively) required plugins isn't present, it won't be activated.
|
||||||
// So, in either of these cases, A doesn't need to be considered further and won't be included in this set.
|
// So, in either of these cases, A doesn't need to be considered further and won't be included in this set.
|
||||||
val minDeactivate = minAbsentPlugins.filter(p => Natures.satisfied(p.select, incrementalModel, natures(flatten(incrementalInputs))))
|
val minDeactivate = minAbsentPlugins.filter(p => Plugins.satisfied(p.select, incrementalModel))
|
||||||
|
|
||||||
val deactivate = for(d <- minDeactivate.toList) yield {
|
val deactivate = for(d <- minDeactivate.toList) yield {
|
||||||
// removing any one of these natures will deactivate `d`. TODO: This is not an especially efficient implementation.
|
// removing any one of these plugins will deactivate `d`. TODO: This is not an especially efficient implementation.
|
||||||
val removeToDeactivate = natures(minimalModel(d)) -- minRequiredNatures
|
val removeToDeactivate = plugins(minimalModel(d)) -- minRequiredPlugins
|
||||||
val newlySelected = !initialModel(d)
|
val newlySelected = !initialModel(d)
|
||||||
// a. suggest removing a nature in removeOneToDeactivate to deactivate d
|
// a. suggest removing a plugin in removeOneToDeactivate to deactivate d
|
||||||
// b. suggest excluding `d` to directly deactivate it in any case
|
// b. suggest excluding `d` to directly deactivate it in any case
|
||||||
// c. note whether d was already activated (in context.enabled) or is newly selected
|
// c. note whether d was already activated (in context.enabled) or is newly selected
|
||||||
DeactivatePlugin(d, removeToDeactivate, newlySelected)
|
DeactivatePlugin(d, removeToDeactivate, newlySelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginRequirements(plugin, context, blockingExcludes, addToExistingNatures, extraPlugins, willRemove, deactivate)
|
PluginRequirements(plugin, context, blockingExcludes, addToExistingPlugins, extraPlugins, willRemove, deactivate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def includeAll[T <: Basic](basic: Set[T]): Natures = And(basic.toList)
|
private[this] def includeAll[T <: Basic](basic: Set[T]): Plugins = And(basic.toList)
|
||||||
private[this] def excludeAll(plugins: Set[AutoPlugin]): Natures = And(plugins map (p => Exclude(p)) toList)
|
private[this] def excludeAll(plugins: Set[AutoPlugin]): Plugins = And(plugins map (p => Exclude(p)) toList)
|
||||||
|
|
||||||
private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case Exclude(b) => b }.toSet
|
private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case Exclude(b) => b }.toSet
|
||||||
private[this] def natures(bs: Seq[Basic]): Set[Nature] = bs.collect { case n: Nature => n }.toSet
|
private[this] def plugins(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case n: AutoPlugin => n }.toSet
|
||||||
|
|
||||||
// If there is a model that includes `plugin`, it includes at least what is returned by this method.
|
// If there is a model that includes `plugin`, it includes at least what is returned by this method.
|
||||||
// This is the list of natures and plugins that must be included as well as list of plugins that must not be present.
|
// This is the list of plugins that must be included as well as list of plugins that must not be present.
|
||||||
// It might not be valid, such as if there are contradictions or if there are cycles that are unsatisfiable.
|
// It might not be valid, such as if there are contradictions or if there are cycles that are unsatisfiable.
|
||||||
// The actual model might be larger, since other plugins might be enabled by the selected natures.
|
// The actual model might be larger, since other plugins might be enabled by the selected plugins.
|
||||||
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) {
|
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) {
|
||||||
case _: Exclude | _: Nature => Nil
|
case _: Exclude => Nil
|
||||||
case ap: AutoPlugin => Natures.flatten(ap.select)
|
case ap: AutoPlugin => Plugins.flatten(ap.select)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** String representation of [[PluginEnable]], intended for end users. */
|
/** String representation of [[PluginEnable]], intended for end users. */
|
||||||
def explainPluginEnable(ps: PluginEnable): String =
|
def explainPluginEnable(ps: PluginEnable): String =
|
||||||
ps match {
|
ps match {
|
||||||
case PluginRequirements(plugin, context, blockingExcludes, enablingNatures, extraEnabledPlugins, toBeRemoved, deactivate) =>
|
case PluginRequirements(plugin, context, blockingExcludes, enablingPlugins, extraEnabledPlugins, toBeRemoved, deactivate) =>
|
||||||
def indent(str: String) = if(str.isEmpty) "" else s"\t$str"
|
def indent(str: String) = if(str.isEmpty) "" else s"\t$str"
|
||||||
def note(str: String) = if(str.isEmpty) "" else s"Note: $str"
|
def note(str: String) = if(str.isEmpty) "" else s"Note: $str"
|
||||||
val parts =
|
val parts =
|
||||||
indent(excludedError(false /* TODO */, blockingExcludes.toList)) ::
|
indent(excludedError(false /* TODO */, blockingExcludes.toList)) ::
|
||||||
indent(required(enablingNatures.toList)) ::
|
indent(required(enablingPlugins.toList)) ::
|
||||||
indent(needToDeactivate(deactivate)) ::
|
indent(needToDeactivate(deactivate)) ::
|
||||||
note(willAdd(plugin, extraEnabledPlugins.toList)) ::
|
note(willAdd(plugin, extraEnabledPlugins.toList)) ::
|
||||||
note(willRemove(plugin, toBeRemoved.toList)) ::
|
note(willRemove(plugin, toBeRemoved.toList)) ::
|
||||||
|
|
@ -326,13 +322,13 @@ private[sbt] object NaturesDebug
|
||||||
private[this] def transitiveString(transitive: Boolean) =
|
private[this] def transitiveString(transitive: Boolean) =
|
||||||
if(transitive) "(transitive) " else ""
|
if(transitive) "(transitive) " else ""
|
||||||
|
|
||||||
private[this] def required(natures: List[Nature]): String =
|
private[this] def required(plugins: List[AutoPlugin]): String =
|
||||||
str(natures)(requiredNature, requiredNatures)
|
str(plugins)(requiredPlugin, requiredPlugins)
|
||||||
|
|
||||||
private[this] def requiredNature(nature: Nature) =
|
private[this] def requiredPlugin(plugin: AutoPlugin) =
|
||||||
s"Required nature ${nature.label} not present."
|
s"Required plugin ${plugin.label} not present."
|
||||||
private[this] def requiredNatures(natures: List[Nature]) =
|
private[this] def requiredPlugins(plugins: List[AutoPlugin]) =
|
||||||
s"Required natures not present:\n\t${natures.map(_.label).mkString("\n\t")}"
|
s"Required plugins not present:\n\t${plugins.map(_.label).mkString("\n\t")}"
|
||||||
|
|
||||||
private[this] def str[A](list: List[A])(f: A => String, fs: List[A] => String): String = list match {
|
private[this] def str[A](list: List[A])(f: A => String, fs: List[A] => String): String = list match {
|
||||||
case Nil => ""
|
case Nil => ""
|
||||||
|
|
@ -367,13 +363,13 @@ private[sbt] object NaturesDebug
|
||||||
s"Need to deactivate ${deactivateString(deactivate)}"
|
s"Need to deactivate ${deactivateString(deactivate)}"
|
||||||
private[this] def deactivateString(d: DeactivatePlugin): String =
|
private[this] def deactivateString(d: DeactivatePlugin): String =
|
||||||
{
|
{
|
||||||
val removeNaturesString: String =
|
val removePluginsString: String =
|
||||||
d.removeOneOf.toList match {
|
d.removeOneOf.toList match {
|
||||||
case Nil => ""
|
case Nil => ""
|
||||||
case x :: Nil => s" or no longer include $x"
|
case x :: Nil => s" or no longer include $x"
|
||||||
case xs => s" or remove one of ${xs.mkString(", ")}"
|
case xs => s" or remove one of ${xs.mkString(", ")}"
|
||||||
}
|
}
|
||||||
s"${d.plugin.label}: directly exclude it${removeNaturesString}"
|
s"${d.plugin.label}: directly exclude it${removePluginsString}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
||||||
|
|
@ -50,9 +50,9 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
|
||||||
/** Configures the sources of automatically appended settings.*/
|
/** Configures the sources of automatically appended settings.*/
|
||||||
def auto: AddSettings
|
def auto: AddSettings
|
||||||
|
|
||||||
/** The [[Natures]] associated with this project.
|
/** The defined [[Plugins]] associated with this project.
|
||||||
A [[Nature]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */
|
A [[AutoPlugin]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */
|
||||||
def natures: Natures
|
def plugins: Plugins
|
||||||
|
|
||||||
/** The [[AutoPlugin]]s enabled for this project. This value is only available on a loaded Project. */
|
/** The [[AutoPlugin]]s enabled for this project. This value is only available on a loaded Project. */
|
||||||
private[sbt] def autoPlugins: Seq[AutoPlugin]
|
private[sbt] def autoPlugins: Seq[AutoPlugin]
|
||||||
|
|
@ -68,18 +68,18 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
|
||||||
val dep = ifNonEmpty("dependencies", dependencies)
|
val dep = ifNonEmpty("dependencies", dependencies)
|
||||||
val conf = ifNonEmpty("configurations", configurations)
|
val conf = ifNonEmpty("configurations", configurations)
|
||||||
val autos = ifNonEmpty("autoPlugins", autoPlugins.map(_.label))
|
val autos = ifNonEmpty("autoPlugins", autoPlugins.map(_.label))
|
||||||
val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"natures: List($natures)" :: autos)
|
val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"plugins: List($plugins)" :: autos)
|
||||||
s"Project(${fields.mkString(", ")})"
|
s"Project(${fields.mkString(", ")})"
|
||||||
}
|
}
|
||||||
private[this] def ifNonEmpty[T](label: String, ts: Iterable[T]): List[String] = if(ts.isEmpty) Nil else s"$label: $ts" :: Nil
|
private[this] def ifNonEmpty[T](label: String, ts: Iterable[T]): List[String] = if(ts.isEmpty) Nil else s"$label: $ts" :: Nil
|
||||||
}
|
}
|
||||||
sealed trait Project extends ProjectDefinition[ProjectReference]
|
sealed trait Project extends ProjectDefinition[ProjectReference]
|
||||||
{
|
{
|
||||||
// TODO: add parameters for natures and autoPlugins in 0.14.0 (not reasonable to do in a binary compatible way in 0.13)
|
// TODO: add parameters for plugins in 0.14.0 (not reasonable to do in a binary compatible way in 0.13)
|
||||||
def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies,
|
def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies,
|
||||||
delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
|
delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
|
||||||
auto: AddSettings = auto): Project =
|
auto: AddSettings = auto): Project =
|
||||||
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, natures, autoPlugins)
|
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, plugins, autoPlugins)
|
||||||
|
|
||||||
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
|
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
|
||||||
{
|
{
|
||||||
|
|
@ -87,7 +87,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
|
||||||
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),
|
resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
|
||||||
settings, configurations, auto, natures, autoPlugins)
|
settings, configurations, auto, plugins, autoPlugins)
|
||||||
}
|
}
|
||||||
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
|
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +95,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
|
||||||
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)
|
||||||
unresolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
|
unresolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
|
||||||
settings, configurations, auto, natures, autoPlugins)
|
settings, configurations, auto, plugins, autoPlugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Applies the given functions to this Project.
|
/** Applies the given functions to this Project.
|
||||||
|
|
@ -136,27 +136,27 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
|
||||||
* Any configured .sbt files are removed from this project's list.*/
|
* Any configured .sbt files are removed from this project's list.*/
|
||||||
def setSbtFiles(files: File*): Project = copy(auto = AddSettings.append( AddSettings.clearSbtFiles(auto), AddSettings.sbtFiles(files: _*)) )
|
def setSbtFiles(files: File*): Project = copy(auto = AddSettings.append( AddSettings.clearSbtFiles(auto), AddSettings.sbtFiles(files: _*)) )
|
||||||
|
|
||||||
/** Sets the [[Nature]]s of this project.
|
/** Sets the [[AutoPlugin]]s of this project.
|
||||||
A [[Nature]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */
|
A [[AutoPlugin]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */
|
||||||
def addNatures(ns: Nature*): Project = setNatures(Natures.and(natures, Natures.And(ns.toList)))
|
def addPlugins(ns: AutoPlugin*): Project = setPlugins(Plugins.and(plugins, Plugins.And(ns.toList)))
|
||||||
|
|
||||||
/** Disable the given plugins on this project. */
|
/** Disable the given plugins on this project. */
|
||||||
def disablePlugins(plugins: AutoPlugin*): Project =
|
def disablePlugins(ps: AutoPlugin*): Project =
|
||||||
setNatures(Natures.and(natures, Natures.And(plugins.map(p => Natures.Exclude(p)).toList)))
|
setPlugins(Plugins.and(plugins, Plugins.And(ps.map(p => Plugins.Exclude(p)).toList)))
|
||||||
|
|
||||||
private[this] def setNatures(ns: Natures): Project = {
|
private[this] def setPlugins(ns: Plugins): Project = {
|
||||||
// TODO: for 0.14.0, use copy when it has the additional `natures` parameter
|
// TODO: for 0.14.0, use copy when it has the additional `plugins` parameter
|
||||||
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, ns, autoPlugins)
|
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, ns, autoPlugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Definitively set the [[AutoPlugin]]s for this project. */
|
/** Definitively set the [[AutoPlugin]]s for this project. */
|
||||||
private[sbt] def setAutoPlugins(autos: Seq[AutoPlugin]): Project = {
|
private[sbt] def setAutoPlugins(autos: Seq[AutoPlugin]): Project = {
|
||||||
// TODO: for 0.14.0, use copy when it has the additional `autoPlugins` parameter
|
// TODO: for 0.14.0, use copy when it has the additional `autoPlugins` parameter
|
||||||
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, natures, autos)
|
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, plugins, autos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] {
|
sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] {
|
||||||
/** The [[AutoPlugin]]s enabled for this project as computed from [[natures]].*/
|
/** The [[AutoPlugin]]s enabled for this project as computed from [[plugins]].*/
|
||||||
def autoPlugins: Seq[AutoPlugin]
|
def autoPlugins: Seq[AutoPlugin]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ object Project extends ProjectExtra
|
||||||
|
|
||||||
private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[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[Def.Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings,
|
delegates0: => Seq[PR], settings0: => Seq[Def.Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings,
|
||||||
val natures: Natures, val autoPlugins: Seq[AutoPlugin]) extends ProjectDefinition[PR]
|
val plugins: Plugins, val autoPlugins: Seq[AutoPlugin]) extends ProjectDefinition[PR]
|
||||||
{
|
{
|
||||||
lazy val aggregate = aggregate0
|
lazy val aggregate = aggregate0
|
||||||
lazy val dependencies = dependencies0
|
lazy val dependencies = dependencies0
|
||||||
|
|
@ -202,11 +202,12 @@ object Project extends 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add parameter for natures in 0.14.0
|
// TODO: add parameter for plugins in 0.14.0
|
||||||
|
// TODO: Modify default settings to be the core settings, and automatically add the IvyModule + JvmPlugins.
|
||||||
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil,
|
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil,
|
||||||
delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
|
delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = Nil, configurations: Seq[Configuration] = Nil,
|
||||||
auto: AddSettings = AddSettings.allDefaults): Project =
|
auto: AddSettings = AddSettings.allDefaults): Project =
|
||||||
unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil)
|
unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Plugins.empty, Nil) // Note: JvmModule/IvyModule auto included...
|
||||||
|
|
||||||
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/
|
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/
|
||||||
def validProjectID(id: String): Option[String] = DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
|
def validProjectID(id: String): Option[String] = DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
|
||||||
|
|
@ -228,21 +229,22 @@ object Project extends ProjectExtra
|
||||||
@deprecated("Will be removed.", "0.13.2")
|
@deprecated("Will be removed.", "0.13.2")
|
||||||
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[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
|
settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
|
||||||
resolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil)
|
resolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Plugins.empty, Nil)
|
||||||
|
|
||||||
private def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ClasspathDep[ProjectRef]],
|
private def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ClasspathDep[ProjectRef]],
|
||||||
delegates: => Seq[ProjectRef], settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings,
|
delegates: => Seq[ProjectRef], settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings,
|
||||||
natures: Natures, autoPlugins: Seq[AutoPlugin]): ResolvedProject =
|
plugins: Plugins, autoPlugins: Seq[AutoPlugin]): ResolvedProject =
|
||||||
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto, natures, autoPlugins) with ResolvedProject
|
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto, plugins, autoPlugins) with ResolvedProject
|
||||||
|
|
||||||
private def unresolved(id: String, base: File, aggregate: => Seq[ProjectReference], dependencies: => Seq[ClasspathDep[ProjectReference]],
|
private def unresolved(id: String, base: File, aggregate: => Seq[ProjectReference], dependencies: => Seq[ClasspathDep[ProjectReference]],
|
||||||
delegates: => Seq[ProjectReference], settings: => Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings,
|
delegates: => Seq[ProjectReference], settings: => Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings,
|
||||||
natures: Natures, autoPlugins: Seq[AutoPlugin]): Project =
|
plugins: Plugins, autoPlugins: Seq[AutoPlugin]): Project =
|
||||||
{
|
{
|
||||||
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
|
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
|
||||||
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto, natures, autoPlugins) with Project
|
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto, plugins, autoPlugins) with Project
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@deprecated("0.13.2", "Use Defaults.coreDefaultSettings instead, combined with AutoPlugins.")
|
||||||
def defaultSettings: Seq[Def.Setting[_]] = Defaults.defaultSettings
|
def defaultSettings: Seq[Def.Setting[_]] = Defaults.defaultSettings
|
||||||
|
|
||||||
final class Constructor(p: ProjectReference) {
|
final class Constructor(p: ProjectReference) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package sbt
|
||||||
|
package plugins
|
||||||
|
|
||||||
|
import Def.Setting
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin for core sbt-isms.
|
||||||
|
*
|
||||||
|
* Can control task-level paralleism, logging, etc.
|
||||||
|
*/
|
||||||
|
object GlobalModule extends AutoPlugin {
|
||||||
|
// We must be explicitly enabled
|
||||||
|
def select = Plugins.empty
|
||||||
|
|
||||||
|
override lazy val projectSettings: Seq[Setting[_]] =
|
||||||
|
Defaults.coreDefaultSettings
|
||||||
|
override lazy val globalSettings: Seq[Setting[_]] =
|
||||||
|
Defaults.globalSbtCore
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package sbt
|
||||||
|
package plugins
|
||||||
|
|
||||||
|
import Def.Setting
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin that enables resolving artifacts via ivy.
|
||||||
|
*
|
||||||
|
* Core Tasks
|
||||||
|
* - `update`
|
||||||
|
* - `makePom`
|
||||||
|
* - `publish`
|
||||||
|
* - `artifacts`
|
||||||
|
* - `publishedArtifacts`
|
||||||
|
*/
|
||||||
|
object IvyModule extends AutoPlugin {
|
||||||
|
// We are automatically included on everything that has the global module,
|
||||||
|
// which is automatically included on everything.
|
||||||
|
def select = GlobalModule
|
||||||
|
|
||||||
|
override lazy val projectSettings: Seq[Setting[_]] =
|
||||||
|
Classpaths.ivyPublishSettings ++ Classpaths.ivyBaseSettings
|
||||||
|
override lazy val globalSettings: Seq[Setting[_]] =
|
||||||
|
Defaults.globalIvyCore
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package sbt
|
||||||
|
package plugins
|
||||||
|
|
||||||
|
import Def.Setting
|
||||||
|
|
||||||
|
/** A plugin representing the ability to build a JVM project.
|
||||||
|
*
|
||||||
|
* Core tasks/keys:
|
||||||
|
* - `run`
|
||||||
|
* - `test`
|
||||||
|
* - `compile`
|
||||||
|
* - `fullClasspath`
|
||||||
|
* Core configurations
|
||||||
|
* - `Test`
|
||||||
|
* - `Compile`
|
||||||
|
*/
|
||||||
|
object JvmModule extends AutoPlugin {
|
||||||
|
// We are automatically enabled for any IvyModule project. We also require its settings
|
||||||
|
// for ours to work.
|
||||||
|
def select = IvyModule
|
||||||
|
|
||||||
|
override lazy val projectSettings: Seq[Setting[_]] =
|
||||||
|
Defaults.runnerSettings ++
|
||||||
|
Defaults.paths ++
|
||||||
|
Classpaths.jvmPublishSettings ++
|
||||||
|
Classpaths.jvmBaseSettings ++
|
||||||
|
Defaults.projectTasks ++
|
||||||
|
Defaults.packageBase ++
|
||||||
|
Defaults.compileBase ++
|
||||||
|
Defaults.defaultConfigs
|
||||||
|
override lazy val globalSettings: Seq[Setting[_]] =
|
||||||
|
Defaults.globalJvmCore
|
||||||
|
|
||||||
|
override def projectConfigurations: Seq[Configuration] =
|
||||||
|
Configurations.default
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// excludePlugins(C) will prevent C, and thus D, from being auto-added
|
// excludePlugins(C) will prevent C, and thus D, from being auto-added
|
||||||
lazy val a = project.addNatures(A, B).disablePlugins(Q)
|
lazy val a = project.addPlugins(A, B).disablePlugins(Q)
|
||||||
|
|
||||||
// without B, C is not added
|
// without B, C is not added
|
||||||
lazy val b = project.addNatures(A)
|
lazy val b = project.addPlugins(A)
|
||||||
|
|
||||||
// with both A and B, C is selected, which in turn selects D
|
// with both A and B, C is selected, which in turn selects D
|
||||||
lazy val c = project.addNatures(A, B)
|
lazy val c = project.addPlugins(A, B)
|
||||||
|
|
||||||
// with no natures defined, nothing is auto-added
|
// with no natures defined, nothing is auto-added
|
||||||
lazy val d = project
|
lazy val d = project
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
object AI extends AutoImport
|
object AI extends AutoImport
|
||||||
{
|
{
|
||||||
lazy val A = Nature("A")
|
trait EmptyAutoPlugin extends AutoPlugin {
|
||||||
lazy val B = Nature("B")
|
def select = Plugins.empty
|
||||||
lazy val E = Nature("E")
|
}
|
||||||
|
object A extends EmptyAutoPlugin
|
||||||
|
object B extends EmptyAutoPlugin
|
||||||
|
object E extends EmptyAutoPlugin
|
||||||
|
|
||||||
lazy val q = config("q")
|
lazy val q = config("q")
|
||||||
lazy val p = config("p").extend(q)
|
lazy val p = config("p").extend(q)
|
||||||
|
|
@ -20,12 +23,12 @@ object AI extends AutoImport
|
||||||
import AI._
|
import AI._
|
||||||
|
|
||||||
object D extends AutoPlugin {
|
object D extends AutoPlugin {
|
||||||
def select: Natures = E
|
def select: Plugins = E
|
||||||
}
|
}
|
||||||
|
|
||||||
object Q extends AutoPlugin
|
object Q extends AutoPlugin
|
||||||
{
|
{
|
||||||
def select: Natures = A && B
|
def select: Plugins = A && B
|
||||||
|
|
||||||
override def projectConfigurations: Seq[Configuration] =
|
override def projectConfigurations: Seq[Configuration] =
|
||||||
p ::
|
p ::
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import Keys._
|
||||||
|
|
||||||
|
|
||||||
object C extends AutoImport {
|
object C extends AutoImport {
|
||||||
lazy val bN = Nature("B")
|
object bN extends AutoPlugin {
|
||||||
|
def select = Plugins.empty
|
||||||
|
}
|
||||||
lazy val check = taskKey[Unit]("Checks that the AutoPlugin and Build are automatically added.")
|
lazy val check = taskKey[Unit]("Checks that the AutoPlugin and Build are automatically added.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,5 +19,5 @@ object A extends AutoPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
object B extends Build {
|
object B extends Build {
|
||||||
lazy val extra = project.addNatures(bN)
|
lazy val extra = project.addPlugins(bN)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
=================
|
||||||
|
Command Engine
|
||||||
|
=================
|
||||||
|
|
||||||
|
Placeholder for command engine details.
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
=================
|
||||||
|
Core Principles
|
||||||
|
=================
|
||||||
|
|
||||||
|
This document details the core principles overarching sbt's design and code style. Sbt's core principles can
|
||||||
|
be stated quite simply:
|
||||||
|
|
||||||
|
1. Everything should have a ``Type``, enforced as much as is practical.
|
||||||
|
2. Dependencies should be **explicit**.
|
||||||
|
3. Once learned, a concept should hold throughout **all** parts of sbt.
|
||||||
|
4. Parallel is the default.
|
||||||
|
|
||||||
|
With these principles in mind, let's walk through the core design of sbt.
|
||||||
|
|
||||||
|
|
||||||
|
Introduction to build state
|
||||||
|
===========================
|
||||||
|
This is the first piece you hit when starting sbt. Sbt's command engine is the means by which
|
||||||
|
it processes user requests using the build state. The command engine is essentially a means of applying
|
||||||
|
**state transformations** on the build state, to execute user requests.
|
||||||
|
|
||||||
|
In sbt, commands are functions that take the current build state (``sbt.State``) and produce the next state. In
|
||||||
|
other words, they are essentially functions of ``sbt.State => sbt.State``. However, in reality, Commands are
|
||||||
|
actually string processors which take some string input and act on it, returning the next build state.
|
||||||
|
|
||||||
|
The details of the command engine are covered in :doc:`the command engine section <Command-Engine>`.
|
||||||
|
|
||||||
|
So, the entirety of sbt is driven off the ``sbt.State`` class. Since this class needs to be resilient in the
|
||||||
|
face of custom code and plugins, it needs a mechanism to store the state from any potential client. In
|
||||||
|
dynamic languages, this can be done directly on objects.
|
||||||
|
|
||||||
|
A naive approach in Scala is to use a ``Map<String,Any>``. However, this vioaltes tennant #1: Everythign should have a `Type`.
|
||||||
|
So, sbt defines a new type of map called an ``AttributeMap``. An ``AttributeMap`` is a key-value storage mechanism where
|
||||||
|
keys are both strings *and* expected `Type`s for their value.
|
||||||
|
|
||||||
|
Here is what the typesafe ``AttributeKey`` key looks like ::
|
||||||
|
|
||||||
|
sealed trait AttributeKey[T] {
|
||||||
|
/** The label is the identifier for the key and is camelCase by convention. */
|
||||||
|
def label: String
|
||||||
|
/** The runtime evidence for `T` */
|
||||||
|
def manifest: Manifest[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
These keys store both a `label` (``string``) and some runtime type information (``manifest``). To put or get something on
|
||||||
|
the AttributeMap, we first need to construct one of these keys. Let's look at the basic definition of the ``AttributeMap`` ::
|
||||||
|
|
||||||
|
trait AttributeMap {
|
||||||
|
/** Gets the value of type `T` associated with the key `k` or `None` if no value is associated.
|
||||||
|
* If a key with the same label but a different type is defined, this method will return `None`. */
|
||||||
|
def get[T](k: AttributeKey[T]): Option[T]
|
||||||
|
|
||||||
|
|
||||||
|
/** Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`.
|
||||||
|
* Any mappings for keys with the same label but different types are unaffected. */
|
||||||
|
def put[T](k: AttributeKey[T], value: T): AttributeMap
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Now that there's a definition of what build state is, there needs to be a way to dynamically construct it. In sbt, this is
|
||||||
|
done through the ``Setting[_]`` sequence.
|
||||||
|
|
||||||
|
Introduction to Settings
|
||||||
|
========================
|
||||||
|
|
||||||
|
TODO - Discuss ``Setting[_]``
|
||||||
|
|
||||||
|
TODO - Transition into ``Task[_]``
|
||||||
|
|
||||||
|
TODO - Transition into ``InputTask[_]``
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
======================
|
||||||
|
Setting Initialization
|
||||||
|
======================
|
||||||
|
|
||||||
|
This page outlines the mechanisms by which sbt loads settings for a particular build, including the hooks where
|
||||||
|
users can control the ordering of everything.
|
||||||
|
|
||||||
|
As stated elsewhere, sbt constructs its initialization graph and task graph via ``Setting[_]`` objects. A setting
|
||||||
|
is something which can take the values stored at other Keys in the build state, and generates a new value for
|
||||||
|
a particular build key. Sbt converts all registered ``Setting[_]`` objects into a giant linear sequence and
|
||||||
|
*compiles* them into the a task graph. This task graph is then used to execute your build.
|
||||||
|
|
||||||
|
All of sbt's loading semantics are contained within the `Load.scala <../../sxr/sbt/Load.scala.html>` file. It is approximately the following:
|
||||||
|
|
||||||
|
.. Note: This image comes from a google drawing: https://docs.google.com/a/typesafe.com/drawings/d/1Aj_IkOaJpRXJNhrVtVJaS8m-YRcKsympVOj3M2sUz7E/edit
|
||||||
|
.. Feel free to request access to modify as appropriate.
|
||||||
|
|
||||||
|
.. image:: settings-initialization-load-ordering.png
|
||||||
|
|
||||||
|
The blue circles represent actions happening when sbt loads a project. We can see that sbt performs the following actions in load:
|
||||||
|
|
||||||
|
1. Compile the user-level project (``~/.sbt/<version>/``)
|
||||||
|
a. Load any plugins defined by this project (``~/.sbt/<version>/plugins/*.sbt`` and ``~/.sbt/<version>/plugins/project/*.scala``)
|
||||||
|
b. Load all settings defined (``~/.sbt/<version>/*.sbt`` and ``~/.sbt/<version>/plugins/*.scala``)
|
||||||
|
2. Compile the current project (``<working-directory/project``)
|
||||||
|
a. Load all defined plugins (``project/plugins.sbt`` and ``project/project/*.scala``)
|
||||||
|
b. Load/Compile the project (``project/*.scala``)
|
||||||
|
3. Load project *.sbt files (``build.sbt`` and friends).
|
||||||
|
|
||||||
|
Each of these loads defines several sequences of settings. The diagram shows the two most important:
|
||||||
|
|
||||||
|
* ``buildSettings`` - These are settings defined to be ``in ThisBuild`` or directly against the ``Build`` object. They are initialized *once* for the build.
|
||||||
|
You can add these, e.g. in ``project/build.scala`` ::
|
||||||
|
|
||||||
|
object MyBuild extends Build {
|
||||||
|
override val settings = Seq(foo := "hi")
|
||||||
|
}
|
||||||
|
|
||||||
|
or in a ``build.sbt`` file ::
|
||||||
|
|
||||||
|
foo in ThisBuild := "hi"
|
||||||
|
|
||||||
|
|
||||||
|
* ``projectSettings`` - These are settings specific to a project. They are specific to a *particular sub project* in the build. A
|
||||||
|
plugin may be contributing its settings to more than on project, in which case the values are duplicated for each project.
|
||||||
|
You add project specific settings, eg. in ``project/build.scala`` ::
|
||||||
|
|
||||||
|
object MyBuild extends Build {
|
||||||
|
val test = project.in(file(".")).settings(...)
|
||||||
|
}
|
||||||
|
|
||||||
|
After loading/compiling all the build definitions, sbt has a series of Seq[Setting[_]] that it must order. As shown in the diagram,
|
||||||
|
the default inclusion order for sbt is:
|
||||||
|
|
||||||
|
1. All AutoPlugin settings
|
||||||
|
2. All settings defined in ``project/Build.scala``
|
||||||
|
3. All settings defined in the user directory (``~/.sbt/<verison>/*.sbt``)
|
||||||
|
4. All local configurations (``build.sbt``)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Controlling Initialization
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The order which sbt uses to load settings is configurable at a *project* level. This means that we can't control
|
||||||
|
the order of settings added to Build/Global namespace, but we can control how each project loads, e.g. plugins and ``.sbt`` files.
|
||||||
|
To do so, use the ``AddSettings`` class ::
|
||||||
|
|
||||||
|
|
||||||
|
import sbt._
|
||||||
|
import Keys._
|
||||||
|
|
||||||
|
import AddSettings._
|
||||||
|
|
||||||
|
object MyOwnOrder extends Build {
|
||||||
|
// here we load config from a txt file.
|
||||||
|
lazy val root = project.in(file(".")).autoSettings( autoPlugins, projectSettings, sbtFiles(file("silly.txt")) )
|
||||||
|
}
|
||||||
|
|
||||||
|
In the above project, we've modified the order of settings to be:
|
||||||
|
|
||||||
|
1. All AutoPlugin settings.
|
||||||
|
2. All settings defined in the ``project/Build.scala`` file (shown above).
|
||||||
|
3. All settings found in the ``silly.txt`` file.
|
||||||
|
|
||||||
|
What we've excluded:
|
||||||
|
|
||||||
|
* All settings from the user directory (``~/.sbt/<verison>``)
|
||||||
|
* All ``*.sbt`` settings.
|
||||||
|
|
||||||
|
The AddSettings object provides the following "groups" of settings you can use for ordering:
|
||||||
|
|
||||||
|
``autoPlugins``
|
||||||
|
All the ordered settings of plugins after they've gone through dependency resolution
|
||||||
|
``projectSettings``
|
||||||
|
The full sequence of settings defined directly in ``project/*.scala`` builds.
|
||||||
|
``sbtFiles(*)``
|
||||||
|
Specifies the exact setting DSL files to include (files must use the ``.sbt`` file format)
|
||||||
|
``userSettings``
|
||||||
|
All the settings defined in the user directory ``~/.sbt/<version>/``.
|
||||||
|
``defaultSbtFiles``
|
||||||
|
Include all local ``*.sbt`` file settings.
|
||||||
|
|
||||||
|
|
||||||
|
*Note: Be very careful when reordering settings. It's easy to accidentally remove core functionality.*
|
||||||
|
|
||||||
|
For example, let's see what happens if we move the ``build.sbt`` files *before* the ``projectSettings``.
|
||||||
|
|
||||||
|
Let's create an example project the following defintiion:
|
||||||
|
|
||||||
|
`project/build.scala` ::
|
||||||
|
|
||||||
|
object MyTestBuild extends Build {
|
||||||
|
|
||||||
|
val testProject = project.in(file(".")).autoSettings(autoPlugins, defaultSbtFiles, projectSettings).settings(
|
||||||
|
version := scalaBinaryVersion.value match {
|
||||||
|
case "2.10" => "1.0-SNAPSHOT"
|
||||||
|
case v => "1.0-for-${v}-SNAPSHOT"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
This build defines a version string which appends the scala version if the current scala version is not the in the ``2.10.x`` series.
|
||||||
|
Now, when issuing a release we want to lock down the version. Most tools assume this can happen by writing a ``version.sbt`` file:
|
||||||
|
|
||||||
|
`version.sbt` ::
|
||||||
|
|
||||||
|
version := "1.0.0"
|
||||||
|
|
||||||
|
However, when we load this new build, we find that the ``version`` in ``version.sbt`` has been **overriden** by the one defined
|
||||||
|
in ``project/Build.scala`` because of the order we defined for settings, so the new ``version.sbt`` file has no effect.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
=================
|
||||||
|
Task Engine
|
||||||
|
=================
|
||||||
|
|
||||||
|
Placeholder for task engine design details.
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
==============
|
||||||
|
Architecture
|
||||||
|
==============
|
||||||
|
|
||||||
|
This is the set of documentation about the Architecture of sbt. This covers all the core components of
|
||||||
|
sbt as well as the general notion of how they all work together. This documentation is suitable for those who wish to
|
||||||
|
have a deeper understanding of sbt's core, but already understand the fundamentals of ``Setting[_]``, ``Task[_]`` and
|
||||||
|
constructing builds.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
Core-Principles
|
||||||
|
Setting-Initialization
|
||||||
|
Task-Engine
|
||||||
|
Command-Engine
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
|
|
@ -19,4 +19,5 @@ Other resources include the :doc:`Examples </Examples/index>` and
|
||||||
Tasks-and-Commands
|
Tasks-and-Commands
|
||||||
Plugins-and-Best-Practices
|
Plugins-and-Best-Practices
|
||||||
Advanced-Index
|
Advanced-Index
|
||||||
|
/Architecture/index
|
||||||
/Launcher/index
|
/Launcher/index
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ into multiple files.
|
||||||
val buildVersion = "2.0.29"
|
val buildVersion = "2.0.29"
|
||||||
val buildScalaVersion = "2.9.0-1"
|
val buildScalaVersion = "2.9.0-1"
|
||||||
|
|
||||||
val buildSettings = Defaults.defaultSettings ++ Seq (
|
val buildSettings = Seq (
|
||||||
organization := buildOrganization,
|
organization := buildOrganization,
|
||||||
version := buildVersion,
|
version := buildVersion,
|
||||||
scalaVersion := buildScalaVersion,
|
scalaVersion := buildScalaVersion,
|
||||||
|
|
|
||||||
|
|
@ -22,20 +22,12 @@ Don't use default package
|
||||||
Users who have their build files in some package will not be able to
|
Users who have their build files in some package will not be able to
|
||||||
use your plugin if it's defined in default (no-name) package.
|
use your plugin if it's defined in default (no-name) package.
|
||||||
|
|
||||||
Avoid overriding `settings`
|
Avoid older `sbt.Plugin` mechanism
|
||||||
-----------------------------
|
----------------------------------
|
||||||
|
|
||||||
sbt will automatically load your plugin's `settings` into the build.
|
sbt has deprecated the old `sbt.Plugin` mechanism in favor of `sbt.AutoPlugin`.
|
||||||
Overriding `val settings` should only be done by plugins intending to
|
The new mechanism features a set of user-level controls and dependency declarations
|
||||||
provide commands. Regular plugins defining tasks and settings should
|
that cleans up a lot of long-standing issues with plugins.
|
||||||
provide a sequence named after the plugin like so:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
val obfuscateSettings = Seq(...)
|
|
||||||
|
|
||||||
This allows build user to choose which subproject the plugin would be
|
|
||||||
used. See later section for how the settings should be scoped.
|
|
||||||
|
|
||||||
Reuse existing keys
|
Reuse existing keys
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,10 @@ It is recommended to explicitly specify the commit or tag by appending it to the
|
||||||
|
|
||||||
lazy val assemblyPlugin = uri("git://github.com/sbt/sbt-assembly#0.9.1")
|
lazy val assemblyPlugin = uri("git://github.com/sbt/sbt-assembly#0.9.1")
|
||||||
|
|
||||||
|
One caveat to using this method is that the local sbt will try to run the remote plugin's build. It
|
||||||
|
is quite possible that the plugin's own build uses a different sbt version, as many plugins cross-publish for
|
||||||
|
several sbt versions. As such, it is recommended to stick with binary artifacts when possible.
|
||||||
|
|
||||||
2) Use the library
|
2) Use the library
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
@ -221,22 +225,25 @@ To make a plugin, create a project and configure `sbtPlugin` to
|
||||||
`true`. Then, write the plugin code and publish your project to a
|
`true`. Then, write the plugin code and publish your project to a
|
||||||
repository. The plugin can be used as described in the previous section.
|
repository. The plugin can be used as described in the previous section.
|
||||||
|
|
||||||
A plugin can implement `sbt.Plugin`. The contents of a Plugin
|
A plugin can implement `sbt.AutoImpot`. The contents of an AutoImport
|
||||||
singleton, declared like `object MyPlugin extends Plugin`, are
|
singleton, declared like `object MyPlugin extends AutoImport`, are
|
||||||
wildcard imported in `set`, `eval`, and `.sbt` files. Typically,
|
wildcard imported in `set`, `eval`, and `.sbt` files. Typically,
|
||||||
this is used to provide new keys (SettingKey, TaskKey, or InputKey) or
|
this is used to provide new keys (SettingKey, TaskKey, or InputKey) or
|
||||||
core methods without requiring an import or qualification.
|
core methods without requiring an import or qualification.
|
||||||
|
|
||||||
In addition, a `Plugin` can implement `projectSettings`, `buildSettings`, and `globalSettings` as appropriate.
|
In addition, a plugin can implement the `AutoPlugin` class. This has additoinal features, such as
|
||||||
The Plugin's `projectSettings` is automatically appended to each project's settings.
|
|
||||||
|
* Specifying plugin dependencies.
|
||||||
|
* Specifying `projectSettings`, `buildSettings`, and `globalSettings` as appropriate.
|
||||||
|
|
||||||
|
The AutoPlugin's `projectSettings` is automatically appended to each project's settings, when its dependencies also exist on that project
|
||||||
|
The `select` method defines the conditions by which this plugin's settings are automatically imported.
|
||||||
The `buildSettings` is appended to each build's settings (that is, `in ThisBuild`).
|
The `buildSettings` is appended to each build's settings (that is, `in ThisBuild`).
|
||||||
The `globalSettings` is appended once to the global settings (`in Global`).
|
The `globalSettings` is appended once to the global settings (`in Global`).
|
||||||
These allow a plugin to automatically provide new functionality or new defaults.
|
These allow a plugin to automatically provide new functionality or new defaults.
|
||||||
One main use of this feature is to globally add commands, such as for IDE plugins.
|
One main use of this feature is to globally add commands, such as for IDE plugins.
|
||||||
Use `globalSettings` to define the default value of a setting.
|
Use `globalSettings` to define the default value of a setting.
|
||||||
|
|
||||||
These automatic features should be used judiciously because the automatic activation generally reduces control for the build author (the user of the plugin).
|
|
||||||
Some control is returned to them via `Project.autoSettings`, which changes how automatically added settings are added and in what order.
|
|
||||||
|
|
||||||
Example Plugin
|
Example Plugin
|
||||||
--------------
|
--------------
|
||||||
|
|
@ -258,16 +265,18 @@ An example of a typical plugin:
|
||||||
::
|
::
|
||||||
|
|
||||||
import sbt._
|
import sbt._
|
||||||
object MyPlugin extends Plugin
|
object MyPlugin extends AutoPlugin
|
||||||
{
|
{
|
||||||
|
// Only enable this plugin for projects which are JvmModules.
|
||||||
|
def select = sbt.plugins.JvmModule
|
||||||
|
|
||||||
// configuration points, like the built in `version`, `libraryDependencies`, or `compile`
|
// configuration points, like the built in `version`, `libraryDependencies`, or `compile`
|
||||||
// by implementing Plugin, these are automatically imported in a user's `build.sbt`
|
// by implementing Plugin, these are automatically imported in a user's `build.sbt`
|
||||||
val newTask = taskKey[Unit]("A new task.")
|
val newTask = taskKey[Unit]("A new task.")
|
||||||
val newSetting = settingKey[String]("A new setting.")
|
val newSetting = settingKey[String]("A new setting.")
|
||||||
|
|
||||||
// a group of settings ready to be added to a Project
|
// a group of settings that are automatically added to projects.
|
||||||
// to automatically add them, do
|
val projectSettings = Seq(
|
||||||
val newSettings = Seq(
|
|
||||||
newSetting := "test",
|
newSetting := "test",
|
||||||
newTask := println(newSetting.value)
|
newTask := println(newSetting.value)
|
||||||
)
|
)
|
||||||
|
|
@ -289,7 +298,17 @@ A build definition that uses the plugin might look like:
|
||||||
|
|
||||||
newSetting := "example"
|
newSetting := "example"
|
||||||
|
|
||||||
Example command plugin
|
|
||||||
|
Root Plugins
|
||||||
|
------------
|
||||||
|
|
||||||
|
Some plugins should always be explicitly enabled on projects. Sbt calls these "RootPlugins", i.e. plugins
|
||||||
|
that are "root" nodes in the plugin depdendency graph. To define a root plugin, just extend the `sbt.RootPlugin`
|
||||||
|
interface. This interface is exactly like the `AutoPlugin` interface except that a `select` method is not
|
||||||
|
needed.
|
||||||
|
|
||||||
|
|
||||||
|
Example command root plugin
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
A basic plugin that adds commands looks like:
|
A basic plugin that adds commands looks like:
|
||||||
|
|
@ -310,9 +329,9 @@ A basic plugin that adds commands looks like:
|
||||||
|
|
||||||
import sbt._
|
import sbt._
|
||||||
import Keys._
|
import Keys._
|
||||||
object MyPlugin extends Plugin
|
object MyPlugin extends RootPlugin
|
||||||
{
|
{
|
||||||
override lazy val settings = Seq(commands += myCommand)
|
override lazy val projectSettings = Seq(commands += myCommand)
|
||||||
|
|
||||||
lazy val myCommand =
|
lazy val myCommand =
|
||||||
Command.command("hello") { (state: State) =>
|
Command.command("hello") { (state: State) =>
|
||||||
|
|
@ -327,6 +346,28 @@ included in one plugin (for example, use `commands ++= Seq(a,b)`). See
|
||||||
:doc:`Commands` for defining more useful commands, including ones that
|
:doc:`Commands` for defining more useful commands, including ones that
|
||||||
accept arguments and affect the execution state.
|
accept arguments and affect the execution state.
|
||||||
|
|
||||||
|
For a user to consume this plugin, it requires an explicit include via the `Project` instance.
|
||||||
|
Here's what their local sbt will look like.
|
||||||
|
|
||||||
|
`build.sbt`
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
val root = Project("example-plugin-usage", file(".")).setPlugins(MyPlugin)
|
||||||
|
|
||||||
|
|
||||||
|
The `setPlugins` method allows projects to explicitly define the `RootPlugin`s they wish to consume.
|
||||||
|
`AutoPlugin`s are automatically added to the project as appropriate.
|
||||||
|
|
||||||
|
Projects can also exclude any type of plugin using the `disablePlugins` method. For example, if
|
||||||
|
we wish to remove the JvmModule settings (`compile`,`test`,`run`), we modify our `build.sbt` as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
val root = Project("example-plugin-usage", file(".")).setPlugins(MyPlugin).disablePlugins(plugins.JvmModule)
|
||||||
|
|
||||||
|
|
||||||
Global plugins example
|
Global plugins example
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ The following two files illustrate. First, if your project is in
|
||||||
|
|
||||||
lazy val root = Project(id = "hello",
|
lazy val root = Project(id = "hello",
|
||||||
base = file("."),
|
base = file("."),
|
||||||
settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala"))
|
settings = Seq(sampleKeyB := "B: in the root project settings in Build.scala"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Now, create `hello/build.sbt` as follows:
|
Now, create `hello/build.sbt` as follows:
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,36 @@ Adding settings for a plugin
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
A plugin can declare that its settings be automatically added, in which case you don't have to do anything to add them.
|
A plugin can declare that its settings be automatically added, in which case you don't have to do anything to add them.
|
||||||
However, plugins often avoid this because you wouldn't control which projects in a :doc:`multi-project build <Multi-Project>` would use the plugin.
|
|
||||||
The plugin documentation will indicate how to configure it, but typically it involves adding the base settings for the plugin and customizing as necessary.
|
As of sbt 0.13.2, there is a new :doc:`auto-plugins <../DetailedTopics/AutoPlugins>` feature that enables plugins
|
||||||
|
to automatically, and safely, ensure their settings and dependencies are on a project. Most plugins should have
|
||||||
|
their default settings automatically, however some may require explicit enablement.
|
||||||
|
|
||||||
|
If you're using a plugin that requires explicit enablement, then you you have to add the following to your
|
||||||
|
`build.sbt` ::
|
||||||
|
|
||||||
|
lazy val util = project.setPlugins(ThePluginIWant)
|
||||||
|
|
||||||
|
Most plugins document whether they need to explicitly enabled. If you're curious which plugins are enabled
|
||||||
|
for a given project, just run the `plugins` command on the sbt console.
|
||||||
|
|
||||||
|
For example ::
|
||||||
|
|
||||||
|
> plugins
|
||||||
|
In file:/home/jsuereth/projects/sbt/test-ivy-issues/
|
||||||
|
sbt.plugins.IvyModule: enabled in test-ivy-issues
|
||||||
|
sbt.plugins.JvmModule: enabled in test-ivy-issues
|
||||||
|
sbt.plugins.GlobalModule: enabled in test-ivy-issues
|
||||||
|
|
||||||
|
|
||||||
|
Here, the plugins output is showing that the sbt default plugins are all enabled. Sbt's default settings are provided via three plugins:
|
||||||
|
|
||||||
|
1. GlobalModule: Provides the core parallelism controls for tasks
|
||||||
|
2. IvyModule: Provides the mechanisms to publish/resolve modules.
|
||||||
|
3. JvmModule: Provides the mechanisms to compile/test/run/package Java/Scala projects.
|
||||||
|
|
||||||
|
|
||||||
|
However, older plugins often required settings to be added explictly, so that :doc:`multi-project build <Multi-Project>` could have different types of projects. The plugin documentation will indicate how to configure it, but typically for older plugins this involves adding the base settings for the plugin and customizing as necessary.
|
||||||
|
|
||||||
For example, for the sbt-site plugin, add ::
|
For example, for the sbt-site plugin, add ::
|
||||||
|
|
||||||
|
|
@ -91,9 +119,10 @@ To create an sbt plugin,
|
||||||
|
|
||||||
1. Create a new project for the plugin.
|
1. Create a new project for the plugin.
|
||||||
2. Set `sbtPlugin := true` for the project in `build.sbt`. This adds a dependency on sbt and will detect and record Plugins that you define.
|
2. Set `sbtPlugin := true` for the project in `build.sbt`. This adds a dependency on sbt and will detect and record Plugins that you define.
|
||||||
3. (optional) Define an `object` that extends `Plugin`. The contents of this object will be automatically imported in `.sbt` files, so ensure it only contains important API definitions and types.
|
3. Define an `object` that extends `AutoPlugin` or `RootPlugin`. The contents of this object will be automatically imported in `.sbt` files, so ensure it only contains important API definitions and types.
|
||||||
4. Define any custom tasks or settings (see the next section :doc:`Custom-Settings`).
|
4. Define any custom tasks or settings (see the next section :doc:`Custom-Settings`).
|
||||||
5. Collect the default settings to apply to a project in a list for the user to add. Optionally override one or more of Plugin's methods to have settings automatically added to user projects.
|
5. Collect the default settings to apply to a project in a list for the user to add. Optionally override one or more of `AutoPlugin`'s methods to have settings automatically added to user projects.
|
||||||
|
6. (Optional) For non-root plguins, declare dependencies on other plugins by overriding the `select` method.
|
||||||
6. Publish the project. There is a :doc:`community repository </Community/Community-Plugins>` available for open source plugins.
|
6. Publish the project. There is a :doc:`community repository </Community/Community-Plugins>` available for open source plugins.
|
||||||
|
|
||||||
For more details, including ways of developing plugins, see :doc:`/Extending/Plugins`.
|
For more details, including ways of developing plugins, see :doc:`/Extending/Plugins`.
|
||||||
|
|
|
||||||
|
|
@ -444,24 +444,28 @@ before it is initialized with an empty sequence.
|
||||||
settings = Seq(
|
settings = Seq(
|
||||||
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
|
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
|
||||||
)
|
)
|
||||||
)
|
).disablePlugins(plugins.IvyModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
To correct this, include the default settings, which includes
|
To correct this, include the IvyModule plugin settings, which includes
|
||||||
`libraryDependencies := Seq()`.
|
`libraryDependencies := Seq()`. So, we just drop the explicit disabling.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
settings = Defaults.defaultSettings ++ Seq(
|
object MyBuild extends Build {
|
||||||
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
|
val root = Project(id = "root", base = file("."),
|
||||||
)
|
settings = Seq(
|
||||||
|
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
A more subtle variation of this error occurs when using :doc:`scoped settings </Getting-Started/Scopes>`.
|
A more subtle variation of this error occurs when using :doc:`scoped settings </Getting-Started/Scopes>`.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
// error: Reference to uninitialized setting
|
// error: Reference to uninitialized setting
|
||||||
settings = Defaults.defaultSettings ++ Seq(
|
settings = Seq(
|
||||||
libraryDependencies += "commons-io" % "commons-io" % "1.2" % "test",
|
libraryDependencies += "commons-io" % "commons-io" % "1.2" % "test",
|
||||||
fullClasspath := fullClasspath.value.filterNot(_.data.name.contains("commons-io"))
|
fullClasspath := fullClasspath.value.filterNot(_.data.name.contains("commons-io"))
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue