ใครสามารถอธิบายวิธีใช้ SBT ที่ถูกต้องได้หรือไม่?


100

ฉันกำลังจะออกจากตู้ในเรื่องนี้! ฉันไม่เข้าใจ SBT ฉันบอกว่าตอนนี้ช่วยฉันด้วย

ถนนทุกสายมุ่งสู่กรุงโรมและที่เป็นเหมือนกันสำหรับ SBT: การเริ่มต้นกับSBTมีSBT, SBT Launcher, SBT-extrasฯลฯ และจากนั้นก็มีวิธีการที่แตกต่างกันรวมและตัดสินใจเกี่ยวกับการเก็บ มีวิธีที่ 'ดีที่สุด' หรือไม่?

ฉันถามเพราะบางครั้งฉันก็หลงทางนิดหน่อย เอกสาร SBT มีความละเอียดและสมบูรณ์มาก แต่ฉันพบว่าตัวเองไม่รู้ว่าควรใช้build.sbtหรือproject/build.propertiesหรือproject/Build.scalaหรือเมื่อproject/plugins.sbtใด

แล้วมันจะสนุกขึ้นมีScala-IDEและSBT- วิธีที่ถูกต้องในการใช้ร่วมกันคืออะไร? อะไรมาก่อนไก่หรือไข่?

ที่สำคัญที่สุดคือคุณจะหาที่เก็บและเวอร์ชันที่เหมาะสมเพื่อรวมไว้ในโครงการของคุณได้อย่างไร? ฉันแค่ดึงมีดพร้าออกมาและเริ่มแฮ็คทางไปข้างหน้าหรือไม่? ฉันมักจะพบโครงการที่มีทุกอย่างและอ่างล้างจานในครัวแล้วฉันก็รู้ว่า - ฉันไม่ใช่คนเดียวที่หลงทางนิดหน่อย

เป็นตัวอย่างง่ายๆตอนนี้ฉันกำลังเริ่มโครงการใหม่ ฉันต้องการใช้คุณสมบัติล่าสุดของSLICKและScalaและอาจต้องใช้ SBT เวอร์ชันล่าสุด อะไรคือจุดเริ่มต้นที่ดีและเพราะเหตุใด ฉันควรกำหนดไฟล์ในไฟล์ใดและควรมีลักษณะอย่างไร ฉันรู้ว่าฉันสามารถทำงานนี้ได้ แต่ฉันต้องการความเห็นจากผู้เชี่ยวชาญว่าทุกอย่างควรไปที่ใด (ทำไมถึงควรไปที่นั่นจะได้รับโบนัส)

ฉันใช้SBTสำหรับโครงการขนาดเล็กมานานกว่าหนึ่งปีแล้ว ผมใช้SBTแล้วSBT Extras(ตามที่มันทำให้ปวดหัวบางอย่างน่าอัศจรรย์หายไป) แต่ผมไม่แน่ใจว่าทำไมฉันควรจะใช้อย่างใดอย่างหนึ่งหรืออื่น ๆ ฉันรู้สึกหงุดหงิดเล็กน้อยที่ไม่เข้าใจว่าสิ่งต่าง ๆ เข้ากันได้อย่างไร ( SBTและที่เก็บ) และคิดว่ามันจะช่วยชายคนต่อไปที่ต้องเผชิญกับความยากลำบากอย่างมากหากสิ่งนี้สามารถอธิบายได้ในแง่ของมนุษย์


2
คุณหมายความว่าอย่างไรกับ "มี Scala-IDE และ SBT" คุณกำหนดโปรเจ็กต์ของคุณด้วย sbt และ sbt สามารถสร้างโปรเจ็กต์ ide (eclipse oder intellij) ดังนั้น SBT มาก่อน ...
ม.ค.

2
@ ฉันพูดถึงเรื่องนั้นเพราะ Scala-IDE ใช้ SBT เป็นตัวจัดการการสร้าง ดูที่assembla.com/spaces/scala-ide/wiki/SBT-based_build_managerและด้านล่างในโพสต์ที่ระบุว่า "ไม่จำเป็นต้องกำหนดไฟล์โครงการ SBT ของคุณ" ซึ่งฉันพบว่าสับสน
แจ็ค

ตกลง. ตามปกติแล้วฉันจะใช้ intellij (หรือ sublime) เพื่อแก้ไข scala ฉันไม่รู้ ฉันเดาว่าผู้สร้างสร้างการกำหนดค่า sbt ของตัวเอง?
ม.ค.

2
@JacobusR ข้อเท็จจริงที่ว่า Scala IDE ใช้ SBT เพื่อสร้างแหล่งที่มาของโครงการของคุณเป็นรายละเอียดการใช้งานและผู้ใช้ไม่จำเป็นต้องกังวลเกี่ยวกับเรื่องนี้ มีความหมายเป็น 0 จริงๆ ผู้ใช้ภายนอก Eclipse สามารถสร้างโปรเจ็กต์ด้วย SBT, Maven, Ant, ... และนั่นจะไม่สร้างความแตกต่างให้กับ Scala IDE อีกอย่างแม้ว่าคุณจะมีโปรเจ็กต์ SBT แต่ Scala IDE ก็ไม่สนใจนั่นคือมันไม่ได้มองหาBuild.scalaการตั้งค่าคลาสพา ธ ของคุณและนั่นคือเหตุผลที่คุณต้องใช้sbteclipseเพื่อสร้าง Eclipse .classpath หวังว่านี่จะช่วยได้
Mirco Dotta

1
@Jan Scala IDE เพิ่มความสับสนและใช่เอกสารที่ให้ภาพรวมที่ใหญ่ขึ้นเกี่ยวกับการตั้งค่าสภาพแวดล้อมการพัฒนา Scala ที่ดีและคำแนะนำที่ชัดเจนของเวิร์กโฟลว์การเขียนโปรแกรมที่เหมาะสมจะมีประโยชน์มาก
แจ็ค

คำตอบ:


29

ที่สำคัญที่สุดคือคุณจะหาที่เก็บและเวอร์ชันที่เหมาะสมเพื่อรวมไว้ในโครงการของคุณได้อย่างไร? ฉันแค่ดึงมีดพร้าออกมาและเริ่มแฮ็คทางไปข้างหน้าหรือไม่? ฉันมักจะพบโครงการที่มีทุกอย่างและอ่างล้างจานในครัว

สำหรับการอ้างอิงตาม Scala ฉันจะไปกับสิ่งที่ผู้เขียนแนะนำ ตัวอย่างเช่น: http://code.google.com/p/scalaz/#SBTระบุว่าใช้:

libraryDependencies += "org.scalaz" %% "scalaz-core" % "6.0.4"

หรือhttps://github.com/typesafehub/sbteclipse/มีคำแนะนำเกี่ยวกับตำแหน่งที่จะเพิ่ม:

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0-RC1")

สำหรับการอ้างอิงที่ใช้ Java ฉันใช้http://mvnrepository.com/เพื่อดูว่ามีอะไรอยู่บ้างจากนั้นคลิกที่แท็บ SBT ตัวอย่างเช่นhttp://mvnrepository.com/artifact/net.sf.opencsv/opencsv/2.3ระบุว่าใช้:

libraryDependencies += "net.sf.opencsv" % "opencsv" % "2.3"

จากนั้นดึงมีดพร้าออกมาและเริ่มแฮ็กไปข้างหน้า หากคุณโชคดีคุณไม่ต้องใช้ขวดโหลที่ขึ้นอยู่กับขวดโหลเดียวกัน แต่มีเวอร์ชันที่เข้ากันไม่ได้ ด้วยระบบนิเวศของ Java คุณมักจะรวมทุกอย่างและอ่างล้างจานในครัวและต้องใช้ความพยายามในการกำจัดการพึ่งพาหรือทำให้แน่ใจว่าคุณจะไม่พลาดการอ้างอิงที่จำเป็น

เป็นตัวอย่างง่ายๆตอนนี้ฉันกำลังเริ่มโครงการใหม่ ฉันต้องการใช้คุณสมบัติล่าสุดของ SLICK และ Scala และอาจต้องใช้ SBT เวอร์ชันล่าสุด อะไรคือจุดเริ่มต้นที่ดีและเพราะเหตุใด

ผมคิดว่าจุดที่มีสติคือการสร้างภูมิคุ้มกันให้กับ SBT ค่อยๆ

ตรวจสอบให้แน่ใจว่าคุณเข้าใจ:

  1. รูปแบบขอบเขต {<build-uri>}<project-id>/config:key(for task-key)
  2. 3 รสชาติของการตั้งค่า ( SettingKey, TaskKey, InputKey) - อ่านส่วนที่เรียกว่า "งานคีย์" ในhttp://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def

เปิดหน้าทั้ง 4 หน้าไว้ตลอดเวลาเพื่อให้คุณสามารถข้ามและค้นหาคำจำกัดความและตัวอย่างต่างๆ:

  1. http://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def
  2. http://www.scala-sbt.org/release/docs/Detailed-Topics/index
  3. http://harrah.github.com/xsbt/latest/sxr/Keys.scala.html
  4. http://harrah.github.com/xsbt/latest/sxr/Defaults.scala.html

ใช้ประโยชน์สูงสุดshowและinspect และการทำให้แท็บเสร็จสมบูรณ์เพื่อทำความคุ้นเคยกับค่าจริงของการตั้งค่าการอ้างอิงคำจำกัดความและการตั้งค่าที่เกี่ยวข้อง ฉันไม่เชื่อว่าความสัมพันธ์ที่คุณจะค้นพบinspectนั้นได้รับการบันทึกไว้ทุกที่ หากมีวิธีที่ดีกว่านี้ฉันต้องการทราบเกี่ยวกับเรื่องนี้


25

วิธีที่ฉันใช้ sbt คือ:

  1. ใช้sbt-extras - เพียงแค่รับเชลล์สคริปต์และเพิ่มลงในรูทของโปรเจ็กต์ของคุณ
  2. สร้างprojectโฟลเดอร์ที่มีMyProject.scalaไฟล์สำหรับตั้งค่า sbt ฉันชอบวิธีนี้มากกว่าbuild.sbtวิธีนี้ - มันเป็นสกาลาและมีความยืดหยุ่นมากกว่า
  3. สร้างproject/plugins.sbtไฟล์และเพิ่มปลั๊กอินที่เหมาะสมสำหรับ IDE ของคุณ ไม่ว่าจะเป็น sbt-eclipse, sbt-idea หรือ ensime-sbt-cmd เพื่อให้คุณสามารถสร้างไฟล์โครงการสำหรับ eclipse, intellij หรือ ensime
  4. เปิด sbt ในรูทของโปรเจ็กต์ของคุณและสร้างไฟล์โปรเจ็กต์สำหรับ IDE ของคุณ
  5. กำไร

ฉันไม่ต้องกังวลกับการตรวจสอบไฟล์โครงการ IDE เนื่องจากไฟล์เหล่านี้สร้างขึ้นโดย sbt แต่อาจมีเหตุผลที่คุณต้องการทำเช่นนั้น

คุณสามารถดูตัวอย่างการตั้งค่าเช่นนี้ที่นี่


ขอบคุณสำหรับคำตอบดีๆ ฉันยอมรับคำตอบอื่นเพราะมันครอบคลุมพื้นที่มากกว่าและโหวตให้เหตุผลของคุณก็ดีเช่นกัน ฉันจะยอมรับทั้งสองอย่างถ้าทำได้
แจ็ค

0

ใช้ typesafe Activator ซึ่งเป็นวิธีการเรียก sbt ที่สวยงามซึ่งมาพร้อมกับเทมเพลตโครงการและเมล็ดพันธุ์: https://typesafe.com/activator

Activator new

Fetching the latest list of templates...

Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
 1) minimal-java
 2) minimal-scala
 3) play-java
 4) play-scala
(hit tab to see a list of all templates)

5
ฉันเป็นส่วนหนึ่งของความคิดที่ว่าหากมีข้อสงสัยการเพิ่มความมหัศจรรย์ให้กับส่วนผสมนั้นไม่น่าจะช่วยแก้ปัญหาของคุณได้
คิวบิก

0

การติดตั้ง

brew install sbt หรือการติดตั้งที่คล้ายกัน sbt ซึ่งในทางเทคนิคประกอบด้วย

เมื่อคุณเรียกใช้งานsbtจากเทอร์มินัลมันจะเรียกใช้สคริปต์ bash ตัวเรียกใช้ sbt โดยส่วนตัวฉันไม่เคยต้องกังวลเกี่ยวกับไตรลักษณ์นี้และใช้ sbt ราวกับว่ามันเป็นสิ่งเดียว

การกำหนดค่า

ในการกำหนดค่า sbt สำหรับ.sbtoptsไฟล์บันทึกโปรเจ็กต์เฉพาะที่รูทของโปรเจ็กต์ การกำหนดค่าทั้งระบบ SBT /usr/local/etc/sbtoptsแก้ไข การดำเนินการsbt -helpควรบอกตำแหน่งที่แน่นอน ตัวอย่างเช่นการให้ SBT หน่วยความจำมากขึ้นเป็น one-off ดำเนินการsbt -mem 4096หรือบันทึก-mem 4096ใน.sbtoptsหรือsbtoptsการเพิ่มหน่วยความจำจะมีผลอย่างถาวร

 โครงสร้างโครงการ

sbt new scala/scala-seed.g8 สร้างโครงสร้างโครงการ Hello World sbt แบบเรียบง่าย

.
├── README.md  // most important part of any software project
├── build.sbt  // build definition of the project
├── project    // build definition of the build (sbt is recursive - explained below)
├── src        // test and main source code
└── target     // compiled classes, deployment package

คำสั่งที่ใช้บ่อย

test                                                // run all test
testOnly                                            // run only failed tests
testOnly -- -z "The Hello object should say hello"  // run one specific test
run                                                 // run default main
runMain example.Hello                               // run specific main
clean                                               // delete target/
package                                             // package skinny jar
assembly                                            // package fat jar
publishLocal                                        // library to local cache
release                                             // library to remote repository
reload                                              // after each change to build definition

เปลือกหอยมากมาย

scala              // Scala REPL that executes Scala language (nothing to do with sbt)
sbt                // sbt REPL that executes special sbt shell language (not Scala REPL)
sbt console        // Scala REPL with dependencies loaded as per build.sbt
sbt consoleProject // Scala REPL with project definition and sbt loaded for exploration with plain Scala langauage

Build definition เป็นโครงการ Scala ที่เหมาะสม

นี่เป็นหนึ่งในแนวคิดหลักเกี่ยวกับ SBT ที่เป็นสำนวน ฉันจะพยายามอธิบายด้วยคำถาม สมมติว่าคุณต้องการกำหนดงาน sbt ที่จะดำเนินการตามคำขอ HTTP ด้วย scalaj-http โดยสัญชาตญาณเราอาจลองทำสิ่งต่อไปนี้ภายในbuild.sbt

libraryDependencies +=  "org.scalaj" %% "scalaj-http" % "2.4.2"

val fooTask = taskKey[Unit]("Fetch meaning of life")
fooTask := {
  import scalaj.http._ // error: cannot resolve symbol
  val response = Http("http://example.com").asString
  ...
}

import scalaj.http._อย่างไรก็ตามเรื่องนี้ผิดพลาดจะพูดว่าหายไป จะเป็นไปได้อย่างไรเมื่อเราถูกเพิ่มเข้าไปscalaj-httpในlibraryDependencies? นอกจากนี้ทำไมมันทำงานเมื่อแทนเราเพิ่มขึ้นต่อไปproject/build.sbt?

// project/build.sbt
libraryDependencies +=  "org.scalaj" %% "scalaj-http" % "2.4.2"

คำตอบคือfooTaskจริงๆแล้วเป็นส่วนหนึ่งของโครงการ Scala ที่แยกจากโครงการหลักของคุณ โครงการ Scala ที่แตกต่างกันนี้สามารถพบได้ภายใต้project/ไดเร็กทอรีซึ่งมีtarget/ไดเร็กทอรีของตัวเองซึ่งมีคลาสที่คอมไพล์อยู่ ในความเป็นจริงproject/target/config-classesควรมีคลาสที่ถอดรหัสเป็นบางอย่างเช่น

object $9c2192aea3f1db3c251d extends scala.AnyRef {
  lazy val fooTask : sbt.TaskKey[scala.Unit] = { /* compiled code */ }
  lazy val root : sbt.Project = { /* compiled code */ }
}

เราจะเห็นว่าfooTaskเป็นเพียงสมาชิกคนหนึ่งของวัตถุ Scala $9c2192aea3f1db3c251dปกติชื่อ เห็นได้ชัดว่าscalaj-httpควรเป็นการพึ่งพาของการกำหนดโครงการ$9c2192aea3f1db3c251dไม่ใช่การพึ่งพาโครงการที่เหมาะสม ดังนั้นจึงจำเป็นต้องมีการประกาศproject/build.sbtแทนbuild.sbtเนื่องจากprojectเป็นที่ที่โครงการ Scala นิยามการสร้างอยู่

เพื่อผลักดันให้จุดที่สร้างความหมายเป็นเพียงอีกหนึ่งโครงการ Scala sbt consoleProjectรัน สิ่งนี้จะโหลด Scala REPL ด้วยโปรเจ็กต์คำจำกัดความการสร้างบนคลาสพา ธ คุณควรเห็นการนำเข้าตามบรรทัดของ

import $9c2192aea3f1db3c251d

ตอนนี้เราสามารถโต้ตอบโดยตรงกับโครงการนิยามการสร้างโดยเรียกมันว่า Scala ที่เหมาะสมแทนbuild.sbtDSL ตัวอย่างเช่นการดำเนินการต่อไปนี้fooTask

$9c2192aea3f1db3c251d.fooTask.eval

build.sbtภายใต้โครงการหลักเป็น DSL spcial project/ที่ช่วยกำหนดสร้างข้อกำหนดของโครงการภายใต้

และสร้างนิยามโครงการ Scala สามารถมีโครงการสร้างนิยาม Scala ภายใต้project/project/และอื่น ๆ เราพูดSBT เป็น recursive

sbt ขนานกันโดยค่าเริ่มต้น

sbt สร้างDAGออกจากงาน สิ่งนี้ช่วยให้สามารถวิเคราะห์การอ้างอิงระหว่างงานและดำเนินการควบคู่กันไปและแม้แต่ทำการคัดลอกข้อมูลซ้ำซ้อน build.sbtDSL ได้รับการออกแบบโดยคำนึงถึงสิ่งนี้ซึ่งอาจนำไปสู่ความหมายที่น่าแปลกใจในตอนแรก คุณคิดว่าลำดับการดำเนินการเป็นอย่างไรในตัวอย่างต่อไปนี้

def a = Def.task { println("a") }
def b = Def.task { println("b") }
lazy val c = taskKey[Unit]("sbt is parallel by-default")
c := {
  println("hello")
  a.value
  b.value
}

โดยสัญชาตญาณอาจคิดว่าขั้นตอนที่นี่คือการพิมพ์ครั้งแรกhelloจากนั้นดำเนินการaแล้วจึงbทำงาน อย่างไรก็ตามเรื่องนี้จริงหมายถึงการดำเนินการaและbในแบบขนานและก่อน println("hello")ดังนั้น

a
b
hello

หรือเนื่องจากคำสั่งซื้อaและbไม่รับประกัน

b
a
hello

บางทีขัดแย้งกันใน sbt มันง่ายกว่าที่จะทำแบบขนานกว่าอนุกรม หากคุณจำเป็นต้องสั่งซื้อแบบอนุกรมที่คุณจะต้องใช้สิ่งที่พิเศษเช่นDef.sequentialหรือDef.taskDynจะเลียนแบบสำหรับความเข้าใจ

def a = Def.task { println("a") }
def b = Def.task { println("b") }
lazy val c = taskKey[Unit]("")
c := Def.sequential(
  Def.task(println("hello")),
  a,
  b
).value

เหมือนกับ

for {
  h <- Future(println("hello"))
  a <- Future(println("a"))
  b <- Future(println("b"))
} yield ()

ที่เราเห็นว่าไม่มีการพึ่งพาระหว่างส่วนประกอบในขณะที่

def a = Def.task { println("a"); 1 }
def b(v: Int) = Def.task { println("b"); v + 40 }
def sum(x: Int, y: Int) = Def.task[Int] { println("sum"); x + y }
lazy val c = taskKey[Int]("")
c := (Def.taskDyn {
  val x = a.value
  val y = Def.task(b(x).value)
  Def.taskDyn(sum(x, y.value))
}).value

เหมือนกับ

def a = Future { println("a"); 1 }
def b(v: Int) = Future { println("b"); v + 40 }
def sum(x: Int, y: Int) = Future { x + y }

for {
  x <- a
  y <- b(x)
  c <- sum(x, y)
} yield { c }

ที่เราเห็นsumขึ้นอยู่กับและมีการรอและab

กล่าวอีกนัยหนึ่ง

  • สำหรับความหมายเชิงประยุกต์ใช้.value
  • สำหรับการใช้ความหมายแบบ monadicsequentialหรือtaskDyn

พิจารณาตัวอย่างข้อมูลอื่นที่สับสนทางความหมายอันเป็นผลมาจากลักษณะการสร้างการพึ่งพาvalueซึ่งแทนที่จะเป็น

`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.
val x = version.value
                ^

เราต้องเขียน

val x = settingKey[String]("")
x := version.value

โปรดทราบว่าไวยากรณ์.valueเป็นเรื่องเกี่ยวกับความสัมพันธ์ใน DAG และไม่ได้หมายความว่า

"ให้ค่าฉันตอนนี้"

แทนหมายถึง

"ผู้โทรของฉันขึ้นอยู่กับฉันก่อนและเมื่อฉันรู้ว่า DAG ทั้งหมดเข้ากันได้อย่างไรฉันจะสามารถให้ค่าที่ร้องขอแก่ผู้โทรได้"

ตอนนี้มันอาจจะชัดเจนขึ้นเล็กน้อยว่าทำไมxยังไม่สามารถกำหนดค่าได้ ยังไม่มีค่าในขั้นตอนการสร้างความสัมพันธ์

เราสามารถเห็นความแตกต่างอย่างชัดเจนในความหมายระหว่างภาษา Scala ที่เหมาะสมกับภาษา DSL ในbuild.sbt. นี่คือกฎเล็ก ๆ น้อย ๆ ที่เหมาะกับฉัน

  • DAG สร้างขึ้นจากนิพจน์ประเภท Setting[T]
  • ในกรณีส่วนใหญ่เราใช้.valueไวยากรณ์และ sbt จะดูแลการสร้างความสัมพันธ์ระหว่างSetting[T]
  • บางครั้งเราต้องปรับแต่งส่วนหนึ่งของ DAG ด้วยตนเองและสำหรับสิ่งที่เราใช้Def.sequentialหรือDef.taskDyn
  • เมื่อได้รับการดูแลความผิดปกติทางไวยากรณ์ในการสั่งซื้อ / ความสัมพันธ์แล้วเราสามารถใช้ความหมายปกติของ Scala ในการสร้างตรรกะทางธุรกิจที่เหลือของงานได้

 คำสั่งเทียบกับงาน

คำสั่งเป็นวิธีที่ขี้เกียจออกจาก DAG การใช้คำสั่งทำให้ง่ายต่อการเปลี่ยนสถานะการสร้างและงานต่อเนื่องตามที่คุณต้องการ ค่าใช้จ่ายคือเราสูญเสียการขนานและการคัดลอกงานที่จัดทำโดย DAG ซึ่งวิธีใดควรเป็นทางเลือกที่ต้องการ sbt shellคุณสามารถคิดว่าคำสั่งเป็นชนิดของการบันทึกถาวรของหนึ่งเซสชั่นอาจทำภายใน ตัวอย่างเช่นให้

vval x = settingKey[Int]("")
x := 13
lazy val f = taskKey[Int]("")
f := 1 + x.value

พิจารณาผลลัพธ์ของเซสชันต่อไปนี้

sbt:root> x
[info] 13
sbt:root> show f
[info] 14
sbt:root> set x := 41
[info] Defining x
[info] The new value will be used by f
[info] Reapplying settings...
sbt:root> show f
[info] 42

set x := 41โดยเฉพาะอย่างยิ่งไม่ว่าเรากลายพันธุ์สร้างรัฐที่มี คำสั่งช่วยให้เราสามารถบันทึกเซสชันข้างต้นได้อย่างถาวรเช่น

commands += Command.command("cmd") { state =>
  "x" :: "show f" :: "set x := 41" :: "show f" :: state
}

นอกจากนี้เรายังสามารถทำให้คำสั่งประเภทปลอดภัยโดยใช้Project.extractและrunTask

commands += Command.command("cmd") { state =>
  val log = state.log
  import Project._
  log.info(x.value.toString)
  val (_, resultBefore) = extract(state).runTask(f, state)
  log.info(resultBefore.toString)
  val mutatedState = extract(state).appendWithSession(Seq(x := 41), state)
  val (_, resultAfter) = extract(mutatedState).runTask(f, mutatedState)
  log.info(resultAfter.toString)
  mutatedState
}

ขอบเขต

ขอบเขตเข้ามามีบทบาทเมื่อเราพยายามตอบคำถามประเภทต่อไปนี้

  • จะกำหนดงานเพียงครั้งเดียวและทำให้พร้อมใช้งานสำหรับโครงการย่อยทั้งหมดในการสร้างหลายโครงการได้อย่างไร
  • จะหลีกเลี่ยงการพึ่งพาการทดสอบบนคลาสพา ธ หลักได้อย่างไร?

sbt มีพื้นที่กำหนดขอบเขตหลายแกนซึ่งสามารถนำทางได้โดยใช้ไวยากรณ์สแลชตัวอย่างเช่น

show  root   /  Compile         /  compile   /   scalacOptions
        |        |                  |             |
     project    configuration      task          key

โดยส่วนตัวฉันไม่ค่อยพบว่าตัวเองต้องกังวลเรื่องขอบเขต บางครั้งฉันต้องการรวบรวมเพียงแค่แหล่งทดสอบ

Test/compile

หรืออาจดำเนินการงานเฉพาะจากโปรเจ็กต์ย่อยเฉพาะโดยไม่ต้องไปที่โปรเจ็กต์นั้นก่อนด้วย project subprojB

subprojB/Test/compile

ฉันคิดว่ากฎง่ายๆต่อไปนี้ช่วยหลีกเลี่ยงการกำหนดขอบเขตภาวะแทรกซ้อน

  • ไม่มีbuild.sbtไฟล์หลายไฟล์ แต่มีเพียงมาสเตอร์เดียวภายใต้โปรเจ็กต์รูทที่ควบคุมโปรเจ็กต์ย่อยอื่น ๆ ทั้งหมด
  • แบ่งปันงานผ่านปลั๊กอินอัตโนมัติ
  • แยกการตั้งค่าทั่วไปออกเป็น Scala ธรรมดาvalและเพิ่มลงในแต่ละโครงการย่อยอย่างชัดเจน

การสร้างหลายโครงการ

แทนที่จะเป็นไฟล์ build.sbt หลายไฟล์สำหรับแต่ละโปรเจ็กต์ย่อย

.
├── README.md
├── build.sbt                  // OK
├── multi1
│   ├── build.sbt              // NOK
│   ├── src
│   └── target
├── multi2
│   ├── build.sbt              // NOK
│   ├── src
│   └── target
├── project                    // this is the meta-project
│   ├── FooPlugin.scala        // custom auto plugin
│   ├── build.properties       // version of sbt and hence Scala for meta-project
│   ├── build.sbt              // OK - this is actually for meta-project 
│   ├── plugins.sbt            // OK
│   ├── project
│   └── target
└── target

มีนายคนเดียวที่build.sbtจะปกครองพวกเขาทั้งหมด

.
├── README.md
├── build.sbt                  // single build.sbt to rule theme all
├── common
│   ├── src
│   └── target
├── multi1
│   ├── src
│   └── target
├── multi2
│   ├── src
│   └── target
├── project
│   ├── FooPlugin.scala
│   ├── build.properties
│   ├── build.sbt
│   ├── plugins.sbt
│   ├── project
│   └── target
└── target

มีแนวทางปฏิบัติทั่วไปในการแยกการตั้งค่าทั่วไปในการสร้างหลายโครงการ

กำหนดลำดับของการตั้งค่าทั่วไปใน val และเพิ่มเข้าไปในแต่ละโครงการ แนวคิดน้อยกว่าที่จะเรียนรู้ด้วยวิธีนั้น

ตัวอย่างเช่น

lazy val commonSettings = Seq(
  scalacOptions := Seq(
    "-Xfatal-warnings",
    ...
  ),
  publishArtifact := true,
  ...
)

lazy val root = project
  .in(file("."))
  .settings(settings)
  .aggregate(
    multi1,
    multi2
  )
lazy val multi1 = (project in file("multi1")).settings(commonSettings)
lazy val multi2 = (project in file("multi2")).settings(commonSettings)

การนำทางโครงการ

projects         // list all projects
project multi1   // change to particular project

ปลั๊กอิน

project/โปรดจำไว้ว่าการสร้างความหมายเป็นโครงการที่สกาล่าที่เหมาะสมที่อยู่ภายใต้ นี่คือที่ที่เรากำหนดปลั๊กอินโดยการสร้าง.scalaไฟล์

.                          // directory of the (main) proper project
├── project
│   ├── FooPlugin.scala    // auto plugin
│   ├── build.properties   // version of sbt library and indirectly Scala used for the plugin
│   ├── build.sbt          // build definition of the plugin
│   ├── plugins.sbt        // these are plugins for the main (proper) project, not the meta project
│   ├── project            // the turtle supporting this turtle
│   └── target             // compiled binaries of the plugin

นี่คือปลั๊กอินอัตโนมัติขั้นต่ำที่อยู่ภายใต้project/FooPlugin.scala

object FooPlugin extends AutoPlugin {
  object autoImport {
      val barTask = taskKey[Unit]("")
  }

  import autoImport._

  override def requires = plugins.JvmPlugin  // avoids having to call enablePlugin explicitly
  override def trigger = allRequirements

  override lazy val projectSettings = Seq(
    scalacOptions ++= Seq("-Xfatal-warnings"),
    barTask := { println("hello task") },
    commands += Command.command("cmd") { state =>
      """eval println("hello command")""" :: state
    }   
  )
}

การแทนที่

override def requires = plugins.JvmPlugin

ได้อย่างมีประสิทธิภาพควรเปิดใช้งานปลั๊กอินสำหรับโครงการย่อยทั้งหมดโดยไม่ต้องเรียกอย่างชัดเจนในenablePluginbuild.sbt

IntelliJ และ sbt

โปรดเปิดใช้งานการตั้งค่าต่อไปนี้ (ซึ่งควรเปิดใช้งานโดยค่าเริ่มต้น )

use sbt shell

ภายใต้

Preferences | Build, Execution, Deployment | sbt | sbt projects

การอ้างอิงที่สำคัญ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.