美文网首页
sbt闲言碎语

sbt闲言碎语

作者: Queen〇fLaponia | 来源:发表于2018-04-30 20:10 被阅读0次

      参考ieda官方指导官方sbt参考手册(翻译烂)JamesRoper,虽然idea做的狠好,但是为了共享项目标准化构建、持续集成发布Docker镜像、自动依赖管理推荐以sbt构建定义脚本做项目构建定义,比如我们可以使用idea绑定sbt,但实际使用的sbt版本在build.properties中可以实现统一,这样所有sbt构建定义一样全部可以统一而无惧sbt自身的升级。idea也明确声明它会以build.sbt脚本为唯一准绳,idea和sbt的匹配虽然磕磕绊绊还算“最终可用”:

      1、idea->sbt:用idea创建的纯scala项目(不用sbt)基本设置与之类似比如sbt子项目即idea模块,要转为sbt项目只需创建build.sbt,然后describe your whole project inside build.sbt and then re-import the project;

      2、idea->sbt:在已有的sbt项目上,可以只通过idea增加模块(模块目录下具备iml文件),之后随时可以删掉iml文件、去到build.sbt中将其改为sbt子项目、refresh sbt项目或者在sbt shell中reload又或者restart sbt shell,在右侧sbt tool window可以看到模块被纳入sbt子项目管理;

      3、sbt->idea:sbt定义的子项目、idea自动识别为模块;

      环境是sbt1.3.7+scala2.12(2.12可能降低性能?)、 Idea2018社区版,老版本的idea2017和sbt1的兼容不太友好(其scala插件也比较老),要想无痛乖乖使用sbt1+、scala2.12+、akka2.5+(无痛指导)。最后发现sbt1.3下sbt-dependency-graph会有问题:Note: sbt >= 1.3.x is currently not supported (but hopefully fixed soon),傻白甜...

    下官裴纶

    The Command Line Interface (CLI)

        sbt有两种使用模式:一次性的批量执行模式和CLI持续交互模式sbt shell:

    1、batch mode批量模式:在项目目录下打开命令行以sbt开头来执行命令:$ sbt compile;串联多个sbt命令以空格分隔、传参以双引号限定:

        $ sbt clean compile "testOnly TestA TestB"

    2、CLI交互模式:类似REPL,在项目目录下打开命令行输入sbt、在idea中用ctrl+shift+s进入sbt shell,直接输入命令执行。idea中的sbt shell一键打开很便利了、查看setting执行show Key;查看Task执行inspect Key,可以执行的命令包括compile、test、clean、update(命令可以依赖tasks and settings),subProjectID/compile执行subProjectID项目范围上定义的的compile任务。打开一个sbt shell就是一次会话,在这一次会话中普通setting在打开时只eval一次、Task则是每次引用时执行一次,因此setting不可引用task。

    sbt项目Anatomy

      scala文件仅仅为build.sbt服务(.sbt builds can be supplemented with project/*.scala files),默认情况下,.sbt中的settings必须设置到Project级别的Scope,除非明确指定Scope; .scala中则可以将settings纳入Build级别的Scope,也可以纳入Project级别的Scope。.sbt脚本基本相当如下.scala

    import sbt._  //默认导入

    import Keys._  //默认导入

    object MyBuild extends Build {

      val helloSbt = project.in(file("."))

        .settings(Seq(

          organization := "org.example",  //内置settings Keys

          name := "hello-sbt",                   //打包用到

          version := "1.0.0-SNAPSHOT", //打包用到

          scalaVersion := "2.10.3",

          scalacOptions += "-deprecation",

          libraryDependencies ++= Seq(...

        ))

    }

      0.13之前.scala是唯一支持multi-project构建定义的方式(之后增加了multi-project .sbt build definition)。所以在1版本之前的sbt可以说还很不成熟

      在plugins.sbt中使用addSbtPlugin方法添加插件,最常用插件之一是sbt-native-packager,它最大好处是可以方便的打各种包包括Docker镜像:

      addSbtPlugin("com.typesafe.sbt" %"sbt-native-packager" %"1.6.1")

      resolvers +=Resolver.sonatypeRepo("public")

      截止目前202002这是最高版本,与其搭配scala2.12.10 + sbt1.3.7 ;外加akka2.5.29;有时在idea中还是需要一顿猛如虎操作让sbt插件完全识别。

      在.sbt脚本中,只能写statements语句和expressions表达式,不能定义object或类、所以叫脚本;statement语句不返回值如val foo = "bar";表达式会返回值如5 + 4,只要是表达式则必有返回值、必有其类型;赋值语句和纯函数调用是statements语句;其他大部分scala代码都是有返回值有类型的响当当表达式。sbt中的表达式类型要么是Setting[_] 要么是 Seq[Setting[_]] 、要么是TaskKey要么是SettingKey.

    《scala实用指南》将表达式翻译为函数值,差评! 匿名函数、lambda甚至闭包都可以归纳为表达式,只要是表达式必有返回值必有自己的类型:X=>Y 当我们说到匿名函数lambda闭包的时候我们在说什么?我们都在说表达式,函数就是表达式、程序就是在表达,表达式就是FP的核心。《Scala in Depth》在第二章核心规则2.2节提出新名词:面向表达式编程OEP,才算把表达式这个统一硬核概念提到了应有的足够高度,好评! 表达式就是函数体、表达式就是lambda、如果没有显式参数列表它还可以是闭包,像这样: 闭包和原生上下文共享变量someVal 没有显式参数列表的闭包类型

    依赖图

    直接依赖dispatch-core 0.11.2的传递依赖图

      As the number of the nodes increases, the time it takes to resolve dependencies grows significantly (This is caused by sbt dependency resolver resolving version conflicts, evaluating exclusion rules and override rules transitively). The slow resolution is exacerbated for builds with many subprojects as the resolution process is repeated for each subprojects. 

      从2014年的0.13.7版本开始加入了LinkedIn的类似增量编译的cached resolution增量解析技术(incrementally resolve the dependency graph.)可以辅助提速:cached resolution feature creates minigraphs for each direct dependency. These minigraphs are resolved using the stock dependency resolver based on Ivy, and the result is stored locally under ~/.sbt/0.13/dependency/and shared across all builds. 参考:the documentation on cached resolution

      在build.sbt设置 updateOptions 可以打开cached resolution:

        updateOptions := updateOptions.value.withCachedResolution(true)

    排斥警告Eviction warnings

      依赖图越来越大,一个常见问题是版本冲突问题

    版本冲突/干扰,你的项目直接依赖slf4j-api1.6.6

      这种情况下会采用较新版本,这种处理方式叫Eviction,1.6.6被1.7.5排斥了。冲突的版本如果是二进制兼容的,排斥就没什么问题,但是,对于二进制不兼容库依赖的排斥就会有问题,比如你的代码使用了v1.1版本API的某个方法,但这个方法在v2.0删除了。从sbt 0.13.6开始,sbt会对这种二进制不兼容的排斥情况发出警告:

    [warn] There may be incompatibilities among your library dependencies.

    [warn] Here are some of the libraries that were evicted:

    [warn]  * com.typesafe.akka:akka-actor_2.10:2.1.4 -> 2.3.7

    [warn] Run 'evicted' to see detailed eviction warnings

      我们可以手动再现这种情况:同时依赖banana-rdf 0.4 和 Akka Actor 2.3.7. 这是由于Akka 2.1.x 和 2.3.x 二进制不兼容,我们只能做如下work around:

    1、升级到较新版本:使用show update、找出冲突版本的调用者,查看有没有库的较新版本可以免除冲突;

    2、降级到较老版本:使用dependencyOverrides to override the dependency graph transitively.

    3、发布本地分支:Consult software license for requirements.

    无法解析依赖Unresolved dependencies error

      依赖找不到基本都是仓库问题,参考末尾的sbt repo配置。

      传递依赖找不到:

                   ::::::::::::::::::::::::::::::::::::::::::::::

    [warn]     ::          UNRESOLVED DEPENDENCIES         ::

    [warn]     ::::::::::::::::::::::::::::::::::::::::::::::

    [warn]  :: foundrylogic.vpp#vpp;2.2.1: not found

    [warn]  ::::::::::::::::::::::::::::::::::::::::::::::

    [warn]  Note: Unresolved dependencies path:

    [warn]    foundrylogic.vpp:vpp:2.2.1

    [warn]      +- org.apache.cayenne:cayenne-tools:3.0.2

    [warn]      +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2 (/foo/build.sbt#L25)

      有问题的依赖是maven-cayenne-plugin 3.0.2,位于build.sbt的第25行。可以添加合适的解析器resolver来解决,或者排除传递依赖,参考:Library Management.


        顺便发现了WSL有意思的一个特性,在win10上用msi win installer方式安装的工具软件比如scala、sbt,在WSL里竟然顺便也配好了,sbt装好以后path也有了什么launch启动脚本都带了,win10安装的scala在WSL查看whereis scala显示:

        scala: /mnt/c/Java/scala/bin/scala /mnt/c/Java/scala/bin/scala.bat

        说明WSL会包含进来可用的win10 的系统path,win10下安装的java8、Git是exe安装,在WSL下查看whereis java竟然也有但是没用。java8还是得装:sudo apt-get install openjdk-8-jre(apt install openjdk-8-jre-headless),win10下安装的java8一样得自己手动配置path.  目前idea的scala插件带lightbend的TechHub/project starter有很多简单示例可供学习,老版本idea里的则还有可能是旧的activator,不要用了。

        sbt特点之一是可以以scala style去定义项目构建过程,sbt构建定义力求是portable也就是可分发的、这很有价值。其次是支持子项目模块化(build.sbt可以定义子项目),这里的sbt子项目就是idea模块,与idea中的module模块一一对应,module模块又基本对应idea中的构件artifact(is an assembly of your project assets that you put together to test, deploy or distribute your software solution or its part. Examples are a collection of compiled Java classes or a jar, a Web application as a directory structure or a war, etc.),在项目根目录下执行sbt可以进入交互式sbt shell,执行命令console还可以进一步打开scala repl,它会在启动控制台前先编译你的工程代码,包含你项目所有依赖,你可以用来直接进行简单测试(有种叠屋架床的感觉)和一般的scala REPL一样:quit返回到sbt。项目根目录也叫base directory基础目录,对win10下的GP项目来说就是这个目录:C:\Users\name\IdeaProjects\incubator-gearpump.

        sbt的源码目录结构和mvn一样,小项目里也可以直接将源码*.scala放在项目根目录下,构建定义则写在build.sbt,它比较简单不能支持scala的对象和类只能写一行一行的DSL,所以在基础目录下还会有一个project子目录即元构建,比如GP项目,在基础目录下执行sbt(不带参数)会进入交互式的sbt shell,输出:

    Loading project definition from C:\Users\name\IdeaProjects\incubator-gearpump\project

    构建定义

    1、sbt的构建定义就是一堆键值对也就是Setting,sbt中一切皆Setting(Task也是Setting[Task[T]])

    构造setting的表达式:key:=value

      James Roper的观点有意思他认为setting是一种特殊Task,所以他的博客开篇就说sbt是一个task engine,这是一种视角,不管你怎么看,高度抽象的话Task和setting可以看做一回事,注意settings 只能依赖其他settings 不能依赖task,task可以依赖其他settings 或task。

      单参数运算符“:=”是sbt定义在所有Key上的,它会给一个普通setting赋一个字符串值或者给一个task赋一个函数值并构建出来一个setting/Task:

        1) 普通Setting的Key是SettingKey、任务Task的Key是TaskKey;

        2) 不管哪种Key都有:=操作符、:=操作符用于从一个键和一个值构建出来一个setting;

        3) SettingKey构建的是Setting[T]、TaskKey构建的是Setting[Task[T]];Setting可以看做一个元祖只要是Setting就是有Key有value的,比如你可以用:baseDirectory.value.getAbsolutePath获取当前项目/子项目的根目录(baseDirectory是sbt内置的settingKey[File],它的值类型是java.io.File);

      三种常用Key:

        1) SettingKey[T],普通静态文本配置;

        2) TaskKey[T]任务、本质就是一个表达式Expression,每次执行都是重新执行;

        3) InputKey[T]输入,可以接收命令行参数的任务,不常用;

      三大Key的运算符:

        1) :=     replace existing

        2) +=    add one element

        3) ++=  add Seq[T](…)

        中文官方指导翻译不仅烂而且过时了,比如讲到构建定义的二种风格:一种是多项目构建另一种是bare.sbt......这里有两个问题:

      1、在英文原版当中出现的Bare .sbt本意是指“原始的.sbt定义文件”,sbt文件叫什么都可以而且可以有多个,只是一般默认是build.sbt.  Bare .sbt中间是有空格的,所以Bare宜翻译过来;

      2、多项目构建在sbt1开始已经可以顺畅地在sbt中定义,所谓的两种定义风格不存在了;


        这篇文章,Akka项目向导工具Aviator/activator一年前就EOF了,截止到到2017,代之以基于sbt 1.0的Tech Hub

      文章大意就是Akka工具开发组重新审视了创建activator的初衷,这种东西是下载的、可能工作在各种环境上的,activator过度复杂了,Activator本质上是对sbt命令的一个包装。大家需要的只是一个好用快速上手的getting started工具,可以快速体验一个helloWorld,更重要的是,sbt变得更好了,它的DSL更好用、安装更方便。用Giter8模板方式给用户提供Tutorials更好。Giter8模板要么提交到你自己公司的Git要么提交到github.

      Giter8是Nathan Hamblin创建,它使用GitHub作为仓库提供各种模板,这样只要是github用户都可以创建自己的模板。sbt已经和Giter8集成了,所以只要输入命令:"sbt new"跟上你的仓库名,就能快速创建原型,也就是说用命令行你也可以快速开始一个项目。Tech Hub同样和Giter8集成了,如果你对sbt很熟悉,也可以直接“sbt new”

      使用Tech Hub的方法是在idea中新建scala项目、选择lightbend project stater:sbt-based project from a Lightbend Tech Hub template

        关于sbt 1.0:随着Scala 2.12发布,sbt从0.13到1变化较大,这俩要配套外加Akka2.5


        olderSky一月份的Sbt baby steps

    两种setting:key和task %"test"定义了维度;扩展Build方式已经deprecated了,只要搞build.sbt就够了 以build.sbt为主、.scala为辅 sbt脚本中可用的三大运算符一直没有变化:key的三大方法 三种Key 在build.sbt里这样定义一个Task并将其实现、设置到一个project上
    在sbt shell里,show可以查看一个 task 的执行结果;由于sbt的内置setting特别是task众多,用inspect可以了解更多setting的信息,虽然 inspect 显示的一些信息没有意义,但是在顶部会显示 setting 的 value 类型和 setting 的简介,还会显式该task在何处定义以及它依赖的其他task(如果有的话);注意hello是Key具体来说是一个TaskKey的名字,不要用setting名字
    将依赖从build.sbt分离到单独的.scala管理 每一个 key 在不同scope 都可以有不同 value 子项目和task都可以作为范围,test in assembly := {} 的效果就是:do not run tests during assembly. 一个 configuration 是一种类型的构建,例如 Compile 或者 Test 一个普通文本setting在不同范围可以具备不同的值

        idea官方建议:We recommend that you switch to Java 8 SDK since the Scala version 2.12, and sbt versions 1.1 and later are not compatible with older Java SDK versions.

        sbt国内更新源问题总结为:华为最好用、阿里可以用、sbt插件repo必须用。关于代理仓库sbt Reference Manual — Proxy Repositories,Resolvers是在代码中解析仓库的实例,有两种一种是预定义的一堆可以看一下sbt官方resolvers,另一种是自定义的最常用的是:resolvers+="Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"。代理仓库的作用:

    其实就是墙内用户的共享云存储

        阿里就是一个代理仓库,sbt要用起来代理仓库需要配置最常用的就是~/.sbt/repositories了,sbt.override.build.repos配置用于指定优先采用repositories、其次才是sbt项目当中代码添加的resolvers. 相同项前者会覆盖后者,这样有了这么一个配置文件就能保证优先采用的代理仓库:-Dsbt.override.build.repos=true,默认是false要打开必须指定。

      废话不多说了截止目前(2020-02)以下可以直接用:
    [repositories]

    local

    huaweicloud-maven: https://repo.huaweicloud.com/repository/maven/

    maven-central: https://repo1.maven.org/maven2/

    sbt-plugin-repo: https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]

    huaweicloud-ivy: https://repo.huaweicloud.com/repository/ivy/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]

    aliyun-nexus: https://maven.aliyun.com/nexus/content/groups/public/

    Central-repository: https://central.maven.org/maven2/

    # 没屁用:

    # typesafe: https://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly

    # sonatype-oss-releases: https://oss.sonatype.org/content/repositories/releases

    # sonatype-oss-snapshots: https://oss.sonatype.org/content/repositories/snapshots

    Just Do it

    相关文章

      网友评论

          本文标题:sbt闲言碎语

          本文链接:https://www.haomeiwen.com/subject/lgsalftx.html