Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sbt入门与进阶 #8

Open
cjuexuan opened this issue Jan 24, 2016 · 0 comments
Open

sbt入门与进阶 #8

cjuexuan opened this issue Jan 24, 2016 · 0 comments
Labels

Comments

@cjuexuan
Copy link
Member

sbt入门

什么是sbt

sbt是一个simple build tools,可以进行scala与java的项目管理,支持增量编译,内置scala console

如何开始

安装配置初始化

先安装,安装方式这里就不写了,国内墙比较高,如果没有梯子的用户,请最好将源改成oschina的
具体做法是:

  • 在用户家目录下的.sbt文件夹下定义一个名为repositories的配置文件
  • 在文件中加入以下内容
[repositories]
local
osc: http://maven.oschina.net/content/groups/public/
typesafe: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly
sonatype-oss-releases
maven-central
sonatype-oss-snapshots

创建项目

接着创建工程目录,或者选用ide,我通常使用idea,会自动生成工程目录夹以及原始的配置,如果非ide则需要手工在工程目录下创建build.sbt文件,手工创建目录,结构类似srctree
其中main主要放工程代码,test放测试代码,然后运行sbt,命令为sbt进入sbt中

  • 可以查看scalaSource来查看scala源码要求的位置,
  • java的也是对应的javaSource
  • 其中里面的配置文件放在对应的resources中,可运行show unmanagedResources查看,
  • 运行console进入scala的Repl中

加入依赖

依赖加入也比较简单
可以一条条的加就像这样

name := "learnsbt"

version := "1.0"

scalaVersion := "2.11.7"

//                                      组织                    库                                   版本
libraryDependencies += "org.scala-js" % "scalajs-library_2.11" % "0.6.6"

具体搜索我是通过mvnrepository.com

当然还有一起加的方法:

name := "HbaseTest"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies ++= Seq(
  "org.apache.hadoop" % "hadoop-common" % "2.6.1" ,
  "org.apache.hbase" % "hbase-client" % "1.1.2",
  "org.apache.hbase" % "hbase-common" % "1.1.2" ,
  "ch.qos.logback" % "logback-classic" % "1.1.3",
  "com.typesafe.scala-logging" % "scala-logging_2.11" % "3.1.0"
)

到这里就可以基本使用了
记得修改配置后需要reload或者update

写的类运行

执行run ,如果有多个类就会出现一个列表,输入对应数字选择即可。

写的测试运行

执行test,方法同上run
这里就基本实现了小白级别的扫盲了

进阶

第一说操作符

:= 是给key分配一个初始表达式,覆盖原来的任何配置
+=是追加一个值到key
++=是追加一个队列,队列中放值,将值追加到key

再说task,里面可以定义task,task可以是执行外部的命令,先简单一点

name := "learnsbt"

version := "1.0"

scalaVersion := "2.11.7"

//定义了task的类型为Unit,key值叫helloTask
val helloTask = taskKey[Unit]("say hello")

helloTask := {
  println("Hello")
}

运行结果

>reload
[info] Loading project definition from /home/ctao/WorkSpace/scala/learnsbt/project
[info] Set current project to learnsbt (in build file:/home/ctao/WorkSpace/scala/learnsbt/)
> helloTask
Hello
[success] Total time: 0 s, completed Jan 24, 2016 5:28:15 PM

当然你也可以定义taskKey的返回类型是String,例如:

//定义了task的类型为String,key值叫helloTask
val helloTask = taskKey[String]("say hello")

helloTask  := "hello"

运行结果是

> reload
[info] Loading project definition from /home/ctao/WorkSpace/scala/learnsbt/project
[info] Set current project to learnsbt (in build file:/home/ctao/WorkSpace/scala/learnsbt/)
> helloTask
[success] Total time: 0 s, completed Jan 24, 2016 5:35:18 PM

奇怪,结果呢,要看结果就执行 show taskKey

> show helloTask
[info] hello
[success] Total time: 0 s, completed Jan 24, 2016 5:36:29 PM

下面我们定义多个task,task之间是_并行执行的_ ,这点很关键,看下面的例子
先定义三个task,第一个是hello,第二个是hi,第三个是bye

val helloTask = taskKey[String]("say hello")

val hiTask = taskKey[String]("say hi")

val byeTask = taskKey[String]("say bye")

首先,我们让三个task内部执行只是休眠2s,如果是并行那么结果会是2s,如果串行那么就是6s

val helloTask = taskKey[String]("say hello")

val hiTask = taskKey[String]("say hi")

val byeTask = taskKey[String]("say bye")

val runAllTask = taskKey[Unit]("run all")

runAllTask := {
  println(helloTask.value)
  println(hiTask.value)
  println(byeTask.value)

}

helloTask := {
  val hello = "hello"
  println(s"hello task run : $hello")
  Thread sleep 2000
  hello
}


hiTask := {
  val hi = "hi"
  println(s"hi task run : $hi")
  Thread sleep 2000
  hi
}

> runAllTask
hi task run : hi
bye task run : bye
hello task run : hello
hello
hi
bye
[success] Total time: 2 s, completed Jan 24, 2016 5:46:12 PM

结果是2s,验证了我们的说法,下面我们bye要依赖hellotask执行,
具体代码为

val helloTask = taskKey[String]("say hello")

val hiTask = taskKey[String]("say hi")

val byeTask = taskKey[String]("say bye")

val runAllTask = taskKey[Unit]("run all")

runAllTask := {
  println(helloTask.value)
  println(hiTask.value)
  println(byeTask.value)

}

helloTask := {
  val hello = "hello"
  println(s"hello task run : $hello")
  Thread sleep 2000
  hello
}


hiTask := {
  val hi = "hi"
  println(s"hi task run : $hi")
  Thread sleep 2000
  hi
}


byeTask := {
  val hi = hiTask.value
  val bye = "bye"
  println(s"hi task value is $hi")
  println(s"bye task run : $bye")
  Thread sleep 2000
  bye
}

结果为:


> runAllTask
hi task run : hi
hello task run : hello
hi task value is hi
bye task run : bye
hello
hi
bye
[success] Total time: 4 s, completed Jan 24, 2016 5:49:06 PM

由此可看出在没有依赖关系的时候是并行,但有依赖关系就是串行,如果我们再设置hi依赖hello,那么就是6s
__
注意:如果互相依赖可能造成死锁,比如a依赖b的value,b里面又依赖a的value,那么就是鸡生蛋和蛋生鸡的问题了,要避免这一点


你会说这么简单的东西有个什么用,当然有用了, 比如我们要创建一个版本信息,值来源于git中的headcommit中的sha,那么我们就可以

val gitHeadCommitSha = taskKey[String]("determine the current git commit SHA")
gitHeadCommitSha := Process("git rev-parse HEAD").lines.head

执行show gitHeadCommitSha

> show gitHeadCommitSha
[info] 961e628ee67985f75d40833143c7e60e0b70ad03
[success] Total time: 0 s, completed Jan 24, 2016 5:57:16 PM

就获取到git中的状态信息了,下面我们将创建一个写版本的task,如下

val gitHeadCommitSha = taskKey[String]("determine the current git commit SHA")

val makeVersionProperties = taskKey[Seq[File]]("Makes a version.properties file.")

gitHeadCommitSha := Process("git rev-parse HEAD").lines.head

makeVersionProperties := {
  val propFile = (resourceManaged in Compile).value / "version.properties"
  val content = s"version=${gitHeadCommitSha.value}"
  IO.write(propFile, content)
  Seq(propFile)
}

我们运行task,将看到


> show makeVersionProperties
[info] List(/home/ctao/WorkSpace/scala/learnsbt/target/scala-2.11/resource_managed/main/version.properties)
[success] Total time: 0 s, completed Jan 24, 2016 6:00:50 PM

查看文件version.properties

version=961e628ee67985f75d40833143c7e60e0b70ad03

剩下的就自己diy了

多工程

有人说我不止一个module,那么应该怎么搞,就像这样

name := "learnsbt"

version := "1.0"

scalaVersion := "2.11.7"

lazy val sbtlearn1 = Project("learnsbt1", file("learnsbt1")).
  settings(libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.3")
lazy val sbtlearn2 = Project("learnsbt2", file("learnsbt2")).
  settings(fork := true)
lazy val sbtlearn3 = Project("learnsbt3", file("learnsbt3")).
  dependsOn(sbtlearn1)

idea中sbt会生成对应的dir,执行 tree -L 3

├── build.sbt
├── learnsbt1
│   ├── project
│   ├── src
│   │   ├── main
│   │   └── test
│   └── target
│       ├── resolution-cache
│       ├── scala-2.10
│       └── streams
├── learnsbt2
│   ├── project
│   ├── src
│   │   ├── main
│   │   └── test
│   └── target
│       ├── resolution-cache
│       ├── scala-2.10
│       └── streams
├── learnsbt3
│   ├── project
│   ├── src
│   │   ├── main
│   │   └── test
│   └── target
│       ├── resolution-cache
│       ├── scala-2.10
│       └── streams

我们查看build的jar包,会发现2中没有,而1有logback,3则由于依赖1则有logback和1,因此后面我们只需要定义一个通用,再定义一些依赖就好了:
buildjar

排除jar包

有的时候我们需要排除jar包,类似maven中的
那么我们可以这样做:

libraryDependencies ++= Seq(
  "org.apache.hadoop" % "hadoop-common" % "2.6.1" excludeAll ExclusionRule(name = "slf4j-log4j12"),
  "org.apache.hbase" % "hbase-client" % "1.1.2" excludeAll ExclusionRule(name = "slf4j-log4j12"),
  "org.apache.hbase" % "hbase-common" % "1.1.2" excludeAll ExclusionRule(name = "slf4j-log4j12"),
  "ch.qos.logback" % "logback-classic" % "1.1.3",
  "com.typesafe.scala-logging" % "scala-logging_2.11" % "3.1.0"
)

由于slf4j有logback和log4j两个实现,我们需要把log4j排除去掉

屏蔽源码:

有的时候我们要过滤源码,那么就是

lazy val ctaokafka = preownedKittenProject("ctaokafka").
  dependsOn(common).
  settings(libraryDependencies ++= Seq(Library.reactiveKafka
  )).settings(CommonSettings.projectSettings)
//  .settings(
//    excludeFilter in(Compile, unmanagedSources) := "*.scala"
//  )
//  .settings(
//    includeFilter := NothingFilter
//  )

被注释掉的地方可以将source屏蔽引入,两个是实现的同一个功能

最后给出一个我现在喜欢用的配置

首先project定义CommonSettings

import sbt.Keys._
import sbt._
import sbt.plugins.JvmPlugin

object CommonSettings extends AutoPlugin {

  override def requires: JvmPlugin.type = plugins.JvmPlugin


  override def trigger: PluginTrigger = allRequirements


  override def projectSettings: Seq[Def.Setting[Boolean]] = Seq(
    parallelExecution in Test := false,
    //    resolvers ++= Dependencies.resolvers,
    fork in Test := true,
    fork in run := true
  )


}

和一个Dependencies

import sbt._
import sbt.Keys._


object Dependencies {

  val core = Seq(
    Library.`scala-async`,
    Library.logback,
    Library.slf4j,
    Library.`play-json`,
    Library.scalatest
  )


  //  val resolvers = DefaultOptions.resolvers(snapshot = true) ++ Seq(
  //    "scalaz-releases" at "http://dl.bintray.com/scalaz/releases" // play-test -> specs2 -> scalaz-stream
  //  )
}


object Version {

  val akkaStream = "2.0.1"

  val reactiveKafka = "0.8.2"

  val scala = "2.11.7"

  val `play-json` = "2.4.6"

  val jline = "0.9.94"

  val logback = "1.1.3"

  val `akka-slf4j` = "2.3.12"

  val slf4j = "1.7.12"

  val `scala-async` = "0.9.5"

  val scalatest = "3.0.0-M14"
}


object Library {
  val akkaStream = "com.typesafe.akka" % "akka-stream-experimental_2.11" % Version.akkaStream

  val reactiveKafka = "com.softwaremill.reactivekafka" %% "reactive-kafka-core" % Version.reactiveKafka

  val `play-json` = "com.typesafe.play" % "play-json_2.11" % Version.`play-json`

  val jline = "jline" % "jline" % Version.jline

  val logback = "ch.qos.logback" % "logback-classic" % Version.logback

  val `akka-slf4j` = "com.typesafe.akka" %% "akka-slf4j" % Version.`akka-slf4j`

  val slf4j = "org.slf4j" % "log4j-over-slf4j" % Version.slf4j

  val `scala-async` = "org.scala-lang.modules" % "scala-async_2.11" % Version.`scala-async`

  val scalatest =  "org.scalatest" % "scalatest_2.11" % Version.scalatest

}

然后build.sbt:

name := "CtaoReactive"

scalaVersion := Version.scala

val gitHeadCommitSha = taskKey[String]("determine the current git commit SHA")

val makeVersionProperties = taskKey[Seq[File]]("Makes a version.properties file.")

inThisBuild(gitHeadCommitSha := Process("git rev-parse HEAD").lines.head)
lazy val common = preownedKittenProject("common").
  settings(
    makeVersionProperties := {
      val propFile = (resourceManaged in Compile).value / "version.properties"
      val content = s"version=${gitHeadCommitSha.value}"
      IO.write(propFile, content)
      Seq(propFile)
    },
    libraryDependencies ++= Seq(Library.akkaStream, Library.`akka-slf4j`)
  ).settings(CommonSettings.projectSettings)

lazy val ctaokafka = preownedKittenProject("ctaokafka").
  dependsOn(common).
  settings(libraryDependencies ++= Seq(Library.reactiveKafka
  )).settings(CommonSettings.projectSettings)
//  .settings(
//    excludeFilter in(Compile, unmanagedSources) := "*.scala"
//  )
//  .settings(
//    includeFilter := NothingFilter
//  )

/**
  * 创建通用模板
  *
  * @param name 工程名
  * @return
  */
def preownedKittenProject(name: String): Project = {
  Project(name, file(name)).
    settings(
      version := "1.0",
      organization := "ctao",
      libraryDependencies ++= Dependencies.core,
      scalaVersion := Version.scala
    )
}

@cjuexuan cjuexuan added the sbt label Jan 25, 2016
@cjuexuan cjuexuan changed the title sbt sbt入门与进阶 Jan 25, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant