Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

sbt core concepts (ScalaMatsuri 2019)

110 views

Published on

sbt is an interactive build tool created for Scala. In this talk I will go over the core concepts of sbt such as tasks vs commands, and scoping.

Published in: Software
  • Be the first to comment

  • Be the first to like this

sbt core concepts (ScalaMatsuri 2019)

  1. 1. sbt core concepts Eugene Yokota (@eed3si9n)
 Lightbend
  2. 2. goal: gain better intuition about sbt sbt に対する直感を育む
  3. 3. sbt by example
  4. 4. 
 build tool • "automates repeatable tasks"
 — Build Systems à la Carte
 Mokhov, Mitchell, Jones 「リピータブルなタスクの自動化」
  5. 5. a casually functional
 build tool カジュアルに関数型なビルドツール
  6. 6. State
  7. 7. State 1. build structure 2. your disk sbt での状態は、ビルド構造とあなたのディスク
  8. 8. Command • State State
  9. 9. Command • State State • processed sequentially • low-level construct; avoid making custom commands 逐次処理される 低レベルなものなので、カスタムコマンドは避ける
  10. 10. examples of command commands help, tasks projects, project hello set name := "foo" <command1>; <command2> ++ 2.13.0, ++ 2.13.0!, +<command> shell command act command
  11. 11. act command • lifts tasks and settings into a command act コマンドはタスクやセッティングを持ち上げる
  12. 12. which state is it changing? commands changes help, tasks no changes projects, project hello build structure set name := "foo" build structure <command1>; <command2> both ++ 2.13.0, ++ 2.13.0!, +<command> both act command disk 変更している状態はどっち?
  13. 13. why State + Command? 何のために State + Command があるの?
  14. 14. why State + Command? • predictable checkpoint • building block for interactiveness:
 "automates repeatable tasks" 予測可能なチェックポイント インタラクティブ性のための構成要素
  15. 15. interactive sbt> testOnly foo.bar command logs sbt shell effects
  16. 16. about time
  17. 17. distributed events event a event b "happens before" process p 分散イベント 同プロセス内では、イベントの順序は自明
  18. 18. distributed events event a event b "happens before" process p process q メッセージの送信はメッセージの受信よりも先にある
  19. 19. distributed events • a → b (a happens before b) • a ↛ b (a does not happen before b) • 2 distinct events a and b are said to be concurrent if a ↛ b and b ↛ a a ↛ b かつ b ↛ a であるとき、2つのイベントは並行
  20. 20. distributed events • a → b (a happens before b) • a ↛ b (a does not happen before b) • 2 distinct events a and b are said to be concurrent if a ↛ b and b ↛ a 2 events are concurrent if neither can causality affect each other 2つのイベントがお互いに因果的に影響を持たない場合、並行
  21. 21. Applicative functor scala> (3.some, 2.some) mapN { _ + _ } res8: Option[Int] = Some(5) scala> (none[Int], 5.some) mapN { _ - _ } res9: Option[Int] = None See 'Oh, All the things you'll traverse' by Luka Jacobowitz
  22. 22. for comprehension getLine flatMap { x => print(length(x)) flatMap { _ => getLine flatMap { y => IO(x ++ y) } } } for { x <- getLine _ <- print(length(x)) y <- getLine } yield x ++ y def procedural: Unit = { val x = getLine print(length(x)) val y = getLine x ++ y } for 内包表記

  23. 23. build.sbt DSL // sbt 0.12 style foo <<= (compile in Compile, bar) map { (c, b) => doSomething(b) } // sbt 1.x style foo := { val c = (Compile / compile).value val b = bar.value doSomething(b) }
  24. 24. build.sbt DSL // sbt 0.12 style foo <<= (compile in Compile, bar) map { (c, b) => doSomething(b) } // sbt 1.x style foo := { val c = (Compile / compile).value val b = bar.value doSomething(b) } Applicative composition
  25. 25. build.sbt DSL foo := { val c = (Compile / compile).value val b = bar.value doSomething(b) } Compile / compile bar foo "happens before"
  26. 26. build.sbt DSL foo := { val c = (Compile / compile).value val b = bar.value doSomething(b) } Compile / compile bar foo "happens before" line of sand in time-space
  27. 27. Applicative composition foo := { val c = (Compile / compile).value val b = bar.value doSomething(b) } Test / test := { val c = (Compile / compile).value val f = foo.value } Compile / compile bar foo "happens before" Test / test "happens before"
  28. 28. why Applicative composition? Compile / compile bar foo "happens before" Test / test "happens before" 何故 Applicative 合成をするのか?
  29. 29. why Applicative composition? Compile / compile bar foo "happens before" Test / test "happens before" 1. minimality (executes task at most once, for input that changed) evaluated only once ミニマル性 (タスクは入力が変化したとき、最多で1回のみ) Compile / compile は一回のみ実行される
  30. 30. why Applicative composition? Compile / compile bar foo "happens before" Test / test "happens before" 1. minimality 2. automatic parallel processing 自動並列処理

  31. 31. act command • given a task, creates a plan that evaluates the task in current and aggregated subprojects • concurrent tasks are evaluated in parallel lazy val root = (project in file(".")) .aggregate(util, core) .settings(...) lazy val util = (project in file("util")) .dependsOn(core) lazy val core = (project in file("core")) act コマンドは、与えられたタスクを現在サブプロジェクトと
 集約されたサブプロジェクトで実行するプランを作成する
  32. 32. act command • how does it relate with State? s0 s1 s2 reload (settings) act (task) act (task) 状態との関連は?

  33. 33. tasks vs commands • prefer tasks for plugin extension • tasks compose automatically • command composition is stateful / sequential プラグイン拡張性にはタスクがオススメ
 タスクは自動合成するが、コマンド合成は stateful
  34. 34. ~ command
  35. 35. build structure ビルド構造

  36. 36. build structure: build build {uri} subproject {uri}foo configuration foo/Compile key-value store (settings + tasks) Scala version subproject {uri}bar configuration bar/Compile Scala version
  37. 37. build structure: subproject build {uri} subproject {uri}foo configuration foo/Compile foo/Runtime foo/Test key-value store (settings + tasks) Scala version
  38. 38. build structure: configuration • has its own sources + library dependencies • Test extends Runtime. Runtime extends Compile. subproject {uri}foo configuration foo/Compile foo/Runtime foo/Test Scala version コンフィギュレーションは独自のソースとライブラリ依存性を 持つ
  39. 39. build structure: k-v store build {uri} subproject {uri}foo configuration foo/Compile key-value store (settings + tasks) Scala version subproject {uri}bar configuration bar/Compile Scala version
  40. 40. key-value store key value name helloworld version 0.1.0 organization com.example
  41. 41. key-value store key value foo/name helloworld ThisBuild/version 0.1.0 ThisBuild/organization com.example foo/Compile/compile <task> foo/Compile/console/scalacOptions List()
  42. 42. key scoping 1. subproject (Zero, ThisBuild, foo, etc.) 2. configuration (Zero, Compile, Test, etc.) 3. in-task (Zero, console, etc.) foo/Compile/console/scalacOptions キーのスコープ付け

  43. 43. key-value store key value name foo/name foo/Zero/Zero/name helloworld foo/Compile/console/scalacOptions List() • keys are automatically scoped to the current subproject • other scope axes default to Zero • Global = Zero/Zero/Zero キーは自動的にカレント・サブプロジェクトにスコープされる 他のスコープ軸のデフォルトは Zero
  44. 44. why key-value store? • inspect command • provides flexible extensibility on most aspects of the build • plugins can created based on other plugins sbt:sbtRoot> inspect tree test [info] Test / test = Task[Unit] [info] +-Test / executeTests = Task[sbt.Tests$Output] [info] | +-classLoaderLayeringStrategy = ScalaLibrary [info] | +-Test / loadedTestFrameworks = Task[scala.collection.immutable.Map[sbt.TestFramework, .. [info] | | +-Test / loadedTestFrameworks / streams = Task[sbt.std.TaskStreams[sbt.internal.util... [info] | | | +-Global / streamsManager = Task[sbt.std.Streams[sbt.internal.util.Init$.. [info] | | | 構造を inspect することができる ビルドを形成する多くの部分を柔軟に拡張できる
  45. 45. setting expresion name := { "hello" } key operator (setting/task) body • operators :=, +=, ++= • a setting expression represents a transformation of k-v store セッティング式
  46. 46. setting expresion ThisBuild / organization := "com.example" name := (ThisBuild / organization).value + "12" • use .value to lookup the setting/task value • key-value store forms a DAG (directed acyclic graph) .value を使ってセッティングやタスクの値を参照する key-value ストアは DAG を形成する
  47. 47. delegation rules x := (core / Test / console / scalacOption).value 1. look for the specified key, then try in-task ⇢ Zero 2. next try Test ⇢ Runtime ⇢ Compile ⇢ Zero 3. next try core ⇢ ThisBuild ⇢ Zero 4. precedence: subproject > configuration > in-task 5. transitive evaluation doesn't carry original context (no dynamic dispatch) 5つの移譲ルールがあり、指定されたキーが無い場合に 次に見る場所を規定する
  48. 48. delegation rules ThisBuild / version := name.value lazy val b = project .settings( name := "b" something := version.value ) • transitive evaluation doesn't carry original context (no dynamic dispatch) 間接的評価は元のコンテキストを伴わない OO の this.draw 的な振る舞いでは無い
  49. 49. tip for plugin authors • define custom keys at the widest scope (Global), and reference the keys using the narrowest scope Global / obfuscateLogic := Logic.Default Compile / obfuscate := { val logic = (Compile / obfuscateLogic).value doObfuscate(logic) } • This allows build users various levels to rewire the setting (Global, ThisBuild, proj, proj/Compile) カスタムキーは最も広いスコープ付けで定義し、 最も狭いスコープ付けで参照すると最大の柔軟性を得られる
  50. 50. key-value store foo/Compile/scalacOptions foo/name ThisBuild/version Test Runtime Compile foo/Compile/compile foo ThisBuild Zero key configuration subproject foo/Runtime/compile foo/Test/compile in-task foo/Compile/console/scalacOptions foo/baseDirectory
  51. 51. sbt 1.3.0
  52. 52. sbt 1.3.0 RC-2 • Coursier for library management • super shell and tracing • turbo mode (enabled layered ClassLoader) • details https://www.lightbend.com/blog/sbt-1.3.0- release Coursier がライブラリ管理のデフォルト
 super shell は現行タスクを表示させる
  53. 53. Thank You
  54. 54. delta vee (2019.06 mixtape)

×