2

搭建环境


2.1 Windows搭建(可选)25
2.2 安装Java25
2.3 安装Ammonite26
2.4 安装Mill29
2.5 IDE支持32

$ amm
Loading...
Welcome to the Ammonite Repl 2.2.0 (Scala 2.13.2 Java 11.0.7)
@ 1 + 1
res0: Int = 2

@ println("hello world" + "!" * 10)
hello world!!!!!!!!!!
</> 2.1.scala

Snippet 2.1: Ammonite Scala REPL初体验

本章,我们将搭建一个简单的Scala编程环境,让你能够编写、运行、测试你的Scala代码。我们将在本书剩余部分全程使用这个搭建好的环境。这个简单的搭建足以使你立刻具备Scala编程的生产力。

搭建开发环境是学习一门新语言的关键步骤。确保你在本章完成搭建。如果碰到问题可以去在线聊天室 https://www.handsonscala.com/chat 获取帮助,只有解决了问题,你才能心无旁骛继续本书剩余部分,而不必被工具相关的事情分心。

我们将安装下列编写和运行Scala代码的工具:

  • Java,一个Scala的底层运行时
  • Ammonite,一个轻量级REPL和脚本执行器
  • Mill,一个大型Scala项目的构建工具
  • IntelliJ IDEA,一个支持Scala的集成开发环境
  • VSCode,一个支持Scala的轻量级文本编辑器

这些工具满足你从编写第一行Scala代码到用Scala构建部署生产系统所需要的一切。

2.1 Windows搭建(可选)

对于Windows系统,使用Scala最简单的方式是通过 Windows Subsystem for Linux 2 (WSL2),它提供了类Unix环境运行你的代码。参照Microsoft网站文档完成安装:

WSL2允许你选择寄宿在Windows电脑上的Linux环境种类。本书选择Ubuntu 18.04 LTS。

完成搭建后,你将获得一个基于标准Linux文件系统的Ubuntu终端,而你的Windows文件系统放在目录 /mnt/c/ 下:

$ cd /mnt/c

$ ls
'Documents and Settings'    PerfLogs        'Program Files (x86)'   Recovery
'Program Files'             ProgramData     Recovery.txt            Users
...
</> 2.2.bash

/mnt/c/ 中的文件在你的Windows环境和Linux环境之间共享:

  • 你可以在Windows编辑代码,然后在Linux终端运行

  • 你可以在Linux生成磁盘文件,然后用Windows Explorer查看

本书许多章节假设你在WSL2的Ubuntu/Linux环境运行代码,而像IntelliJ或VSCode这样的图形编辑器运行在你的Windows环境,WSL2让你在Linux和Windows之间无缝切换。尽管Windows可以直接开发Scala应用,但是WSL2让你学习本书时避免兼容性问题,以及其他干扰项。

2.2 安装Java

Scala运行在 Java Virtual Machine (JVM),因此需要提前安装Java。打开你的命令行(Mac OS-X上是 Terminal 应用,Windows上是WSL2/Ubuntu),输入命令 java -version,如果你看到类似下列输出,则证明你已经安装了Java:

$ java -version
openjdk version "11.0.7" 2020-04-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.7+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.7+9, mixed mode)
</> 2.3.bash

如果你已经安装Java,可以直接 安装Ammonite (2.3)。否则,你将看到下列输出:

$ java -version
-bash: java: command not found
</> 2.4.bash

你可以通过下列网站下载并安装一个JVM版本(本书样例中使用版本11):

安装方法因操作系统有所不同,其提供了Windows,Mac OS-X的指导,Linux (.deb.rpm 安装包) 稍有不同。安装好后,回到你的命令行,确保执行命令 java -version 正确地产生如上输出。

如果你通过终端安装Java,比如WSL Ubuntu发行版或者无UI的服务器,你可以使用标准包管理器,比如在Ubuntu 18.04中可以使用如下命令:

$ sudo apt update

$ sudo apt install default-jdk

$ java -version
openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment (build 11.0.6+10-post-Ubuntu1ubuntu118.04.1)
OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1, ...)
</> 2.5.bash

Java版本具有高度兼容性,无论你安装何种版本,只要适用本书所有样例即可。

2.3 安装Ammonite

在Mac OS-X,Linux,或者Windows WSL2中,我们可以用下列命令安装Ammonite:

$ sudo curl -L https://github.com/lihaoyi/Ammonite/releases/download/2.2.0/2.13-2.2.0 \
  -o /usr/local/bin/amm

$ sudo chmod +x /usr/local/bin/amm

$ amm
</> 2.6.bash

这将打开Ammonite Scala REPL:

Loading...
Welcome to the Ammonite Repl 2.2.0 (Scala 2.13.2 Java 11.0.7)
@
</> 2.7.scala

当你看到这样的输出表明Ammonite已经准备好了,你可以使用 Ctrl-D 退出Ammonite。

Max OS-X中,Ammonite也可以使用Homebrew包管理器获取,输入命令 brew install ammonite-repl 即可。

2.3.1 Scala REPL

Ammonite REPL是一个交互式的Scala终端,你可以输入代码表达式,打印运行结果:

@ 1 + 1
res0: Int = 2

@ "i am cow".substring(2, 4)
res1: String = "am"
</> 2.8.scala

代码出错时会打印错误信息:

@ "i am cow".substing(2, 3)
cmd0.sc:1: value substing is not a member of String
did you mean substring?
val res0 = "i am cow".substing(2, 3)
                      ^
Compilation Failed
</> 2.9.scala

你可以在 . 之后敲击tab键进行补全,显示某个对象的所有可用方法,或者在输入部分方法名后敲击tab键过滤出可用列表,或者输入完整方法名后再敲击tab键显示方法签名:

@ "i am cow".<tab>
...
exists                   maxOption                stripSuffix              ||
filter                   min                      stripTrailing
filterNot                minBy                    subSequence
find                     minByOption              substring

@ "i am cow".sub<tab>
subSequence   substring

@ "i am cow".substring<tab>
def substring(x$1: Int): String
def substring(x$1: Int, x$2: Int): String
</> 2.10.scala

如果某个REPL命令执行时间过久,你可以使用 Ctrl-C 停止它:

@ while (true) { Thread.sleep(1000); println(1 + 1) } // loop forever
2
2
2
2
2
<Ctrl-C>
Interrupted! (`repl.lastException.printStackTrace` for details)

@
</> 2.11.scala

2.3.2 Scala脚本

Ammonite除了提供REPL,也可以运行Scala脚本文件。Scala脚本是名称以 .sc 结尾,包含Scala代码的文件。Scala脚本尽管配置化较弱,但是比起像mill这样的全功能构建工具,它以更轻量的方式运行Scala代码。

比如,我们可以使用任何文本编辑器(Vim, Sublime Text, VSCode等等)创建下述文件 myScript.sc

myScript.scprintln(1 + 1) // 2

println("hello" + " " + "world") // hello world

println(List("I", "am", "cow")) // List(I,am,cow)</> 2.12.scala

注意在脚本中,表达式不会输出它们的运行结果,你需要使用 println 打印它们。编辑完成后,你可以使用 amm myScript.sc 运行脚本:

$ amm myScript.sc
Compiling /Users/lihaoyi/myScript.sc
2
hello world
List(I, am, cow)
</> 2.13.bash

第一次运行脚本时,会花一些时间编译脚本,得到可执行文件。脚本编译之后,后续再运行会快很多。

2.3.2.1 监控脚本

如果你在单一脚本上工作,你可以使用命令 amm -w 或者 amm --watch 监控脚本,当脚本有变动时自动重新运行:

$ amm -w myScript.sc
2
hello world
am
Watching for changes to 2 files... (Ctrl-C to exit)
</> 2.14.bash

现在当你修改脚本后,它会自动重新编译并运行脚本。这比手动运行快多了,当你频繁修补脚本以让其正确运行时是非常方便的。

你可以用舒适的编辑器编辑Scala脚本:IntelliJ (2.5.1),VSCode (2.5.3),或者任何文本编辑器。

2.3.3 在REPL中使用脚本

运行 amm 时可以带上 --predef选项,在REPL中使用Scala脚本里的函数。比如下列脚本:

myScript.scdef hello(n: Int) = {
  "hello world" + "!" * n
}</> 2.15.scala

你可以在REPL中这样使用它:

$ amm --predef myScript.sc
Loading...
Welcome to the Ammonite Repl 2.2.0 (Scala 2.13.2 Java 11.0.7)
@ hello(12)
res0: String = "hello world!!!!!!!!!!!!"
</> 2.16.bash

当你的代码片段很大需要保存到文件并在编辑器中编辑时,这有助于你同时借助Scala REPL来交互式地测试代码。

当你修改脚本文件后,你需要使用 Ctrl-D 退出REPL,重新打开它使得修改后的脚本生效。你也可以结合选项 --watch/-w,这样当你退出后,如果脚本文件有变动,REPL会自动重启。

2.4 安装Mill

Mill是Scala项目的一种构建工具,它是为更大型Scala项目而设计的。尽管Ammonite REPL和脚本对小型代码是非常适用的,但它们不支持运行单测、打包代码、部署、以及其它此类任务。在更大规模的项目中,你需要像Mill这样的构建工具完成这类事情。

2.4.1 Mill项目

上手Mill最简单的方式是下载示例项目:

$ curl -L https://github.com/lihaoyi/mill/releases/download/0.8.0/0.8.0-example-3.zip \
  -o example-3.zip

$ unzip example-3.zip

$ cd example-3

$ find . -type f
./build.sc
./mill
./foo/test/src/ExampleTests.scala
./foo/src/Example.scala
</> 2.17.bash

示例项目有四个文件。build.sc 文件包含项目定义,定义了主模块 foo,其中带一个测试模块 test

build.scimport mill._, scalalib._

object foo extends ScalaModule {
  def scalaVersion = "2.13.8"
  object test extends Tests {
    def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.7.4")
    def testFrameworks = Seq("utest.runner.Framework")
  }
}</> 2.18.scala

上述定义的 test 模块依赖一个第三方库:ivy"com.lihaoyi::utest:0.7.4"。阅读本书时,我们还会看到其它依赖库,以及它们是如何在Mill项目中使用的。

foo 模块的Scala代码放在 foo/src/ 目录下:

foo/src/Example.scalapackage foo
object Example {
  def main(args: Array[String]): Unit = {
    println(hello())
  }
  def hello(): String = "Hello World"
}</> 2.19.scala

foo.test 测试模块的Scala代码放在 foo/test/src/ 目录下:

foo/test/src/ExampleTests.scalapackage foo
import utest._
object ExampleTests extends TestSuite {
  def tests = Tests {
    test("hello") {
      val result = Example.hello()
      assert(result == "Hello World")
      result
    }
  }
}</> 2.20.scala

最后,示例项目包含一个 mill 文件。你可以使用命令 ./mill ... 编译并运行项目。

$ ./mill foo.compile
Compiling /Users/lihaoyi/test2/example-1/build.sc
...
7 warnings found
[info] Compiling 1 Scala source to /Users/lihaoyi/test2/example-1/out/foo/compile/dest...
[info] Done compiling.

$ ./mill foo.run
Hello World
</> 2.21.bash

第一次使用 ./mill 时会花费一些时间为你下载正确的Mill版本。尽管我们上面分别执行了 ./mill foo.compile 以及 ./mill foo.run,但如果你想直接运行代码,你可以仅执行 ./mill foo.run。如果有必要的话,Mill在运行前会自动重新编译你的代码。

在任何其它项目中,甚至全新项目中,你只需要把 mill 脚本文件复制到项目根路径来使用Mill。你也可以通过如下方法下载启动脚本:

$ curl -L https://github.com/lihaoyi/mill/releases/download/0.8.0/0.8.0 -o mill

$ chmod +x mill
</> 2.22.bash

2.4.2 运行单测

你可以执行 ./mill foo.test 在Mill中进行测试:

$ ./mill foo.test
-------------------------------- Running Tests --------------------------------
+ foo.ExampleTests.hello 10ms  Hello World
</> 2.23.bash

这里展示了示例项目测试用例运行成功的结果。

2.4.3 创建独立可执行jar包

截至目前,我们一直都是用Mill构建工具运行代码。但是如果我们想脱离Mill,准备代码并部署到生产系统,我们可以执行 ./mill foo.assembly

$ ./mill foo.assembly

这会创建一个可被分发、部署、脱离Mill独立运行的 out.jar 文件。默认情况下,Mill为 foo.assembly 任务创建的输出文件放在 out/foo/assembly/dest (因Mill版本不同,可能放在out/foo/assembly.dest),你可以使用 ./mill show 打印完整路径:

$ ./mill show foo.assembly
"ref:18e58778:/Users/lihaoyi/test/example-3/out/foo/assembly/dest/out.jar"
</> 2.24.scala

你可以运行这个可执行的jar包,验证其是否按预期工作:

$ out/foo/assembly/dest/out.jar
Hello World
</> 2.25.bash

现在你的代码已经准备好被部署了!

通常来讲,Mill项目运行Scala代码比在Ammonite Scala REPL或者Scala脚本中交互式地运行代码繁琐一些,但是对于任何生产系统,具备轻松测试与代码打包的能力至关重要。

2.5 IDE支持

最常用的Scala程序编辑器是IntelliJ以及VSCode。本节介绍两者的安装方法,但在本书的后续学习中,你只需要根据自己偏好安装任意一种。

2.5.1 为IntelliJ安装Scala支持

你可以通过下列网站安装IntelliJ,免费的社区版本就够了。

接下来,我们在加载界面安装IntelliJ Scala插件:

Intellij Splash

或者通过菜单栏安装

Intellij Menu

进入 Plugins 页面:

Intellij Plugin

搜索 Scala,点击 Install。然后重启编辑器即可。

2.5.2 IntelliJ集成Mill

在电脑上安装好IntelliJ之后,运行下述终端命令来生成IntelliJ可识别的IDEA项目文件:

$ ./mill mill.scalalib.GenIdea/idea

接下来,使用IntelliJ File / Open 菜单项,选择 build.sc 所在目录。这便在IntelliJ打开了Mill项目,编辑代码时IntelliJ提供了编码帮助:

Intellij Indexed

如果你看到提示 Project JDK is not defined: Setup JDK:点击 Setup JDK 链接并选择 前述安装 (2.2) 的Java版本。注意每当你在 build.sc 添加依赖或者新增模块后,你需要重新执行命令 ./mill mill.scalalib.GenIdea/idea,然后重启IntelliJ以便让变动生效。

2.5.3 Visual Studio Code支持

Visual Studio Code文本编辑器通过插件Metals支持Scala:

images/vscode-metals-install.png

使用VSCode开发Mill项目,我们需要在 .mill-version 文件中指明Mill版本。然后你可以通过 Import build 打开Mill项目所在目录:

$ echo "0.8.0" > .mill-version

images/vscode-metals-import.png

你的电脑太卡或者负载过高时,可能出现导入超时,这时候需要重试几次。如果重试多次导入也不成功,你可以尝试更新Metals版本。点击 F1,搜索 Open Settings (UI),然后依次选择Extensions > Metals > Server Version,使用最新的SNAPSHOT版本:

你也可以尝试启用 VSCode Remote Development,点击 F1,搜索 Remote-WSL: Reopen in WSL。它会在Ubuntu虚拟机里运行VSCode分析逻辑,这比直接在Windows主机环境运行更可靠:

完成之后,将光标悬浮在方法上面时,点击 Cmd 或者 Ctrl,将出现方法签名。

images/vscode-metals-popup.png

Metals还支持Vim,Sublime Text,Atom,以及其他编辑器。你可以参考它们的文档获取更多详情,安装相关编辑器插件:

2.6 总结

截至目前,你已经完成三件事:

  • 你可以运行 ammamm myScript.sc 来启动Ammonite Scala REPL以及运行脚本
  • Mill示例项目中执行 ./mill foo.run 运行代码,或者 ./mill foo.test 完成测试
  • 使用IntelliJ或者VSCode支持Mill项目开发

本书把Mill作为主要构建工具,因为这是最容易的上手方式。你可能在实际开发中碰到其他备选的构建工具:

这些工具的使用将贯穿全书,在你阅读后续章节之前,花些时间熟悉这些工具:在Ammonite REPL中编写代码,创建一些脚本,添加代码和测试用例到Mill示例项目,然后运行它们。

Discuss Chapter 2 online at https://www.handsonscala.com/discuss/2