Go forth and build! ...my code!
Sooooo... Scala Build Tool.Like all things with me recently, it started out by reading a book about Scala while commuting to work (riding the tube for 2 hours daily gives you a LOT of time to read). Now, this is like... the 8th (?) programming language I've learned in my life? Anyway, suffice to say, by the time I got around to feeding my OCD and writing the obligatory "Hello World" using Scala IDE for Eclipse, I probably knew more Scala than was good for me.
Anyway, I was determined to mingle with the cool kids, so I started looking at Akka's actors. Within minutes, I was overwhelmed by a desire to use Akka for re-enacting a movie scene. So here it is, from The Big Lebowski, a scene with The Dude (Akka actor Jeff Bridges) and Jackie (Akka actor Ben Gazzara):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | object BigLebowski extends App { implicit val system = ActorSystem( "the-stage" ) // our actors val gazzara = system.actorOf(Props[BenGazzara]) val bridges = system.actorOf(Props[JeffBridges]) // the roles val jackie = new Movie.Role( "Jackie Treehorn" ) val dude = new Movie.Role( "The Dude" ) // the script for our scene val script = Movie.Script( "BigLebowski-Scene-1" , // gazzara plays jackie, and bridges plays "the dude" Map(gazzara -> jackie, bridges -> dude), // here's the scene's dialogue: (jackie, "Interactive erotic software. The wave of the future, Dude. " + "One hundred percent electronic!" ), (dude, "Yeah well, I still jerk off manually." )) // director distributes script, and... Action! system.actorSelection( "/user/*" ) ! script } |
Hello, SBT
XML is so very lame, that the cool kids don't even know what that is. It is rumored an old man once tried to explain it to them, but none of them payed attention (except the few that died of boredom 30 seconds into the explanation). The net outcome was that the cool kids never knew the joys of Maven and Ant, but at least they lived to write tools like Gradle and SBT. But this is all beside the point. The point is, you wanna be jaw-dropping-awesome. And here's how you do it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | version := "0.0.1" name := "big-lebowski-movie" scalaVersion := "2.11.4" resolvers + = Opts.resolver.mavenLocalFile libraryDependencies ++ = { val akkaV = "2.3.8" Seq( "com.typesafe.akka" %% "akka-actor" % akkaV ) } |
SBT's model and API
The first thing you should know about this file is that the content is actually regular Scala: there is no such thing as some special SBT language that you must learn to define your project build. The theory goes...if you know Scala you already know how to write a build.sbt file. But that's only half the truth really. It is half the truth because there is such a thing as an SBT library, which defines a Scala API for describing your build. So to write a build.sbt you need to know Scala and also the SBT library API.
Which brings us to the second thing you need to know about build.sbt: the following lines are implied to exist at the very beginning of the build.sbt file:
1 2 3 4 5 | import sbt. _ import Process. _ import Keys. _ // actual content of build.sbt follows here |
It's all keys and settings
Here's an inconvenient truth: assume you could disregard for a moment all the coolness of SBT, what do you think you'd be left with? It may come as a surprise, but you'd actually find yourself thinking of a Map data structure. Yes, I mean that simple data structure that associates keys with values. But in the case of SBT, we have several pre-defined keys with special meaning, and values that are instances of the Scala class Setting[T] (defined by the SBT library). So here's that file again, this time with enlightening comments:1 2 3 4 5 6 7 8 9 10 | // the key "version" is assigned a Setting[String] value of "0.0.1" version := "0.0.1" // the key "name" is assigned a Setting[String] value of "big-lebowski-movie" name := "big-lebowski-movie" // the key "scalaVersion" is assigned a Setting[String] value of "2.11.4" scalaVersion := "2.11.4" // we'll discuss the rest later |
We now discuss the special key resolvers, along with the += method. We have invoked resolvers.+=(...) twice:
1 2 3 4 5 6 7 8 | // this tells SBT to look for dependencies in your local maven repository resolvers + = Opts.resolver.mavenLocalFile // above line is actually resolvers.+=(Opts.resolver.mavenLocalFile) // this tells SBT to look for dependencies in TypeSafe's Ivy repository resolvers + = Resolver.typesafeIvyRepo( "releases" ) // above line is actually resolvers.+=(Resolver.typesafeIvyRepo("releases")) |
And the final key we used is libraryDependencies, whose value is a Setting[Seq[ModuleID]], where ModuleID is an SBT class representing a dependency:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // libraryDependencies.++=(...) libraryDependencies ++ = { // the curly braces are simply a Scala block of code. therefore the block's // value is equal to the last expression in the block // but first, we assign a value variable val akkaV = "2.3.8" // try adding this line and running sbt again: // println("YES, IT'S REALLY JUST SCALA CODE!") // this is the last expression of the code block. // as such, the entire block evaluates to the statement below Seq( // the three strings below are the organization, name and revision respectively // the %% and % methods create a ModuleID object from the 3 strings "com.typesafe.akka" %% "akka-actor" % akkaV // so the net result is a Seq[ModuleID], with a single element in it ) } |
The %% and % methods are used to construct a ModuleID object using three String values. Notice how we use the variable akkaV for the revision. In an actual use case where we might pull in several Akka modules, keeping the version in a single place will make it easy to change to another version.
Blank lines of coolness
The only open point right now should be the blank lines. If you look back at our original file, you will see that each key-setting definition is followed by one (blank line). This is NOT a coincidence. In fact, if you remove one of them, you will see SBT complaining while parsing the file. The reason for this is that SBT expects build.sbt to contain a list of key-setting mapping expressions (as opposed to statements). Therefore some separator needs to be used. The newline character happens to be what SBT chose as a delimiter. You can read all about it here.
Conclusion
A few last words. Like other build tools, SBT is extensible. Several plug-ins exist that extend its capabilities. One way to add plugins is by using the project/plugins.sbt file, which we use to install a plugin called sbt-assembly:
1 | addSbtPlugin( "com.eed3si9n" % "sbt-assembly" % "0.12.0" ) |
Another plugin you may find useful if you use Scala IDE for Eclipse is the sbteclipse-plugin. This will generate Eclipse project files for your code so that you may work with it in Eclipse. You just need to run sbt eclipse and the required files (.project and .classpath) will be generated. All you need is to add the plugin:
1 | addSbtPlugin( "com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0" ) |
No comments:
Post a Comment