diff --git a/Design-Overview.md b/Design-Overview.md new file mode 100644 index 0000000..2c1b9a2 --- /dev/null +++ b/Design-Overview.md @@ -0,0 +1,114 @@ +# Overview of sbt's Design + +The main execution layers that comprise sbt are the launcher, the command engine, and the build tool, which is subdivided into project configuration and task execution. + +## Launcher + +Execution starts with the [launcher], which is a self-contained jar that pulls down an application and its dependencies according to a configuration file and then runs it. +For sbt, this means getting the requested version of sbt (from `project/build.properties`) and the right Scala version and then starting sbt itself. +The launcher also provides several services to launched applications, like reloading a different version of the application or retrieving other Scala versions. +It is possible to use the launcher to launch other applications as well as documented on the [launcher] page. +[Conscript] is a lightweight application distribution mechanism built on top of this functionality. + +## Command Engine + +The next level is the command engine, which is where [State] and [Commands] come in. +When sbt is launched, it sets up an initial State that registers the default Commands (`set, reload, alias, ...`) and sets the initial commands to run. +The initial commands are: add commands from `.sbtrc` files, load the project, and enter the interactive prompt if no commands were specified. +Then, sbt starts the engine with `MainLoop.runLogged(initialState)`, which runs until all commands are processed and then exits. +You can see this configuration in [xMain.run], which is the entry-point to sbt. + +State keeps the list of scheduled command strings in remainingCommands. +runLogged processes State by: + +1. taking the next command string from remainingCommands (a `Seq[String]`) +2. parsing it according to the current `State`'s definedCommands (a `Seq[Command]`) +3. producing the command's `State => State` function from this parse +4. applying the function to the current `State` (running the command) +5. looping back to 1 if the new State has remainingCommands + +There are actually two alternative entry-points to sbt that are defined below the main one, [ScriptMain] and [ConsoleMain], that provide the different front-ends described on the [Scripts] page. +In addition, the command engine can be used to write standalone interactive [command line applications]. + +## Build tool + +It is on top of this command engine that the build tool part of sbt is built. +The build tool part is kicked off with the command that loads the build: `reload` (called `reload` because users usually call it in the context of reloading the build). +The other important build-tool-related command runs tasks on a loaded build. + +### Build loading + +The `reload` command's job is to produce a [BuildStructure] and put it in [State] for future commands like task execution to use. +[BuildStructure] is the data type that represents everything about a build: projects and relationships, evaluated settings, and logging configuration. +It produces a [BuildStructure] by evaluating [build loaders]. +The default build loader configures sbt using the standard `.sbt` and `project/Build.scala` files that you know. +The [build loaders] page shows an example of how one might write a build loader to read from a pom.xml instead (you couldn't define custom tasks or anything like that without another file, though). + +Once the `reload` command has the `BuildStructure` value, it stores it in `State.attributes`, keyed by `Keys.stateBuildStructure`. + +### State.attributes + +Now, a diversion back to `State` to cover attributes... +`State.attributes` is a [typesafe map]. +Keys are of type `AttributeKey[T]` and you can only associate values of type `T` with that key. +`State` has convenience methods set/get that delegate to the underlying `attributes` map. + +To pass information between commands, you put data in the `attributes` map. +An example of this is [release plugin], which sets `skipTests` according to the command line options. +The `release` command itself schedules other commands to run and those can configure themselves from `skipTests` in `State.attributes`. +This is one way a command can change the behavior of tasks without needing to reload the project: it sets attributes in `State` and the task accesses `State` via the `state` task. + +### Project.extract + +The `Project.extract(state)` call at its core calls `state.get(Keys.stateBuildStructure)` to get the `BuildStructure` back. +It does some other things as well: + + * throws a nicer exception if a project isn't loaded + * loads the session with `state.get(Keys.sessionSettings)` + * returns the session and structure in an [Extracted] value, which provides a better interface to them + +### Session settings + +The [SessionSettings] datatype tracks a few pieces of information that are not persisted. +The two main pieces are: + + * the current project: changed by the `project` command, for example + * additional settings: added by the `set` command, for example + +SessionSettings only tracks this information; setting these values on a `SessionSettings` object does not apply the changes. +In particular, the project has to be reloaded for the additional settings to take effect. +Reloading checks the settings for problems like references to non-existing settings and then the settings are reevaluated. +The release plugin has a [reapply] method that shows the proper way to add settings to the current project. + +### Modifying settings + +Given a sequence of settings (`Seq[Setting[_]]`), the `Load.transformSettings` method resolves any unspecified scopes in the raw settings sequence. +This usually means associating the settings with the current project. +Then, `BuiltinCommands.reapply` actually makes the settings take effect. +It checks, processes, and loads the new settings, updates `BuildStructure`, and stuffs everything back into `State`. + +### Tasks + +The task execution command pulls out the current, loaded project from `State` (via `Project.extract`), looks up the task to run, and runs it. +Commands can get the values produced by tasks, but tasks don't directly transform `State` or run commands (there are some rare exceptions). +Tasks can get the current `State` via the `state` task, which is a special task that gets injected by the task execution command and set to the current `State`. + +## Comments, requests for clarification + +[xMain.run]: http://www.scala-sbt.org/0.12.1/sxr/Main.scala.html#11563 +[launcher]: http://www.scala-sbt.org/release/docs/Detailed-Topics/Launcher.html +[command line applications]: http://www.scala-sbt.org/release/docs/Extending/Command-Line-Applications.html +[Scala Days 2012 talk]: http://skillsmatter.com/podcast/scala/tab-completion-parser-combinators +[Conscript]: https://github.com/n8han/conscript +[State]: http://www.scala-sbt.org/release/docs/Extending/Build-State.html +[Commands]: http://www.scala-sbt.org/release/docs/Extending/Commands.html +[ScriptMain]: http://www.scala-sbt.org/0.12.1/sxr/Main.scala.html#11564 +[ConsoleMain]: http://www.scala-sbt.org/0.12.1/sxr/Main.scala.html#11565 +[Scripts]: http://www.scala-sbt.org/release/docs/Detailed-Topics/Scripts.html +[build loaders]: http://www.scala-sbt.org/release/docs/Extending/Build-Loaders.html +[BuildStructure]: http://www.scala-sbt.org/0.12.1/api/sbt/Load$$BuildStructure.html +[Extracted]: http://www.scala-sbt.org/0.12.1/api/sbt/Extracted.html +[typesafe map]: http://www.scala-sbt.org/0.12.1/api/sbt/AttributeMap.html +[SessionSettings]: http://www.scala-sbt.org/0.12.1/api/sbt/SessionSettings.html +[release plugin]: https://github.com/sbt/sbt-release +[reapply]: https://github.com/sbt/sbt-release/blob/master/src/main/scala/ReleaseExtra.scala#L228