使用SBT编译Spark

因为对maven比较熟悉,所以一直用maven编译Spark项目,但就像官方所说:

SBT is supported for day-to-day development since it can provide much faster iterative compilation. More advanced developers may wish to use SBT.

所以最好还是折腾一下用SBT好了。

粗浅的理解SBT

SBT目录结构

SBT默认的源文件目录结构与Maven一样,即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
src/
main/
resources/
<files to include in main jar here>
scala/
<main Scala sources>
java/
<main Java sources>
test/
resources
<files to include in test jar here>
scala/
<test Scala sources>
java/
<test Java sources>

其他所有在src/的目录将被忽略。同时,隐藏的文件夹也会被忽略。

SBT项目构建相关的文件目录结构为:

├─src
├─ build.sbt
├─ project
│  ├── build.properties
│  ├── plugins.sbt
│  ├── Build.scala

build.sbt

用于描述该项目的构建方法以及属性,设置项目名称,版本,依赖等,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val derby = "org.apache.derby" % "derby" % "10.4.1.3"

lazy val commonSettings = Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.11.4"
)

lazy val root = (project in file(".")).
settings(commonSettings: _*).
settings(
name := "hello",
libraryDependencies += derby
)

上面的build.sbt构建了名称为hello的项目,并添加了derby依赖。

该文件的命名并不一定需要是build.sbt,可以是任何名称,甚至不一定只能是一个文件,或者甚至可以没有(在其他地方配置项目构建信息,例如spark项目继承了maven的pom.xml文件,所以其没有build.sbt文件)。实际上,SBT会扫描项目目录下的所有.sbt文件并执行,最终生成包含项目所有信息的一个键值对的Map。

project/build.properties

设置SBT的版本

1
sbt.version=0.13.8

这个文件不是必须的:如果没有这个文件,sbt会使用默认的sbt版本版本。

project/

在这里有一个概念,那就是sbt是递归的。也就是说,project/文件夹实际上也是一个sbt项目,这个项目的功能是构建我们的项目。通常我们会给我们的项目命名为root(上面的build.sbt示例中lazy val root = (project in file(".")),这里其实可以不一定要用root,用其他的命名也是可以的,用root是惯例),那么project/目录下的这个项目则会被自动命名为root-build

所以project/中的.sbt文件就是这个root-build项目的构建文件,跟上面的build.sbt一样,它同样可以没有,或者有多个,同样可以有任何命名。一般的惯例是命名为plugin.sbt,这是因为一般在这个文件中配置SBT插件。而实际上所谓的配置插件就是给给root-build项目添加依赖而已,使用的addSbtPlugin其源码也就是在为root-build项目添加依赖。

同样的project/目录下的.scala文件就是root-build的源文件了,sbt会执行这些文件,这些文件中可以定义关于项目root的构建信息,也可以定义多个项目,比较灵活。

全局配置

除了在项目目录中进行配置外,还可以配置全局的构建信息,全局配置将会影响所有的项目。全局配置可以在~/.sbt/build.sbt中可以定义(将影响root项目),全局的插件配置可以在~/.sbt/plugins中新建.sbt.scala文件进行配置(将影响root-build)项目。

设置SBT的repository代理

在Spark源码目录的project/plugins.sbt中可以看到默认的repository的地址:

1
2
3
4
5
resolvers += Resolver.url("artifactory", url("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"

resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/"

但是这几个地址在天朝访问速度是在太慢,于是想办法设置代理源。经过一番查询,发现可以创建文件~/.sbt/repositories,内容如下:

1
2
3
4
5
6
[repositories]
local
local-maven:file:///home/mz/.m2/repository
repo2:http://repo2.maven.org/maven2/
ivy-typesafe:http://dl.bintray.com/typesafe/ivy-releases, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
ivy-sbt-plugin:http://dl.bintray.com/sbt/sbt-plugin-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]

这里设置本地的maven库和一些其他的库地址,其中后面两个地址是ivy的库,所以要在后面加上ivy库路径解析方法。

设置了repository,再使用sbt下载依赖仍然是无比的慢,想了下应该是全局的repository代理无法覆盖项目中设置的,查了一下,需要在SBT_OPTS中加上:

1
-Dsbt.override.build.repos=true

这样就不会使用project/plugins.sbt中定义的库了。

编译Spark

以上是对SBT做了一些粗浅的分析,了解了这么多之后可以初步的使用sbt来编译Spark了。

SBT使用起来类似于maven,不同的是它提供了交互模式和持续构建模式,不过这不在本文的讨论范围。

由于其构建信息集成自maven,maven中的profiles也是有效的,所以可以使用下面的命令进行编译、打包:

1
sbt -Pyarn -Phadoop-2.6 -Phive -Phive-thriftserver assemly

如果要运行测试,最好先进行打包:

1
2
sbt -Pyarn -Phadoop-2.6 -Phive -Phive-thriftserver assemly
sbt -Pyarn -Phadoop-2.6 -Phive -Phive-thriftserver test

编译spark项目所需内存较大,如果用下载的sbt默认的内存是-Xmx1024m,会发生堆内存溢出。最好使用源代码中自带的build/sbt,它把内存设置为了-Xmx2048m,这样编译不会内存溢出。