Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
430 lines (409 sloc) 19.4 KB
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,900%7CRoboto+Mono" rel="stylesheet">
<title>Essential SBT (build tool for Scala)</title>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.13/clipboard.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/toolbar/prism-toolbar.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-bash.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism-okaidia.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
<style type="text/css">
body {
font-family: 'Roboto', sans-serif;
line-height:1.6;
color:rgb(5,5,5);
background:rgb(255,255,255);
}
article > h2 {
text-transform: uppercase;
}
code {
font-family: 'Roboto Mono', monospace;
}
article pre {
margin-left:3em;
}
article {
margin-bottom:3em;
}
#share {
margin-right:auto;
border-radius:2em;
margin-bottom:1.6em;
background:rgb(250,250,255);
border:1px dotted rgb(245,245,250);
max-width:32em;
padding:1.6em;
padding-top:0.9em;
}
#me {
margin-bottom:3em;
margin-right:auto;
border-radius:2em;
background:rgb(250,250,255);
border:1px dotted rgb(245,245,250);
max-width:34em;
}
#me {
display:flex;
line-height:1;
}
#me> div {
padding:0em 1em;
}
#me >img {
border-radius: 2em;
image-resolution: snap;
height: 11em;
}
article a{
text-decoration: none;
}
a {
color:darkslategrey;
}
a:hover {
color:darkslateblue;
}
article > h2 {
cursor:default;
}
article > h2 > a {
display:block;
cursor:default;
}
code {
cursor:default;
}
article > h2:hover > a{
color:darkslateblue;
background-color: beige;
}
.filtered { display:none;}
@media(max-width:640px) {
.filtered {
display:none;
}
}
.filter-on .filtered {
filter:blur(1.2px) grayscale(0.6) sepia(0.2);
}
.filtered {
position:absolute;
right:2em;
}
nav a {
text-decoration: none;
}
#atftbx > p > span { display:none; }
nav button {
box-shadow: 0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;
font-family: inherit;
font-size: 100%;
padding: .5em 1em;
color: #444;
border: 1px solid #999;
border: 0 rgba(0,0,0,0);
background-color: #E6E6E6;
text-decoration: none;
border-radius: 2px;
}
nav.hide ul {
display:none;
}
nav ul {
list-style-type: none;
}
nav li:before {
content: "";
}
pre {
max-width:42em;
}
#dsq-app1 {
z-index:5;
}
#video iframe {
width:31.5em;
max-width:100%;
height:17em;
}
</style>
<!-- thanks to https://gist.github.com/hotmeteor/5055c1dab2f1043058c2 -->
<meta property="og:url" content="https://www.scalawilliam.com/essential-sbt/">
<!-- http://ogp.me/ -->
<meta property="og:type" content="article">
<meta property="article:published_time" content="2016-12-10">
<meta property="article:modified_time" content="2018-04-19">
<meta property="og:title" content="Essential SBT for Scala">
<meta property="og:image" content="https://cloud.githubusercontent.com/assets/2464813/21298157/aa2bc418-c5c7-11e6-8026-318c092da59e.png">
<meta property="og:description" content="Guide to Essential SBT for Scala. Clear and concise examples for the person who wants to use SBT in production. Includes an sbt-native-packager example to produce a Docker image. Includes an example of SBT modules and ScalaTest TDD.">
<meta itemprop="description" content="Guide to Essential SBT for Scala. Clear and concise examples for the person who wants to use SBT in production. Includes an sbt-native-packager example to produce a Docker image. Includes an example of SBT modules and ScalaTest TDD.">
<meta name="description" content="Guide to Essential SBT for Scala. Clear and concise examples for the person who wants to use SBT in production. Includes an sbt-native-packager example to produce a Docker image. Includes an example of SBT modules and ScalaTest TDD.">
<meta name="twitter:description" content="Guide to Essential SBT for Scala. Clear and concise examples for the person who wants to use SBT in production. Includes an sbt-native-packager example to produce a Docker image. Includes an example of SBT modules and ScalaTest TDD.">
<meta property="og:site_name" content="Scala William">
<link rel="author" href="https://plus.google.com/u/0/103489630517643950426/">
<link rel="publisher" href="https://plus.google.com/u/0/103489630517643950426/">
<meta itemprop="name" content="Essential SBT for Scala">
<meta itemprop="image" content="https://cloud.githubusercontent.com/assets/2464813/21298157/aa2bc418-c5c7-11e6-8026-318c092da59e.png">
<meta name="author" content="William Narmontas">
<link rel="canonical" href="https://www.scalawilliam.com/essential-sbt/">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ScalaWilliam">
<meta name="twitter:title" content="Essential SBT for Scala">
<meta name="twitter:image" content="https://cloud.githubusercontent.com/assets/2464813/21298157/aa2bc418-c5c7-11e6-8026-318c092da59e.png">
<script type="text/javascript">
function toggle(cnt){
if ( cnt.classList.contains("hide") ) {
cnt.classList.remove("hide");
} else {
cnt.classList.add("hide");
}
}
function toggle_contents(){
return toggle(document.querySelector("#contents"));
}
function toggle_references(){
return toggle(document.querySelector("#references"));
}
function toggle_changelog(){
return toggle(document.querySelector("#changelog"));
}
</script>
</head>
<body>
<header>
<h1>Essential SBT (build tool for Scala)</h1>
<h2>By <a href="/">William "Scala William" Narmontas</a></h2>
<blockquote>Don't learn how SBT works, learn how to use it!</blockquote>
<p>To run these commands you'll need a shell or a terminal. For Windows, you may want to install <a href="http://babun.github.io/">Babun, a Windows shell you will love</a>.</p>
<p>SBT installation steps:
<a href="http://www.scala-sbt.org/release/docs/Installing-sbt-on-Mac.html">Mac</a>,
<a href="http://www.scala-sbt.org/release/docs/Installing-sbt-on-Windows.html">Windows</a>,
<a href="http://www.scala-sbt.org/release/docs/Installing-sbt-on-Linux.html">Linux</a>,
or <a href="http://www.scala-sbt.org/release/docs/Manual-Installation.html">manual</a>. You'll also need <a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html">Oracle JDK 8</a> or <a href="http://openjdk.java.net/install/">OpenJDK 8</a>.</p>
<p>The aim of this is to show you how SBT <em>can be</em> simple to get started with, and how to get production level iterations going. We don't aim to build a strong understanding of SBT - there are plenty of other materials online for this. The aim is to be as minimalist as possible: no text editors, no IDEs; just a copy-paste into the shell. Follow along with the video if you're like to see how this works. I hope this to be more fruitful for somebody who comes, say, from a Python or PHP background and prefer to see immediate results.</p>
</header>
<blockquote class="twitter-tweet" data-lang="en"><p lang="pt" dir="ltr">Essential <a href="https://twitter.com/hashtag/SBT?src=hash">#SBT</a> for <a href="https://twitter.com/hashtag/Scala?src=hash">#Scala</a> - <a href="https://t.co/LUKDY8w1Zr">https://t.co/LUKDY8w1Zr</a></p>&mdash; William Narmontas (@ScalaWilliam) <a href="https://twitter.com/ScalaWilliam/status/807439609751707648">December 10, 2016</a></blockquote>
<aside id="video">
<iframe src="https://www.youtube-nocookie.com/embed/JI0i7f2byPY?rel=0" allowfullscreen="" frameborder="0"></iframe>
</aside>
<nav id="contents">
<button onclick="toggle_contents();">Toggle Index / Table of Contents</button>
<ul>
<li> Test 1</li>
</ul>
</nav><br>
<nav id="references">
<button onclick="toggle_references();">Toggle References</button>
<ul>
<li><a href="http://www.scala-lang.org/api/2.12.1/scala/collection/JavaConverters$.html">Scala API: Scala JavaConverters</a></li>
<li><a href="https://jsoup.org/">jsoup: Java HTML Parser</a></li>
<li><a href="http://mvnrepository.com/artifact/org.jsoup/jsoup/1.10.1">Maven artifact for jsoup<br></a></li>
<li><a href="http://www.scala-sbt.org/0.13/docs/Multi-Project.html#Classpath+dependencies">SBT classpath dependencies</a></li>
<li><a href="http://www.scala-sbt.org/1.0/docs/Multi-Project.html">Multi-Project SBT builds</a></li>
<li><a href="http://www.scala-sbt.org/sbt-native-packager/">sbt-native-packager: Code once, deploy anywhere</a></li>
<li><a href="http://www.scala-sbt.org/sbt-native-packager/formats/docker.html">sbt-native-packager: Docker plugin</a></li>
<li><a href="http://www.scala-sbt.org/sbt-native-packager/archetypes/java_app/index.html">sbt-native-packager: JavaAppPackaging</a></li>
<li><a href="http://scala-lang.org/news/2.12.0">Scala 2.12.0 released<br></a></li>
<li><a href="http://www.scalatest.org/">ScalaTest homepage<br></a></li>
<li><a href="http://www.scala-sbt.org/1.0/docs/Triggered-Execution.html">SBT: Triggered Execution</a></li>
<li><a href="https://www.gnu.org/software/sed/">GNU sed: stream editor</a></li></ul></nav>
<br>
<nav id="changelog">
<button onclick="toggle_changelog();">Toggle Changelog</button>
<ul>
<li><strong>March 30, 2018</strong>: Update article to SBT 1.1.2 and other latest libraries</li>
<li><strong>January 15, 2017</strong>: Take in
<a href="https://groups.google.com/d/msg/scala-user/Dn43rIVVIpM/P1yIzgcGCgAJ">Валерий Байбосынов's feedback</a>.
<br/>Added clearer steps with the continuous stuff.</li>
<li><strong>December 13, 2016</strong>: Add references and a changelog</li>
<li><strong>December 10, 2016</strong>: Initial release</li>
</ul>
</nav>
<article>
<h2>Create a basic empty SBT project</h2>
<div class="filtered">AHH</div>
<pre><code>mkdir -p new-project
cd new-project
mkdir -p project
echo 'sbt.version=1.1.2' &gt; project/build.properties</code></pre>
<h2>Compile a project</h2>
<pre><code class="language-bash">sbt compile</code></pre>
<h2>Recompile on code change</h2>
<pre><code>sbt '~compile'</code></pre>
<h2 id="create-source-file"><a href="#create-source-file">Create a source file</a></h2>
<pre><code>mkdir -p src/main/scala/
cat &gt; src/main/scala/SimpleApp.scala &lt;&lt; EOF
object SimpleApp extends App {
println("It works")
}
EOF</code></pre>
<h2>Run continuously</h2>
<p>Run <code>~</code> commands in another terminal window. Press <kbd>Enter</kbd> to exit.</p>
<pre><code>sbt '~run'</code></pre>
<h2>Change source to verify it works</h2>
<pre><code>sed -i -e 's/works/definitely works/' \
src/main/scala/SimpleApp.scala</code></pre>
<h2>Add ScalaTest</h2>
<pre><code>echo 'libraryDependencies += "org.scalatest" %%' \
'"scalatest" % "3.0.5" % "test"' &gt;&gt; build.sbt</code></pre>
<h2>Test continuously</h2>
<pre><code>sbt '~test'</code></pre>
<h2>Create a test</h2>
<pre><code>mkdir -p src/test/scala/
cat &gt; src/test/scala/SimpleTest.scala &lt;&lt; EOF
import org.scalatest._
import Matchers._
class SimpleTest extends FunSuite {
def sum(a: Int, b: Int): Int = ???
test("sum works for 1 + 2 = 3") {
sum(1, 2) shouldEqual 3
}
}
EOF</code></pre>
<h2>Make it pass</h2>
<pre><code>sed -i -e 's/???/a + b/' src/test/scala/SimpleTest.scala</code></pre>
<h2>Use Scala 2.12</h2>
<pre><code>echo 'scalaVersion := "2.12.4"' &gt;&gt; build.sbt</code></pre>
<h2>Add the sbt-native-packager plugin</h2>
<pre><code>echo 'addSbtPlugin("com.typesafe.sbt" % '\
'"sbt-native-packager" % "1.3.3")' \
&gt; project/native-packager.sbt</code></pre>
<h2>Enable sbt-native-packager</h2>
<pre><code>echo 'enablePlugins(JavaAppPackaging)' &gt;&gt; build.sbt</code></pre>
<h2>Package the app</h2>
<pre><code>sbt 'show dist'</code></pre>
<h2>Run packaged app</h2>
<pre><code>unzip -o -d /tmp/my-project \
./target/universal/new-project-0.1.0-SNAPSHOT.zip
/tmp/my-project/new-project-0.1.0-SNAPSHOT/bin/new-project</code></pre>
<h2>Dockerize your app</h2>
<pre><code>eval $(docker-machine env default)
sbt docker:publishLocal
</code></pre>
<h2>Run the docker image</h2>
<pre><code>docker run new-project:0.1.0-SNAPSHOT</code></pre>
<h2>Create a submodule</h2>
<pre><code>echo 'lazy val fizzbuzz = project' &gt;&gt; build.sbt</code></pre>
<h2>Compile the submodule</h2>
<pre><code>sbt fizzbuzz/compile</code></pre>
<h2>Use Scala 2.12 for submodule</h2>
<pre><code>echo ' .settings(scalaVersion := "2.12.4")' &gt;&gt; build.sbt</code></pre>
<h2>Add ScalaTest to submodule</h2>
<pre><code>echo ' .settings(libraryDependencies += "org.scalatest"' \
'%% "scalatest" % "3.0.5" % "test")' &gt;&gt; build.sbt</code></pre>
<h2>Continually test the submodule</h2>
<pre><code>sbt '~fizzbuzz/test'</code></pre>
<h2>Create a method in the submodule</h2>
<pre><code>mkdir -p fizzbuzz/src/main/scala/
cat &gt; fizzbuzz/src/main/scala/Fizz.scala &lt;&lt; EOF
object Fizz {
def buzz(n: Int): String = ???
}
EOF</code></pre>
<h2>Create a test in the submodule</h2>
<pre><code>mkdir -p fizzbuzz/src/test/scala/
cat &gt; fizzbuzz/src/test/scala/FizzTest.scala &lt;&lt; EOF
import org.scalatest._
import Matchers._
class FizzTest extends FunSuite {
test("fizzBuzz for 4 is 4") {
Fizz.buzz(4) shouldEqual "4"
}
}
EOF</code></pre>
<h2>Pass the test</h2>
<pre><code>sed -i -e 's/???/s"$n"/' \
fizzbuzz/src/main/scala/Fizz.scala</code></pre>
<h2>Make the app depend on the submodule</h2>
<pre><code>echo 'dependsOn(fizzbuzz)' &gt;&gt; build.sbt</code></pre>
<h2>Use the submodule in the app</h2>
<pre><code>sed -i -e 's/".*"/Fizz.buzz(args.last.toInt)/'\
src/main/scala/SimpleApp.scala</code></pre>
<h2>Run the app with arguments</h2>
<pre><code>sbt "run --- 123"</code></pre>
<h2>Explicitly name the project</h2>
<pre><code>echo 'name := "new-project"' &gt;&gt; build.sbt</code></pre>
<h2>Explicitly version the project</h2>
<pre><code>echo 'version := "0.2"' &gt;&gt; build.sbt</code></pre>
<h2>Add JSoup Java library to main project</h2>
<pre><code>echo 'libraryDependencies += "org.jsoup" % "jsoup"' \
'% "1.10.1"' &gt;&gt; build.sbt</code></pre>
<h2>Print all BBC News headlines</h2>
<pre><code>mkdir -p src/main/scala/
cat &gt; src/main/scala/SimpleApp.scala &lt;&lt; EOF
import org.jsoup.Jsoup
import scala.collection.JavaConverters._
object SimpleApp extends App {
val doc = Jsoup.connect("http://www.bbc.com/news").get()
val newsHeadlines = doc.select(".title-link__title-text")
newsHeadlines.asScala.map(_.text()).foreach(println)
}
EOF</code></pre>
</article>
<aside id="share">
<p style="font-weight:bold">Learned something? Educate your friends by sharing!</p>
<blockquote class="twitter-tweet" data-cards="hidden" data-lang="en"><p lang="pt" dir="ltr">Essential <a href="https://twitter.com/hashtag/SBT?src=hash">#SBT</a> for <a href="https://twitter.com/hashtag/Scala?src=hash">#Scala</a> - <a href="https://t.co/LUKDY8w1Zr">https://t.co/LUKDY8w1Zr</a></p>&mdash; William Narmontas (@ScalaWilliam) <a href="https://twitter.com/ScalaWilliam/status/807439609751707648">December 10, 2016</a></blockquote>
</aside>
<aside id="me">
<img src="/index/images/_MG_0066_S_S.JPG">
<div>
<h3>Author: William "Scala William" Narmontas</h3>
<p>Scala Specialist since 2013. Went to Cambridge.<br>Worked on many production Scala projects.</p>
<div id="follow">
<p style="font-weight:bold">Useful? Follow me on Social media!</p>
<div class="addthis_inline_follow_toolbox"></div>
</div>
</div>
</aside>
<h2>You may also find this interesting</h2>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I just published “Limit degrees of freedom in development” <a href="https://t.co/A99m04mIua">https://t.co/A99m04mIua</a></p>&mdash; William Narmontas (@ScalaWilliam) <a href="https://twitter.com/ScalaWilliam/status/810082466895695872">December 17, 2016</a></blockquote>
<p>This page is <a href="https://github.com/ScalaWilliam/ScalaWilliam.com/blob/master/essential-sbt/index.html">editable on GitHub</a>.</p>
<script type="text/javascript">
[].forEach.call(document.querySelectorAll("pre > code"), function(item) {
item.classList.add("language-bash") ;
});
var ul = document.querySelector("#contents ul");
while ( ul.firstChild ) {
ul.removeChild(ul.firstChild);
};
[].forEach.call(document.querySelectorAll("article > h2"),function(item) {
var title = item.textContent;
var id = item.getAttribute("id");
if ( !id ) {
id = title.toLowerCase()
.replace(/[^a-z]/g, "-")
.replace(/-+/g,"-");
item.setAttribute("id", id);
}
var li = document.createElement("li");
var a = document.createElement("a");
a.appendChild(document.createTextNode(title));
a.setAttribute("href", "#" + id);
li.appendChild(a);
ul.appendChild(li);
var a2 = document.createElement("a");
a2.setAttribute("href", "#" + id);
a2.appendChild(document.createTextNode(title));
while(item.firstChild) {
item.removeChild(item.firstChild);
}
item.appendChild(a2);
});
</script>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<script>
toggle_contents();
toggle_references();
toggle_changelog();
</script>
<!-- Go to www.addthis.com/dashboard to customize your tools --> <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-584b716cc10a0e3c"></script>
</body></html>