From 70a54b98484047d3b41c786b733d85cd4fe35048 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 7 Aug 2013 18:24:19 -0400 Subject: [PATCH] initial refresh of getting started guide for 0.13: multiple projects and plugins still todo: Custom-Settings, Full-Def --- src/sphinx/Detailed-Topics/Best-Practices.rst | 2 + src/sphinx/Extending/Plugins.rst | 106 +++---- .../Getting-Started/Custom-Settings.rst | 7 +- src/sphinx/Getting-Started/Full-Def.rst | 2 +- .../Getting-Started/Library-Dependencies.rst | 2 +- src/sphinx/Getting-Started/Multi-Project.rst | 233 ++++++++------- src/sphinx/Getting-Started/Using-Plugins.rst | 266 +++++------------- src/sphinx/Getting-Started/index.rst | 4 +- 8 files changed, 253 insertions(+), 369 deletions(-) diff --git a/src/sphinx/Detailed-Topics/Best-Practices.rst b/src/sphinx/Detailed-Topics/Best-Practices.rst index 05a10ef49..50d857f17 100644 --- a/src/sphinx/Detailed-Topics/Best-Practices.rst +++ b/src/sphinx/Detailed-Topics/Best-Practices.rst @@ -4,6 +4,8 @@ Best Practices This page describes best practices for working with sbt. +.. _global-vs-local-plugins: + `project/` vs. `~/.sbt/` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/sphinx/Extending/Plugins.rst b/src/sphinx/Extending/Plugins.rst index 5684ce1e9..3a855073f 100644 --- a/src/sphinx/Extending/Plugins.rst +++ b/src/sphinx/Extending/Plugins.rst @@ -12,9 +12,8 @@ example, you might use markdown processing task. A plugin can define a sequence of sbt Settings that are automatically added to all projects or that are explicitly declared for selected projects. For example, a plugin might add a -'proguard' task and associated (overridable) settings. Because -:doc:`Commands` can be added with the `commands` setting, a plugin can -also fulfill the role that processors did in 0.7.x. +`proguard` task and associated (overridable) settings. +Also, :doc:`Commands` can be added with the `commands` setting The :doc:`Plugins-Best-Practices` page describes the currently evolving guidelines to writing sbt plugins. See also the @@ -59,7 +58,7 @@ Specifically, 3. Sources in the `project/` project are the build definition files and are compiled using the classpath built from the managed and unmanaged dependencies. -4. Project dependencies can be declared in +4. Project dependencies can be declared in `project/plugins.sbt` or `project/project/Build.scala` and will be available to the build definition sources. Think of `project/project/` as the build definition for the build definition. @@ -85,14 +84,7 @@ This means that plugins will not see classes or resources from build definitions Global plugins -------------- -In sbt 0.7.x, a processor was a way to add new commands to sbt and -distribute them to users. A key feature was the ability to have per-user -processors so that once declared, it could be used in all projects for -that user. In sbt 0.10+, plugins and processors are unified. -Specifically, a plugin can add commands and plugins can be declared -globally for a user. - -The `~/.sbt/plugins/` directory is treated as a global plugin +The :sublit:`|globalPluginsBase|` directory is treated as a global plugin definition project. It is a normal sbt project whose classpath is available to all sbt project definitions for that user as described above for per-project plugins. @@ -161,35 +153,28 @@ To switch back to the main project: 1d) Project dependency ~~~~~~~~~~~~~~~~~~~~~~ -This variant shows how to use the external project support in sbt 0.10 -to declare a source dependency on a plugin. This means that the plugin -will be built from source and used on the classpath. +This variant shows how to use sbt's external project support to declare a source dependency on a plugin. +This means that the plugin will be built from source and used on the classpath. -Edit `project/project/Build.scala` +Edit `project/plugins.sbt` :: - import sbt._ - object PluginDef extends Build { - override lazy val projects = Seq(root) - lazy val root = Project("plugins", file(".")) dependsOn( webPlugin ) - lazy val webPlugin = uri("git://github.com/JamesEarlDouglas/xsbt-web-plugin") - } + lazy val root = project.in( file(".") ).dependsOn( assemblyPlugin ) + lazy val assemblyPlugin = uri("git://github.com/sbt/sbt-assembly") If sbt is running, run `reload`. -Note that this approach can be useful used when developing a plugin. A -project that uses the plugin will rebuild the plugin on `reload`. This -saves the intermediate steps of `publishLocal` and `cleanPlugins` -required in 0.7. It can also be used to work with the development -version of a plugin from its repository. +Note that this approach can be useful used when developing a plugin. +A project that uses the plugin will rebuild the plugin on `reload`. +This saves the intermediate steps of `publishLocal` and `update`. +It can also be used to work with the development version of a plugin from its repository. -It is recommended to explicitly specify the commit or tag by appending -it to the repository as a fragment: +It is recommended to explicitly specify the commit or tag by appending it to the repository as a fragment: :: - lazy val webPlugin = uri("git://github.com/JamesEarlDouglas/xsbt-web-plugin#0.9.7") + lazy val assemblyPlugin = uri("git://github.com/sbt/sbt-assembly#0.9.1") 2) Use the library ~~~~~~~~~~~~~~~~~~ @@ -222,7 +207,7 @@ Introduction ------------ A minimal plugin is a Scala library that is built against the version of -Scala that sbt runs (currently, 2.9.1) or a Java library. Nothing +Scala that sbt runs (currently, |scalaRelease|) or a Java library. Nothing special needs to be done for this type of library, as shown in the previous section. A more typical plugin will provide sbt tasks, commands, or settings. This kind of plugin may provide these settings @@ -240,13 +225,18 @@ A plugin can implement `sbt.Plugin`. The contents of a Plugin singleton, declared like `object MyPlugin extends Plugin`, are wildcard imported in `set`, `eval`, and `.sbt` files. Typically, this is used to provide new keys (SettingKey, TaskKey, or InputKey) or -core methods without requiring an import or qualification. In addition, -the `settings` member of the `Plugin` is automatically appended to -each project's settings. This allows a plugin to automatically provide -new functionality or new defaults. One main use of this feature is to -globally add commands, like a processor in sbt 0.7.x. These features -should be used judiciously because the automatic activation removes -control from the build author (the user of the plugin). +core methods without requiring an import or qualification. + +In addition, a `Plugin` can implement `projectSettings`, `buildSettings`, and `globalSettings` as appropriate. +The Plugin's `projectSettings` is automatically appended to each project's settings. +The `buildSettings` is appended to each build's settings (that is, `in ThisBuild`). +The `globalSettings` is appended once to the global settings (`in Global`). +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. +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 -------------- @@ -289,32 +279,15 @@ An example of a typical plugin: Usage example ------------- -A light build definition that uses the plugin might look like: +A build definition that uses the plugin might look like: + +`build.sbt` :: MyPlugin.newSettings - newSetting := "light" - -A full build definition that uses this plugin might look like: - -:: - - object MyBuild extends Build - { - lazy val projects = Seq(root) - lazy val root = Project("root", file(".")) settings( MyPlugin.newSettings : _*) settings( - MyPlugin.newSetting := "full" - ) - } - -Individual settings could be defined in `MyBuild.scala` above or in a -`build.sbt` file: - -:: - - newSettings := "overridden" + newSetting := "example" Example command plugin ---------------------- @@ -358,7 +331,7 @@ Global plugins example ---------------------- The simplest global plugin definition is declaring a library or plugin -in `~/.sbt/plugins/build.sbt`: +in :sublit:`|globalPluginsBase|\ build.sbt`: :: @@ -369,14 +342,14 @@ user. In addition: -1. Jars may be placed directly in `~/.sbt/plugins/lib/` and will be +1. Jars may be placed directly in :sublit:`|globalPluginsBase|\ lib/` and will be available to every build definition for the current user. 2. Dependencies on plugins built from source may be declared in - ~/.sbt/plugins/project/Build.scala\` as described at + :sublit:`|globalPluginsBase|\ project/Build.scala` as described at :doc:`/Getting-Started/Full-Def`. 3. A Plugin may be directly defined in Scala source files in - `~/.sbt/plugins/`, such as `~/.sbt/plugins/MyPlugin.scala`. - `~/.sbt/plugins/build.sbt` should contain `sbtPlugin := true`. + :sublit:`|globalPluginsBase|`, such as :sublit:`|globalPluginsBase|\ MyPlugin.scala`. + :sublit:`|globalPluginsBase|\ /build.sbt` should contain `sbtPlugin := true`. This can be used for quicker turnaround when developing a plugin initially: @@ -388,7 +361,7 @@ In addition: overhead of `publishLocal` and cleaning the plugins directory of the project using the plugin. -These are all consequences of `~/.sbt/plugins/` being a standard +These are all consequences of :sublit:`|globalPluginsBase|` being a standard project whose classpath is added to every sbt project's build definition. @@ -398,3 +371,6 @@ Best Practices If you're a plugin writer, please consult the :doc:`Plugins-Best-Practices` page; it contains a set of guidelines to help you ensure that your plugin is consistent with and plays well with other plugins. + +.. |globalBase| replace:: ~/.sbt/|version|/ +.. |globalPluginsBase| replace:: |globalBase|\ plugins/ diff --git a/src/sphinx/Getting-Started/Custom-Settings.rst b/src/sphinx/Getting-Started/Custom-Settings.rst index 8dd6b3977..16715739d 100644 --- a/src/sphinx/Getting-Started/Custom-Settings.rst +++ b/src/sphinx/Getting-Started/Custom-Settings.rst @@ -82,8 +82,8 @@ you can often use the convenient APIs in Use plugins! ------------ -If you find you have a lot of custom code in `.scala` files, consider -moving it to a plugin for re-use across multiple projects. +If you find you have a lot of custom code, consider +moving it to a plugin for re-use across multiple builds. It's very easy to create a plugin, as :doc:`teased earlier ` and :doc:`discussed at more length here `. @@ -93,4 +93,5 @@ Next This page has been a quick taste; there's much much more about custom tasks on the :doc:`/Detailed-Topics/Tasks` page. -You're at the end of Getting Started! There's a :doc:`brief recap `. +Move on to :doc:`Full-Def`. + diff --git a/src/sphinx/Getting-Started/Full-Def.rst b/src/sphinx/Getting-Started/Full-Def.rst index cf7fe8dbe..fb70b6be9 100644 --- a/src/sphinx/Getting-Started/Full-Def.rst +++ b/src/sphinx/Getting-Started/Full-Def.rst @@ -265,4 +265,4 @@ the build definition. Next ---- -Move on to :doc:`using plugins `. +You're at the end of Getting Started! There's a :doc:`brief recap `. diff --git a/src/sphinx/Getting-Started/Library-Dependencies.rst b/src/sphinx/Getting-Started/Library-Dependencies.rst index b42636c69..feca9a495 100644 --- a/src/sphinx/Getting-Started/Library-Dependencies.rst +++ b/src/sphinx/Getting-Started/Library-Dependencies.rst @@ -245,4 +245,4 @@ dependencies :doc:`on this page `, if you d answer on this introductory page. If you're reading Getting Started in order, for now, you might move on -to read :doc:`.scala build definition `. +to read :doc:`Multi-Project`. diff --git a/src/sphinx/Getting-Started/Multi-Project.rst b/src/sphinx/Getting-Started/Multi-Project.rst index 720405b09..8da02aab9 100644 --- a/src/sphinx/Getting-Started/Multi-Project.rst +++ b/src/sphinx/Getting-Started/Multi-Project.rst @@ -5,8 +5,8 @@ Multi-Project Builds This page introduces multiple projects in a single build. Please read the earlier pages in the Getting Started Guide first, in -particular you need to understand :doc:`build.sbt ` and -:doc:`.scala build definition ` before reading this page. +particular you need to understand :doc:`build.sbt ` before +reading this page. Multiple projects ----------------- @@ -15,38 +15,122 @@ It can be useful to keep multiple related projects in a single build, especially if they depend on one another and you tend to modify them together. -Each sub-project in a build has its own `src/main/scala`, generates +Each sub-project in a build has its own source directories, generates its own jar file when you run `package`, and in general works like any other project. -Defining projects in a `.scala` file --------------------------------------- +A project is defined by declaring a `lazy val` of type `Project <../../api/sbt/Project.html>`_. +For example, :: -To have multiple projects, you must declare each project and how they -relate in a `.scala` file; there's no way to do it in a `.sbt` file. -However, you can define settings for each project in `.sbt` files. -Here's an example of a `.scala` file which defines a root project -`hello`, where the root project aggregates two sub-projects, -`hello-foo` and `hello-bar`: + lazy val util = project + + lazy val core = project + +The name of the val is used as the project's ID and base directory name. +The ID is used to refer to the project at the command line. +The base directory may be changed from the default using the `in` method. +For example, the following is a more explicit way to write the previous example: :: + + lazy val util = project.in( file("util") ) + + lazy val core = project in file("core") + +Dependencies +------------ + +Projects in the build can be completely independent of one another, but usually +they will be related to one another by some kind of dependency. There are two +types of dependencies: aggregate and classpath. + + +Aggregation +~~~~~~~~~~~ + +Aggregation means that running a task on the aggregate project will also +run it on the aggregated projects. For example, :: + + lazy val root = + project.in( file(".") ) + .aggregate(util, core) + + lazy val util = project + + lazy val core = project + + +In the above example, the root project aggregates `util` and `core`. +Start up sbt with two subprojects as in the example, and try `compile`. +You should see that all three projects are compiled. + +*In the project doing the aggregating*, the `root` project in +this case, you can control aggregation per-task. +For example, to avoid aggregating the `update` task: :: - import sbt._ - import Keys._ + lazy val root = + project.in( file(".") ) + .aggregate(util, core) + .settings( + aggregate in update := false + ) - object HelloBuild extends Build { - lazy val root = Project(id = "hello", - base = file(".")) aggregate(foo, bar) + [...] - lazy val foo = Project(id = "hello-foo", - base = file("foo")) +`aggregate in update` is the `aggregate` key scoped to the `update` task. +(See :doc:`scopes `.) - lazy val bar = Project(id = "hello-bar", - base = file("bar")) - } +Note: aggregation will run the aggregated tasks in parallel and with no +defined ordering between them. + +Classpath dependencies +~~~~~~~~~~~~~~~~~~~~~~ + +A project may depend on code in another project. This is done by adding +a `dependsOn` method call. For example, if `core` needed +`util` on its classpath, you would define `core` as: + +:: + + lazy val core = project.dependsOn(util) + +Now code in `core` can use classes from `util`. +This also creates an ordering between the projects when compiling them; +`util` must be updated and compiled before `core` can be compiled. + +To depend on multiple projects, use multiple arguments to `dependsOn`, +like `dependsOn(bar, baz)`. + +Per-configuration classpath dependencies +++++++++++++++++++++++++++++++++++++++++ + +`foo dependsOn(bar)` means that the `compile` configuration in +`foo` depends on the `compile` configuration in `bar`. You could +write this explicitly as `dependsOn(bar % "compile->compile")`. + +The `->` in `"compile->compile"` means "depends on" so +`"test->compile"` means the `test` configuration in `foo` would +depend on the `compile` configuration in `bar`. + +Omitting the `->config` part implies `->compile`, so +`dependsOn(bar % "test")` means that the `test` configuration in +`foo` depends on the `Compile` configuration in `bar`. + +A useful declaration is `"test->test"` which means `test` depends on `test`. +This allows you to put utility code for testing in `bar/src/test/scala` and then use that code in `foo/src/test/scala`, +for example. + +You can have multiple configurations for a dependency, separated by +semicolons. For example, +`dependsOn(bar % "test->test;compile->compile")`. + + +Default root project +-------------------- + +If a project is not defined for the root directory in the build, sbt creates a default +one that aggregates all other projects in the build. -sbt finds the list of `Project` objects using reflection, looking for -fields with type `Project` in the `Build` object. Because project `hello-foo` is defined with `base = file("foo")`, it will be contained in the subdirectory `foo`. Its sources could be @@ -93,79 +177,6 @@ You may find it cleaner to put everything including settings in You cannot have a `project` subdirectory or `project/*.scala` files in the sub-projects. `foo/project/Build.scala` would be ignored. -Aggregation ------------ - -Projects in the build can be completely independent of one another, if -you want. - -In the above example, however, you can see the method call -`aggregate(foo, bar)`. This aggregates `hello-foo` and `hello-bar` -underneath the root project. - -Aggregation means that running a task on the aggregate project will also -run it on the aggregated projects. Start up sbt with two subprojects as -in the example, and try `compile`. You should see that all three -projects are compiled. - -*In the project doing the aggregating*, the root `hello` project in -this case, you can control aggregation per-task. So for example in -`hello/build.sbt` you could avoid aggregating the `update` task: - -:: - - aggregate in update := false - -`aggregate in update` is the `aggregate` key scoped to the -`update` task, see :doc:`scopes `. - -Note: aggregation will run the aggregated tasks in parallel and with no -defined ordering. - -Classpath dependencies ----------------------- - -A project may depend on code in another project. This is done by adding -a `dependsOn` method call. For example, if `hello-foo` needed -`hello-bar` on its classpath, you would write in your `Build.scala`: - -:: - - lazy val foo = Project(id = "hello-foo", - base = file("foo")) dependsOn(bar) - -Now code in `hello-foo` can use classes from `hello-bar`. This also -creates an ordering between the projects when compiling them; -`hello-bar` must be updated and compiled before `hello-foo` can be -compiled. - -To depend on multiple projects, use multiple arguments to `dependsOn`, -like `dependsOn(bar, baz)`. - -Per-configuration classpath dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`foo dependsOn(bar)` means that the `Compile` configuration in -`foo` depends on the `Compile` configuration in `bar`. You could -write this explicitly as `dependsOn(bar % "compile->compile")`. - -The `->` in `"compile->compile"` means "depends on" so -`"test->compile"` means the `Test` configuration in `foo` would -depend on the `Compile` configuration in `bar`. - -Omitting the `->config` part implies `->compile`, so -`dependsOn(bar % "test")` means that the `Test` configuration in -`foo` depends on the `Compile` configuration in `bar`. - -A useful declaration is `"test->test"` which means `Test` depends on -`Test`. This allows you to put utility code for testing in -`bar/src/test/scala` and then use that code in `foo/src/test/scala`, -for example. - -You can have multiple configurations for a dependency, separated by -semicolons. For example, -`dependsOn(bar % "test->test;compile->compile")`. - Navigating projects interactively --------------------------------- @@ -175,7 +186,35 @@ a task like `compile`, it runs on the current project. So you don't necessarily have to compile the root project, you could compile only a subproject. +You can run a task in another project by explicitly specifying the +project ID, such as `subProjectID/compile`. + +Common code +----------- + +The definitions in `.sbt` files are not visible in other `.sbt` files. +In order to share code between `.sbt` files, define one or more Scala +files in the `project/` directory of the build root. This directory +is also an sbt project, but for your build. + +For example: + +`/project/Common.scala` :: + + import sbt._ + import Keys._ + + object Common { + def text = "org.example" + } + +``/build.sbt :: + + organization := Common.text + +See :doc:`Full-Def` for details. + Next ---- -Move on to create :doc:`custom settings `. +Move on to :doc:`using plugins `. diff --git a/src/sphinx/Getting-Started/Using-Plugins.rst b/src/sphinx/Getting-Started/Using-Plugins.rst index df3f06539..7a950aaca 100644 --- a/src/sphinx/Getting-Started/Using-Plugins.rst +++ b/src/sphinx/Getting-Started/Using-Plugins.rst @@ -3,225 +3,66 @@ Using Plugins ============= Please read the earlier pages in the Getting Started Guide first, in -particular you need to understand :doc:`build.sbt `, +particular you need to understand :doc:`build.sbt ` and :doc:`library dependencies `, -and :doc:`.scala build definition ` before reading -this page. +before reading this page. What is a plugin? ----------------- A plugin extends the build definition, most commonly by adding new settings. The new settings could be new tasks. For example, a plugin -could add a `code-coverage` task which would generate a test coverage +could add a `codeCoverage` task which would generate a test coverage report. -Adding a plugin ---------------- +Declaring a plugin +------------------ -The short answer -~~~~~~~~~~~~~~~~ +If your project is in directory `hello`, edit `hello/project/plugins.sbt` and declare the plugin dependency by passing the plugin's Ivy module ID to `addSbtPlugin`: :: -If your project is in directory `hello`, edit -`hello/project/build.sbt` and add the plugin location as a resolver, -then call `addSbtPlugin` with the plugin's Ivy module ID: + addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.7.0") -:: +Not every plugin is located on one of the default repositories and a plugin's documentation may instruct you to also add the repository where it can be found: :: - resolvers += Classpaths.typesafeResolver + resolvers += ... - addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") +Plugins usually provide settings that get added to a project to enable the plugin's functionality. +This is described in the next section. + + +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. +However, plugins often avoid this because you wouldn't control which projects in a :doc:`multi-project build ` 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. + +For example, for the sbt-site plugin, add :: + + site.settings + +to a `build.sbt` to enable it for that project. + +If the build defines multiple projects, instead add it directly to the project: :: + + // don't use the site plugin for the `util` project + lazy val util = project + + // enable the site plugin for the `core` project + lazy val core = project.settings( site.settings : _*) -If the plugin were located on one of the default repositories, you -wouldn't have to add a resolver, of course. Global plugins -~~~~~~~~~~~~~~ +-------------- -Plugins can be installed for all your projects at once by dropping them -in `~/.sbt/plugins/`. `~/.sbt/plugins/` is an sbt project whose -classpath is exported to all sbt build definition projects. Roughly -speaking, any `.sbt` files in `~/.sbt/plugins/` behave as if they -were in the `project/` directory for all projects, and any `.scala` -files in `~/.sbt/plugins/project/` behave as if they were in the -`project/project/` directory for all projects. +Plugins can be installed for all your projects at once by dropping them in :sublit:`|globalPluginsBase|`. +:sublit:`|globalPluginsBase|` is an sbt project whose classpath is exported to all sbt build definition projects. +Roughly speaking, any `.sbt` or `.scala` files in :sublit:`|globalPluginsBase|` behave as if they were in the `project/` directory for all projects. -You can create `~/.sbt/plugins/build.sbt` and put `addSbtPlugin()` +You can create :sublit:`|globalPluginsBase|\ build.sbt` and put `addSbtPlugin()` expressions in there to add plugins to all your projects at once. - -How it works -~~~~~~~~~~~~ - -Be sure you understand the :doc:`recursive nature of sbt projects ` -described earlier and how to add a :doc:`managed dependency `. - -Dependencies for the build definition -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Adding a plugin means *adding a library dependency to the build -definition*. To do that, you edit the build definition for the build -definition. - -Recall that for a project `hello`, its build definition project lives -in `hello/*.sbt` and `hello/project/*.scala`: - -.. code-block:: text - - - hello/ # your project's base directory - - build.sbt # build.sbt is part of the source code for the - # build definition project inside project/ - - project/ # base directory of the build definition project - - Build.scala # a source file in the project/ project, - # that is, a source file in the build definition - -If you wanted to add a managed dependency to project `hello`, you -would add to the `libraryDependencies` setting either in -`hello/*.sbt` or `hello/project/*.scala`. - -You could add this in `hello/build.sbt`: - -:: - - libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test" - -If you add that and start up the sbt interactive mode and type -`show dependencyClasspath`, you should see the derby jar on your -classpath. - -To add a plugin, do the same thing but recursed one level. We want the -*build definition project* to have a new dependency. That means changing -the `libraryDependencies` setting for the build definition of the -build definition. - -The build definition of the build definition, if your project is -`hello`, would be in `hello/project/*.sbt` and -`hello/project/project/*.scala`. - -The simplest "plugin" has no special sbt support; it's just a jar file. -For example, edit `hello/project/build.sbt` and add this line: - -:: - - libraryDependencies += "net.liftweb" % "lift-json" % "2.0" - -Now, at the sbt interactive prompt, `reload plugins` to enter the -build definition project, and try `show dependencyClasspath`. You -should see the lift-json jar on the classpath. This means: you could use -classes from lift-json in your `Build.scala` or `build.sbt` to -implement a task. You could parse a JSON file and generate other files -based on it, for example. Remember, use `reload return` to leave the -build definition project and go back to the parent project. - -(Stupid sbt trick: type `reload plugins` over and over. You'll find -yourself in the project rooted in -`project/project/project/project/project/project/`. Don't worry, it -isn't useful. Also, it creates `target` directories all the way down, -which you'll have to clean up.) - -`addSbtPlugin` -^^^^^^^^^^^^^^^^ - -`addSbtPlugin` is just a convenience method. Here's its definition: - -:: - - def addSbtPlugin(dependency: ModuleID): Setting[Seq[ModuleID]] = - libraryDependencies += - sbtPluginExtra(dependency, (sbtVersion in update).value, scalaVersion.value) - -The appended dependency is based on `sbtVersion in update` -(sbt's version scoped to the `update` task) and `scalaVersion` (the -version of scala used to compile the project, in this case used to -compile the build definition). `sbtPluginExtra` adds the sbt and Scala -version information to the module ID. - -`plugins.sbt` -^^^^^^^^^^^^^^^ - -Some people like to list plugin dependencies (for a project `hello`) -in `hello/project/plugins.sbt` to avoid confusion with -`hello/build.sbt`. sbt does not care what `.sbt` files are called, -so both `build.sbt` and `project/plugins.sbt` are conventions. sbt -*does* of course care where the sbt files are *located*. `hello/*.sbt` -would contain dependencies for `hello` and `hello/project/*.sbt` -would contain dependencies for `hello`'s build definition. - -Plugins can add settings and imports automatically --------------------------------------------------- - -In one sense a plugin is just a jar added to `libraryDependencies` for -the build definition; you can then use the jar from build definition -code as in the lift-json example above. - -However, jars intended for use as sbt plugins can do more. - -If you download a plugin jar (`here's one for -sbteclipse `_) -and unpack it with `jar xf`, you'll see that it contains a text file -`sbt/sbt.plugins`. In `sbt/sbt.plugins` there's an object name on -each line like this: - -.. code-block:: text - - com.typesafe.sbteclipse.SbtEclipsePlugin - -`com.typesafe.sbteclipse.SbtEclipsePlugin` is the name of an object -that extends `sbt.Plugin`. The `sbt.Plugin` trait is very simple: - -:: - - trait Plugin { - def settings: Seq[Setting[_]] = Nil - } - -sbt looks for objects listed in `sbt/sbt.plugins`. When it finds -`com.typesafe.sbteclipse.SbtEclipsePlugin`, it adds -`com.typesafe.sbteclipse.SbtEclipsePlugin.settings` to the settings -for the project. It also does -`import com.typesafe.sbteclipse.SbtEclipsePlugin._` for any `.sbt` -files, allowing a plugin to provide values, objects, and methods to -`.sbt` files in the build definition. - -Adding settings manually from a plugin --------------------------------------- - -If a plugin defines settings in the `settings` field of a `Plugin` -object, you don't have to do anything to add them. - -However, plugins often avoid this because you could not control which -projects in a :doc:`multi-project build ` would use the plugin. - -A whole batch of settings can be added by directly referencing the sequence of settings in a `build.sbt` file. So, if a plugin has something like this: - -:: - - object MyPlugin extends Plugin { - val myPluginSettings = Seq(settings in here) - } - -You could add all those settings in `build.sbt` with this syntax: - -:: - - myPluginSettings - -Creating a plugin ------------------ - -After reading this far, you pretty much know how to *create* an sbt -plugin as well. There's one trick to know; set `sbtPlugin := true` in -`build.sbt`. If `sbtPlugin` is true, the project will scan its -compiled classes for instances of `Plugin`, and list them in -`sbt/sbt.plugins` when it packages a jar. `sbtPlugin := true` also -adds sbt to the project's classpath, so you can use sbt APIs to -implement your plugin. - -Learn more about creating a plugin at :doc:`/Extending/Plugins` -and :doc:`/Extending/Plugins-Best-Practices`. +This feature should be used sparingly, however. +See :ref:`Best Practices `. Available Plugins ----------------- @@ -236,7 +77,32 @@ Some especially popular plugins are: :doc:`Check out the list`. + +Creating a Plugin +----------------- + +A minimal plugin is a Scala library that is built against the version of Scala that sbt runs, which is currently |scalaVersion|. +Nothing special needs to be done for this type of plugin. +It can be published as a normal project and declared in `project/plugins.sbt` like a normal dependency (without `addSbtPlugin`). + +A more typical plugin will provide sbt tasks, commands, or settings. +This kind of plugin may provide these settings automatically or make them available for the user to explicitly integrate. +To create an sbt 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. + 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. + 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. + 6. Publish the project. There is a :doc:`community repository ` available for open source plugins. + +For more details, including ways of developing plugins, see :doc:`/Extending/Plugins`. +For best practices, see :doc:`/Extending/Plugins-Best-Practices`. + Next ---- -Move on to :doc:`multi-project builds `. +Move on to create :doc:`custom settings `. + +.. |globalBase| replace:: ~/.sbt/|version|/ +.. |globalPluginsBase| replace:: |globalBase|\ plugins/ diff --git a/src/sphinx/Getting-Started/index.rst b/src/sphinx/Getting-Started/index.rst index fa0e9378e..bc5b0a1b0 100644 --- a/src/sphinx/Getting-Started/index.rst +++ b/src/sphinx/Getting-Started/index.rst @@ -14,8 +14,8 @@ Getting Started Scopes More-About-Settings Library-Dependencies - Full-Def - Using-Plugins Multi-Project + Using-Plugins Custom-Settings + Full-Def Summary