Here's a short little guide to sbt's build structure.
First off, the key you need to investigate how sbt is building things. Here's a snippet from a build.sbt
:
import sbt.Keys._
val bs: sbt.BuildStructure = buildStructure.value
This will give you all the gory details of our build (or builds, as is the case).
Note: Sbt may have more than one build definition loaded, each with its own classpath of sorts (actually these get merged, but that's an implementation restriction really). If you use a source dependency, like ProjectRef(....)
in your build, you may wind up with more than one sbt build getting loaded
Next, let's look at each of these loaded builds, we can grab that via "units" on the build structure. A single unit represents a "meta-build". e.g. the project/
directory of the current working directory represents one such "unit". If you have a source dependency (e.g. a git repository), that would be another "unit". Each unit is indexed by the source URI ->
> bs.units
res0: Map[java.net.URI,sbt.LoadedBuildUnit] = Map(file:/home/jsuereth/projects/sbt/sbt/ -> /home/jsuereth/projects/sbt/sbt)
You can see the "base" directory for the project here is my local checkout of sbt. This is a mapped lookup of all the sbt.LoadedBuiildUnit
. A sbt.LoadedBuildUnit
will give you everything you need to generate an ENSIME project that can autocomplete even the .sbt format.
Here's the useful bits:
> val testUnit = bs.units.values.head // Hackery to just get the only one for now
testUnit: sbt.LoadedBuildUnit = /home/jsuereth/projects/sbt/sbt
scala> testUnit.classpath
res10: Seq[java.io.File] = List(/home/jsuereth/projects/sbt/sbt/project/target/scala-2.10/sbt-0.13/classes, /home/jsuereth/.sbt/0.13/plugins/target/scala-2.10/sbt-0.13/classes, /home/jsuereth/.ivy2/cache/scala_2.10/sbt_0.13/com.github.mpeltonen/sbt-idea/jars/sbt-idea-1.6.0.jar, /home/jsuereth/.ivy2/cache/commons-io/commons-io/jars/commons-io-2.0.1.jar, /home/jsuereth/.ivy2/cache/scala_2.10/sbt_0.13/me.lessis/sbt-growl-plugin/jars/sbt-growl-plugin-0.1.3.jar, /home/jsuereth/.ivy2/cache/me.lessis/meow_2.10/jars/meow_2.10-0.1.1.jar, /home/jsuereth/.sbt/boot/scala-2.10.4/lib/scala-library.jar, /home/jsuereth/.ivy2/cache/scala_2.10/sbt_0.13/com.jsuereth/sbt-pgp/jars/sbt-pgp-1.0.0.jar, /home/jsuereth/.ivy2/cache/com.jsuereth/pgp-library_2.10/jars/pgp-library_2.10-1.0.0.jar, /home/jsuereth/.ivy...
scala> testUnit.imports
res12: Seq[String] = List(import sbt._, Keys._, dsl._, import _root_.org.sbtidea.SbtIdeaPlugin._, _root_.growl.GrowlingTests._, _root_.com.typesafe.sbt.SbtSite._, _root_.com.typesafe.sbt.SbtGhPages._, _root_.com.typesafe.sbt.SbtGit._, _root_.com.typesafe.sbt.SbtScalariform._, import _root_.com.typesafe.sbt.SbtPgp.autoImport._, _root_.com.typesafe.sbt.JavaVersionCheckPlugin.autoImport._, _root_.bintray.BintrayPlugin.autoImport._, import StatusPlugin.autoImport._, SbtLauncherPlugin.autoImport._, NightlyPlugin.autoImport._, import _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.com.typesafe.sbt.SbtPgp, _root_.com.typesafe.sbt.JavaVersionCheckPlugin, _root_.sbtdoge.DogePlugin, _root_.bintray.BintrayPl...
scala> testUnit.rootProjects
res13: Seq[String] = List(sbtRoot)
Note: Yes, people COULD have multi-module meta-builds. We do this as a testing trick for some plugins, although don't worry about supporting it for now, as i'm not sure a lot of people use that feature
scala> testUnit.unit.plugins.detected.autoPlugins
res26: Seq[sbt.DetectedAutoPlugin] = List(DetectedAutoPlugin(sbt.plugins.IvyPlugin,sbt.plugins.IvyPlugin,false), DetectedAutoPlugin(sbt.plugins.JvmPlugin,sbt.plugins.JvmPlugin,false), DetectedAutoPlugin(sbt.plugins.CorePlugin,sbt.plugins.CorePlugin,false), DetectedAutoPlugin(sbt.plugins.JUnitXmlReportPlugin,sbt.plugins.JUnitXmlReportPlugin,false), DetectedAutoPlugin(com.typesafe.sbt.SbtPgp,com.typesafe.sbt.SbtPgp,true), DetectedAutoPlugin(com.typesafe.sbt.JavaVersionCheckPlugin,com.typesafe.sbt.JavaVersionCheckPlugin,true), DetectedAutoPlugin(sbtdoge.DogePlugin,sbtdoge.DogePlugin,false), DetectedAutoPlugin(bintray.BintrayPlugin,bintray.BintrayPlugin,true), DetectedAutoPlugin(StatusPlugin,StatusPlugin,true), DetectedAutoPlugin(SbtLauncherPlugin,SbtLauncherPlugin,true), DetectedAutoPlugin...
Some of these may need to live in alternative indicies so you can provide smarter autocompletion for .sbt or build.scala files, possibly even prompting the user to have their build.scala include some of the auto-imports from AutoPlugins.
In any case, hope this helps you implement what you need. I'm still debating how much of that to publicize, as we're not locked down 100% on interfaces. You can be assured nothing i've documented here will change significantly, but there may be tweaks between 0.13 and 1.0 for removing old-school plugins and doing some general TLC/cleanup.