6.2 SBT

要构建Scala项目,可使用大部分基于JVM的构建工具,包括Apache Maven和Gradle(第4章构建Java项目时使用的构建工具),但Scala提供了自己的构建工具——SBT(Scala Build Tool)。

Scala IDE默认并不支持SBT。稍后你将看到,这不是问题,因为可以反过来做:让SBT支持Eclipse。我们将使用SBT新建一个项目,并安装一个能够创建并更新Eclipse项目的SBT插件。本节介绍如下与SBT相关的主题:

  • 安装SBT;
  • 新建基于SBT的项目;
  • 添加在SBT中添加与Eclipse相关命令的SBT插件。

6.2.1 安装SBT

要安装SBT,请访问http://www.scala-sbt.org,并下载用于你使用的操作系统的最新版本。

对于Windows操作系统,提供了负责安装并设置环境变量Path的MSI安装程序;对于其他操作系统,必须下载并解压缩一个归档文件(ZIP或TGZ),再将其位置添加到环境变量Path中。

与Gradle一样,SBT命令也可以从命令行执行,方法是将命令指定为参数。SBT的独特之处在于,它还提供了一个交互式shell。在交互式模式下运行SBT时,可以交互的方式执行SBT命令;另外,这个shell还支持自动补全——你只需按Tab键即可。

要检查安装情况,可在命令提示符(Windows)或终端(macOS和Linux)中输入sbt并按回车,这将以交互模式启动SBT。

6.2.2 创建基于SBT的Eclipse IDE项目

前面说过,Scala IDE for Eclipse插件存在的一个严重问题是,它当前不支持SBT。通过在SBT中安装一个插件,可从SBT生成Eclipse项目文件。要新建可在安装了Scala IDE插件的Eclipse IDE中打开的基于SBT的项目,可采取如下工作流程。

(1) 使用项目模板新建一个基于SBT的项目。

(2) 在SBT中添加插件sbteclipse。

(3) 在SBT中,使用插件sbteclipse生成一个新的Eclipse IDE/Scala IDE项目。

  • 新建SBT项目

要新建项目,最简单的方式是让SBT生成一个Hello World项目(其中包含一个空的构建文件)。必须在Eclipse IDE的workspace目录(Eclipse IDE用来存储项目的目录)中执行创建新项目的命令。如果你不确定这个目录的位置,可启动Eclipse并尝试创建一个Java项目,这样将显示目录workspace的路径。要新建一个基于SBT的项目,请按如下步骤操作。

  • 启动命令提示符(Windows)或终端(macOS和Linux),并切换到目录workspace,再输入如下命令并按回车:
  1. sbt new sbt/scala-seed.g8
  • 首次执行这个命令时,SBT会下载一些依赖项。过段时间后,它将要求你提供项目名。请输入Akka Quotes并按回车。这将在目录akka-quotes中新建一个项目。

  • 切换到目录akka-quotes,再输入sbt并按回车,以启动SBT的交互式shell。

  • 输入下面的命令并按回车来尝试运行生成的项目:
  1. run

同样,首次运行项目时,SBT将下载一些依赖项。下载完毕后,你将在控制台中看到一条“hello”消息:

6.2 SBT - 图1

输入命令exit并按回车,以退出这个交互式shell。

用来生成这个项目的模板基于最新且稳定的Scala版本,因此必须检查安装的Scala IDE版本是否支持这个版本。为此,在Eclipse IDE中选择菜单Window>Preferences。

在左边的列表中,找到并展开条目Scala,再选择其中的条目Installations。将安装的Scala IDE支持的Scala版本都记录下来;在我的系统中,支持的版本为Scala 2.11.8和Scala 2.10.6。在文本编辑器中,打开目录akka-quotes中生成的构建文件built.sbt。在我的系统中,这个文件类似于下面这样:

  1. import Dependencies._
  2. lazy val root = (project in file(".")).
  3. settings(
  4. inThisBuild(List(
  5. organization := "com.example",
  6. scalaVersion := "2.12.1",
  7. version := "0.1.0-SNAPSHOT"
  8. )),
  9. name := "Hello",
  10. libraryDependencies += scalaTest % Test
  11. )

如果其中的变量scalaVersion的值包含在安装的Scala IDE支持的Scala版本中,就万事大吉。否则,你就必须将变量scalaVersion的值修改为相应的版本。对我来说,必须将这个变量修改成下面这样:

  1. scalaVersion := "2.11.8",

如果必须修改这个变量的值,请在修改后从命令行运行如下命令,以清理并重新编译项目:

  1. sbt clean run

SBT将使用修改后的Scala版本重新编译项目。鉴于这个模板是基于最简单项目的,这通常可行——不会出现任何问题。

6.2 SBT - 图2 使用本章介绍的工具链时,如果出现严重的版本冲突问题,请尝试下载本书使用的版本,等你有了足够多的经验后,再切换到最新的版本。

  • 加载插件SBTEclipse

要获悉你必须安装该插件的哪个版本,请访问该插件的项目页面http://github.com/typesafehub/sbteclipse):

6.2 SBT - 图3

这个GitHub项目页面提供了如何在项目中添加这个插件的说明。你应查找以addSbtPlugin打头的行;我访问这个项目页面时,这行的内容如下:

  1. addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin"
  2. % "5.1.0")

在项目akka-quotes的子目录project中,新建一个名为plugins.sbt的文件,并将前述一行内容复制并粘贴到这个文件中,让SBT知道这个项目需要插件SBTEclipse。

6.2 SBT - 图4 务必将这个文件存储在子目录project中,否则SBT将找不到它。

  • 使用SBTEclipse生成新的Eclipse IDE项目

切换到目录akka-quotes,输入sbt并按回车以再次启动SBT交互式shell。

SBT将下载并激活插件SBTEclipse。从现在开始,在这个项目目录中启动SBT后,将能够创建或更新Eclipse IDE/Scala IDE项目。执行插件sbteclipse添加的如下命令:

  1. eclipse

插件sbteclipse将在这个SBT项目的目录中生成项目文件,而安装了Scala IDE插件的Eclipse IDE能够导入的这些文件。

  • 在Eclipse IDE中导入生成的项目

返回到Eclipse IDE(如果它没有运行,就启动它)。为导入SBTEclipse生成的项目,请执行如下步骤。

(1) 在屏幕左边的Package Explorer的空白区域右击鼠标,并选择Import…。

(2) 将出现Import对话框,让你选择一个导入向导。请选择General>Existing Projects into Workspace,并单击按钮Next。

(3) 单击Select root directory旁边的按钮Browse…,选择目录workspace下的子目录akka-quotes,并单击OK按钮。

(4) 导入向导对话框的项目列表中将包含项目akka-quotes。单击按钮Finish关闭这个对话框。

这个项目将出现在Package Explorer中。在Package Explorer中,选择文件src/main/Scala> example>Hello.scala,再按Ctrl + F11运行这个文件。你也可单击工具栏中的Run按钮或选择菜单Run>Run来运行这个文件。如果一切顺利,你将在Console选项卡中看到问候“hello”:

6.2 SBT - 图5

6.2.3 Scala编译器(scalac

构建项目时,SBT将替我们调用Scala编译器scalac,因此我们无需直接调用它。但你必须知道,SBT使用的是scalac,而不是前一章一直使用的命令scala

最重要的差别在于,Scala编译器像Java编译器javac一样,要求将代码封装在类中。命令scala通过在幕后创建一个不可见的类来满足这种要求,但编译器scalac不会这样做,因此在Scala IDE中编写由SBT构建的Scala代码时,必须定义类并将代码添加到其中。

为此,有两种办法:

  • 创建包含方法main()的单例对象;
  • 让单例对象扩展特质App

  • 创建包含方法main()的单例对象

这与Java很像。鉴于Scala没有与Java访问修饰符static等价的东西,因此必须使用单例对象。方法main()必须将String数组(Array[String])作为输入参数,且返回类型为Unit(类似于Java关键字void):

  1. object MainObject {
  2. def main(args: Array[String]): Unit = {
  3. println("Executable code here...")
  4. }
  5. }
  • 创建扩展特质App的单例对象

可不给单例对象添加方法main(),而使用Scala提供的特质AppApp特质的不同寻常之处在于,你不能重写其中的任何方法,也不能在实现了特质App的类中添加方法main()。相反,你只需在类中直接添加可执行的代码,就像它们是类的主构造函数的可执行代码一样:

  1. object MainObject extends App {
  2. println("Executable code here...")
  3. }

本章将采用这种方法。