scala-sbt

本文介绍一些关于sbt的使用,关于sbt的安装,请参考sbt install

使用sbt进行编译

对于sbt来说,最简单的工程就是某个目录下只有一个scala文件。对于这种文件,在当前目录下运行sbt,进入sbt控制台后执行run即可。

vim HelloWorld.scala

1
2
3
4
5
object HelloWorld{
def main(args: Array[String]) {
println("Hello, SBT")
}
}

执行:

1
2
3
4
5
6
7
8
9
$sbt
>run
[info] Updating {file:/Users/renweiming/tmp/}tmp...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/renweiming/tmp/target/scala-2.10/classes...
[info] Running HelloWorld
Hello, SBT
[success] Total time: 4 s, completed 2016-7-27 23:19:35

sbt工程的目录结构

Sbt工程的目录结构和Maven的有点儿类似:
sbt_project_structure

src目录结构

  • src/main/java 存放Java源代码
  • src/main/resources 存放相应的资源文件
  • src/main/scala 存放Scala源代码
  • src/test/java 存放Java的测试源代码
  • src/test/resources 存放测试相应的资源文件
  • src/test/scala 存放Scala的测试源代码

build.sbt

build.sbt是build的定义文件。SBT运行使用两种形式的build定义文件:一种是放在根目录下的,即build.sbt文件,是一种简化的build定义;另一种是放在project目录下,采用纯Scala语言编写,更加复杂,当然也更加完备,更加具有表现力。
一个简单的build.sbt文件的内容

1
2
3
4
name := "hello"
organization := "xxx.xxx.xxx"
version := "0.0.1-SNAPSHOT"
scalaVersion := "2.9.2"

其中name和version是必须的,在生成jar的时候,这两个值会作为jar的名称的一部分而存在。
处理上面的部分信息,还可以在build.sbt中添加项目的依赖信息

1
2
3
4
5
6
7
8
9
libraryDependencies += "ch.qos.logback" % "logback-core" % "1.0.0"
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.0.0"
//或者以下格式
libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-core" % "1.0.0",
"ch.qos.logback" % "logback-classic" % "1.0.0"
)

项目目录及相关文件介绍

项目目录下的几个文件实际上都是非必需存在的,恩可以根据情况自由添加。

build.properties

该文件声明使用的要使用哪个版本的SBT来编译当前项目,最新的sbt boot launcher能够兼容编译所有0.10.x版本的SBT,比如如果我们使用的是0.12版本的sbt,但是却想用0.11.3版本的sbt来编译当前项目,则可以在build.properties文件中添加sbt.version=0.11.3进行指定。默认,当前项目的构建采用的是sbt boot lancher对应的版本。

plugins.sbt

该文件用来生命当前项目希望使用那些插件来增强当前项目的sbt功能的使用。比如assembly功能等,都有相应的sbt插件供使用,要使用这些插件只需要在plugins.sbt中声明即可:

1
2
3
4
resolvers += Resolver.url("git://github.com/jrudolph/sbt-dependency-graph.git")
resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0")
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.6.0")

为了能够成功加载这些sbt插件,我们将他们的查找位置添加到resolovers当中。另外,sbt支持直接将相应的github项目作为依赖或者插件依赖,而不用先将相应的依赖或插件发布到maven或ivy的repository中才能用。

其他

以上目录和文件通常是在创建项目的时候需要我们创建的,实际上,SBT还会在编译或运行期间自动的生成某些目录或文件。比如SBT会在项目的根目录下和project目录下生成target目录,并将编译结果和某些缓存信息保存于其中,一般情况,我们不希望将这些目录和文件添加到版本控制中。

SBT的使用

SBT支持两种使用方式:

  • 批处理模式
  • 可交互模式

大多情况下,我们会采用managed dependencies方式来管理依赖,但是在需要快速构建项目的情况下,会使用unmanaged dependencies来管理依赖。

Unmanaged Dependencies

要使用unmanaged dependencies的方式来管理依赖其实很简单,只需要将依赖的jar包放到lib目录下即可。
如果想修改默认的lib目录,可以通过配置进行修改,如3dlibs:

1
unmanagedBase <<= baseDirectory { base => base / "3dlibs" }

通常采用默认目录lib即可,这对于大多数项目都是首选。

Managed Dependencies

managed Dependencies采用Apache lvy的依赖管理方式,可以支持从Maven或lvy的Repository中现在对应的依赖。
要在SBT中使用managed dependencies,需要在libraryDependencies中添加所需要的依赖,一般格式如下:

1
2
3
4
5
6
7
8
9
10
11
libraryDependencies += groupId % artifactID % version
#如:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
#更加细化的控制
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" exclude("org", "artifact")
libraryDependencies += "org.apache.derby" %% "derby" % "10.4.1.3"
#批量添加
libraryDependencies ++= Seq("org.apache.derby" %% "derby" % "10.4.1.3",
"org.scala-tools" %% "scala-stm" % "0.3",
...)

Resovers

对于managed dependencies来说,虽然我们指定了依赖库,但是SBT并不知道去哪里获取这些库以及相关资料。
默认情况下,SBT会去默认的Maven2的Repository中获取依赖,但是如果默认的Repository中找不到我们的依赖,那么我们可以通过resolver机制来追加更多的repository,以便SBT去获取:

1
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

at前面的字符串指定了要追加的repository的标志名称,at后面的字符串指定了要追加repository的路径。
除了可以添加远程的Maven Repository,也可以将本地的Maven Repository添加的resolver的范围中:

1
resolvers += "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository"

使用实例

本人想要使用SBT对自己的spark application代码进行编译打包

手动创建项目目录以及文件

1
2
3
4
5
6
7
8
9
10
11
12
13
./src
./src/test
./src/test/resources
./src/test/scala
./src/test/java
./src/main
./src/main/resources
./src/main/scala
./src/main/scala/com
./src/main/scala/com/antispam
./src/main/scala/com/antispam/WordCount.scala
./src/main/java
./build.sbt

创建Application文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vim ./src/main/scala/com/antispam/WordCount.scala
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
object WordCount {
def main(args:Array[String]) {
val logFile = "/tmp/rwm/README.md"
val conf = new SparkConf().setAppName("Word count application")
val sc = new SparkContext(conf)
val logData = sc.textFile(logFile, 2).cache()
val numAs = logData.filter(line => line.contains("a")).count()
val numBs = logData.filter(line => line.contains("b")).count()
println("lines with a:%s, lines with b:%s".format(numAs, numBs))
}
}

创建sbt构建文件

1
2
3
4
5
6
7
vim ./build.sbt
name := "wordCount"
organization := "com.antispam"
version := "0.0.1"
scalaVersion := "2.11.0"
libraryDependencies +="org.apache.spark"%%"spark-core"%"1.6.1"

使用sbt对项目进行打包

1
sbt package

执行spark的任务提交命令spark-submit

1
spark-submit --class com.antispam.WordCount --master spark://spark-002.yz:8081 word_count_2.11-0.0.1.jar

注意,该实例中,没有使用到除了spark之外的第三方jar包