Helps you find:
Undeclared dependencies
// in your build.sbt ...
libraryDependencies +=
"org.typelevel" %% "cats-effect" % "1.0.0"
// somewhere in your src/main/scala ...
import cats.effect.IO
import cats.data.NonEmptyList
val foo: IO[Unit] = IO {
println(NonEmptyList.of(1, 2, 3))
}
Unused dependencies
// in your build.sbt ...
libraryDependencies +=
"org.scalatest" %% "scalatest" % "3.0.5"
declared
actual
undeclared compile dependencies
declared
actual
unused compile dependencies
libraryDependencies
How do we work out what libraries our project actually needs in order to compile?
First idea: bytecode analysis + jar traversal
import cats.data._
object Foo {
val valid = Validated.valid("hello")
}
Constant pool: ... #23 = Utf8 cats/data/Validated$ #24 = Class #23 // cats/data/Validated$ ...
First idea: bytecode analysis + jar traversal
First idea: bytecode analysis + jar traversal
TERRIBLE IDEA
sbt:example> inspect compile
[info] Task: xsbti.compile.CompileAnalysis
[info] Description:
[info] Compiles sources.
compile analysis? 🤔
printActualDeps := {
compile.in(Compile).value
.asInstanceOf[sbt.internal.inc.Analysis]
.relations
.allLibraryDeps
.foreach(println)
}
sbt:example> printActualDeps /Users/chris/.ivy2/cache/org.typelevel/cats-effect_2.12/jars/cats-effect_2.12-0.10.1.jar /Users/chris/.ivy2/cache/org.typelevel/cats-core_2.12/jars/cats-core_2.12-1.2.0.jar /Users/chris/.ivy2/cache/com.chuusai/shapeless_2.12/bundles/shapeless_2.12-2.3.3.jar /Users/chris/.sbt/boot/scala-2.12.6/lib/scala-library.jar /Users/chris/.ivy2/cache/org.http4s/http4s-blaze-server_2.12/jars/http4s-blaze-server_2.12-0.18.16.jar /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/rt.jar /Users/chris/.ivy2/cache/com.google.guava/guava/bundles/guava-26.0-jre.jar
// no need to add anything to project/plugins.sbt
enablePlugins(SbtPlugin)
scalaVersion := "2.12.8"
organization := "chris"
libraryDependencies ++= Seq(
// whatever libraries you need ...
)
// src/main/scala/hello/HelloPlugin.scala
package hello
import sbt._
object HelloPlugin extends AutoPlugin {
object autoImport {
val sayHello = taskKey[Unit]("say hello")
}
// if you want your plugin to be enable automatically
override def trigger = allRequirements
override def projectSettings = Seq(
autoImport.sayHello := {
println("Hello world!")
}
)
}
Publish your plugin locally:
Add it to another sbt project:
$ sbt publishLocal
// project/plugins.sbt
addSbtPlugin("chris" % "sbt-hello-world" % "0.1.0-SNAPSHOT")
And try it out:
sbt:sbt-example> sayHello Hello world!
... is really easy!
// project/plugins.sbt
addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.4")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.9")
// build.sbt
organization := "com.foo.bar"
description := "my amazing plugin"
licenses += ("Apache-2.0", url("..."))
publishMavenStyle := false
bintrayRepository := "my-sbt-plugins"
bintrayOrganization in bintray := None
sbt 0.13.x and 1.x are very similar,
but lots of classes got moved around
Type aliases in sbt version-specific package objects
package object explicitdeps {
type Logger = sbt.util.Logger
type ModuleID = sbt.librarymanagement.ModuleID
type Binary = sbt.librarymanagement.Binary
type Analysis = sbt.internal.inc.Analysis
...
}
package object explicitdeps {
type Logger = sbt.Logger
type ModuleID = sbt.ModuleID
type Binary = sbt.CrossVersion.Binary
type Analysis = sbt.inc.Analysis
...
}
src/main/scala-sbt-1.0/...
src/main/scala-sbt-0.13/...
everything else goes
in src/main/scala
Shared code must be valid in both 2.12.x and 2.10.x
package object explicitdeps {
...
implicit class NodeSeqOps(nodeSeq: scala.xml.NodeSeq) {
def \@(attributeName: String): String =
(nodeSeq \ ("@" + attributeName)).text
}
}
src/main/scala-sbt-0.13/...
Add shims in version-specific package object:
LambdAle CFP opens soon!