寒假学习14-sbt

Posted on 2022-01-14 19:32  ***Pepsi***  阅读(76)  评论(0编辑  收藏  举报

关于SBT:

特性:

  • 简单项目零配置。
  • 用Scala源码管理项目构建。
  • 精确的重编译,节省时间。
  • 使用Coursier的库管理器。
  • 支持Scala和Java的混合项目。
  • 等等等,具体就不列了,总之一个大型项目构建系统该有的东西。

安装:

  • sbt依赖Java,确保已经安装了JDK1.8或以上版本。
  • 下载压缩包或者安装包,这里的版本是1.5.5。
  • 解压或者安装。
  • 配置环境变量SBT_HOME,并添加%SBT_HOME%\bin到path环境变量,安装包的话会自动配置。

通过案例入门sbt

创建一个项目hello作为例子:

  • windows上没有的命令按照含义操作即可。
$ mkdir foo-build
$ cd foo-build
$ touch build.sbt

开始sbt shell:

$ sbt
[info] Updated file /tmp/foo-build/project/build.properties: set sbt.version to 1.1.4
[info] Loading project definition from /tmp/foo-build/project
[info] Loading settings from build.sbt ...
[info] Set current project to foo-build (in build file:/tmp/foo-build/)
[info] sbt server started at local:///Users/eed3si9n/.sbt/1.0/server/abc4fb6c89985a00fd95/sock
sbt:foo-build>
  • 第一次初始化时间会很长。
  • 退出shell:
sbt:foo-build> exit
  • 编译:
sbt:foo-build> compile
  • sbt shell中Tab可以补全。

对修改重新编译:

  • compile命令(或其他命令同理)前加一个~前缀,会进入等待状态,当项目发生修改是会自动重新编译。当然退出这个状态后就不会了。
sbt:foo-build> ~compile
[success] Total time: 0 s, completed May 6, 2018 3:52:08 PM
1. Waiting for source changes... (press enter to interrupt)

创建源文件:

  • 执行~compile并保持,创建目录src/main/scala/example新建源文件保存就能看到编译过程了。
// src/main/scala/example/Hello.scala
package example

object Hello extends App {
  println("Hello")
}

sbt shell常用操作:

  • help帮助。
  • help run具体条目的帮助。
  • run运行程序。
  • 上下箭头切换已执行命令。
  • scalaVersion scala版本。

配置修改:

  • 切换当前项目的scala版本:set ThisBuild / scalaVersion := "2.13.6"
  • session save保存配置到build.sbt,此时其中就会多出ThisBuild / scalaVersion := "2.13.6"
  • 编辑build.sbt
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello"
  )
  • 重新加载配置reload

测试:

  • 添加ScalaTest到依赖
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.7" % Test,
  )
  • 执行测试:test
  • 后续继续运行追加的测试:~testQuick

编写测试:src/test/scala/HelloSpec.scala

// src/test/scala/HelloSpec.scala
import org.scalatest.funsuite._

class HelloSpec extends AnyFunSuite {
  test("Hello should start with H") {
    assert("hello".startsWith("H"))
  }
}
  • 测试结果当然是失败
sbt:Hello> test
[info] HelloSpec:
[info] - Hello should start with H *** FAILED ***
[info]   "hello" did not start with "H" (HelloSpec.scala:5)
[info] Run completed in 214 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***
[error] Failed tests:
[error]         HelloSpec
[error] (Test / test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 0 s, completed 2021年9月27日 下午11:58:01
  • 改一下源码再测试就能通过了:
// src/test/scala/HelloSpec.scala
import org.scalatest.funsuite._

class HelloSpec extends AnyFunSuite {
  test("Hello should start with H") {
    assert("Hello".startsWith("H"))
  }
}

添加库依赖:

// build.sbt
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.2",
    libraryDependencies += "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0",
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.7" % Test,
  )

使用REPL(Read-Eval-Print Loop):

sbt:Hello> console
  • 在scala的REPL环境中粘贴::paste
  • 退出::q

修改build.sbt创建一个子项目:

ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0",
    libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.7" % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
  )
  • reload时会自动创建目录core/
  • 列出所有子项目:projects
  • 编译子项目:
helloCore/compile
  • 子项目添加依赖:
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0",
    libraryDependencies += scalaTest % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
    libraryDependencies += scalaTest % Test,
  )

广播命令、添加依赖:

  • 设置.aggregate(...),这样发送到hello的命令都会被广播到helloCore
  • 使用.dependsOn(...)可以设置依赖,下面的设置使hello依赖于helloCore
  • 将Gigahorse的依赖移到helloCore
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"
val gigahorse = "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0"

lazy val hello = (project in file("."))
  .aggregate(helloCore)
  .dependsOn(helloCore)
  .settings(
    name := "Hello",
    libraryDependencies += scalaTest % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
    libraryDependencies += scalaTest % Test,
    libraryDependencies += gigahorse,
  )

使用Play JSON解析JSON:

  • 添加依赖。
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"
val gigahorse = "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0"
val playJson  = "com.typesafe.play" %% "play-json" % "2.9.2"

lazy val hello = (project in file("."))
  .aggregate(helloCore)
  .dependsOn(helloCore)
  .settings(
    name := "Hello",
    libraryDependencies += scalaTest % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
    libraryDependencies ++= Seq(gigahorse, playJson),
    libraryDependencies += scalaTest % Test,
  )
  • 重载,添加文件:core/src/main/scala/example/core/Weather.scala
// core/src/main/scala/example/core/Weather.scala
package example.core

import gigahorse._, support.okhttp.Gigahorse
import scala.concurrent._, duration._
import play.api.libs.json._

object Weather {
  lazy val http = Gigahorse.http(Gigahorse.config)

  def weather: Future[String] = {
    val baseUrl = "https://www.metaweather.com/api/location"
    val locUrl = baseUrl + "/search/"
    val weatherUrl = baseUrl + "/%s/"
    val rLoc = Gigahorse.url(locUrl).get.
      addQueryString("query" -> "New York")
    import ExecutionContext.Implicits.global
    for {
      loc <- http.run(rLoc, parse)
      woeid = (loc \ 0  \ "woeid").get
      rWeather = Gigahorse.url(weatherUrl format woeid).get
      weather <- http.run(rWeather, parse)
    } yield (weather \\ "weather_state_name")(0).as[String].toLowerCase
  }

  private def parse = Gigahorse.asString andThen Json.parse
}
  • 修改src/main/scala/example/Hello.scala:
package example

import scala.concurrent._, duration._
import core.Weather

object Hello extends App {
  val w = Await.result(Weather.weather, 10.seconds)
  println(s"Hello! The weather in New York is $w.")
  Weather.http.close()
}
  • 运行:run
sbt:Hello> run
[info] running example.Hello
Hello! The weather in New York is light cloud.
[success] Total time: 5 s, completed 2021年9月28日 上午10:29:57

添加sbt-native-packager插件:

  • 创建project/plugins.sbt
  • addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.4")
    • 修改build.sbtHello项目添加.enablePlugins(JavaAppPackaging)
    • 重载,本地没有执行成功,所以下面的dist命令也就不能用。

    重载并创建.zip分发包:dist

  • sbt:Hello> dist
    [info] Wrote /tmp/foo-build/target/scala-2.12/hello_2.12-0.1.0-SNAPSHOT.pom
    [info] Wrote /tmp/foo-build/core/target/scala-2.12/hello-core_2.12-0.1.0-SNAPSHOT.pom
    [info] Your package is ready in /tmp/foo-build/target/universal/hello-0.1.0-SNAPSHOT.zip

    应用容器化:

    • Docker/publishLocal
    • 运行容器化后的应用:docker run hello:0.1.0-SNAPSHOT

    设置应用版本:

// build.sbt
ThisBuild / version      := "0.1.0"
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"
val gigahorse = "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0"
val playJson  = "com.typesafe.play" %% "play-json" % "2.9.2"

lazy val hello = (project in file("."))
  .aggregate(helloCore)
  .dependsOn(helloCore)
  .enablePlugins(JavaAppPackaging)
  .settings(
    name := "Hello",
    libraryDependencies += scalaTest % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
    libraryDependencies ++= Seq(gigahorse, playJson),
    libraryDependencies += scalaTest % Test,
  )

临时切换Scala版本:

  • ++2.12.14

在Bash中直接运行sbt的命令:

sbt clean "testOnly HelloSpec"
  • 这样程序运行起来会慢一些。
  • 连续的开发的话,推荐使用sbt shell或者连续测试比如~testQuick

new命令:

$ sbt new scala/scala-seed.g8
....
A minimal Scala project.

name [My Something Project]: hello

Template applied in ./hello
  • 会创建一个简单的项目,要求输入项目名时输入hello,会在hello/下创建一个新项目。

sbt使用

项目的目录结构:

  • base directory是包含项目的目录,这里称为项目根目录。
  • sbt使用和Maven一样的源码结构,源文件路径都是基于项目根目录的相对路径。
src/
  main/
    resources/
       <files to include in main jar here>
    scala/
       <main Scala sources>
    scala-2.12/
       <main Scala 2.12 specific sources>
    java/
       <main Java sources>
  test/
    resources
       <files to include in test jar here>
    scala/
       <test Scala sources>
    scala-2.12/
       <test Scala 2.12 specific sources>
    java/
       <test Java sources>
  • 其他src/中的目录会被忽略,所有隐藏目录都会被忽略。
  • 源码可以被放在根目录的hello/app.scala,对小项目是可行的。然而一般来说,人们倾向于将项目放在src/main/下面来保证事情能够有条理地进行。如果你自行管理定制了项目的构建的话,自定义源码的位置也是可行的。
  • sbt的构建定义文件:build.sbt
  • 除此之外,project目录中的.scala文件可以定义项目帮助文件和一次性的插件。
build.sbt
project/
  Dependencies.scala
  • 生成文件:.class,生成的jar,其他文件和文档等会被默认输出到target目录。
  • 一般生成文件应该要排除在版本控制之外,在.gitignore中添加:
target/

运行:

  • 运行sbt shell:sbt无参数运行,进入sbt的提示符,有tab补全和执行历史。
  • 编译:compile
  • 运行:run
  • 不进入sbt shell直接运行sbt命令:用""包起来表示是一个命令,相对来说会慢一些。
sbt clean compile "testOnly TestA TestB"
  • 会一次执行clean compile testOnlyTestA TestB是传给testOnly的参数。
  • 保存文件时自动重编译运行测试:~testQuick
  • 命令加上~后会进入循环模式,保存文件都会自动运行。回车退出。
  • 常用命令:
CommandDescription
clean Deletes all generated files (in the target directory).
compile Compiles the main sources (in src/main/scala and src/main/java directories).
test Compiles and runs all tests.
console Starts the Scala interpreter with a classpath including the compiled sources and all dependencies. To return to sbt, type :quit, Ctrl+D (Unix), or Ctrl+Z (Windows).
run argument* Runs the main class for the project in the same virtual machine as sbt.
package Creates a jar file containing the files in src/main/resources and the classes compiled from src/main/scala and src/main/java.
help command Displays detailed help for the specified command. If no command is provided, displays brief descriptions of all commands.
reload Reloads the build definition (build.sbt, project/.scala, project/.sbt files). Needed if you change the build definition.

Copyright © 2024 ***Pepsi***
Powered by .NET 8.0 on Kubernetes