Skip to content

Instantly share code, notes, and snippets.

@ches
Last active February 24, 2023 23:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ches/8f014efac55f61bbd39a14546bb8943f to your computer and use it in GitHub Desktop.
Save ches/8f014efac55f61bbd39a14546bb8943f to your computer and use it in GitHub Desktop.
sbt mini-plugin for a tool needing compile-time only Maven dependencies, in this case custom WartRemover extension lints
import sbt._, Keys._
import wartremover.WartRemover, WartRemover.autoImport.wartremoverClasspaths
trait CustomWartsKeys {
lazy val customWartsVersion = settingKey[String]("Version of the my-warts library dependency")
}
/** sbt plugin to integrate a package of custom WartRemover extension lints into a project's build.
*
* The purpose of an sbt plugin for this is to solve a problem with WartRemover's [[$custom-warts
* official guidance for custom warts]]: it uses `libraryDependencies` in the `Compile` classpath,
* which means that a published library project that uses custom warts in this way will have a POM
* runtime dependency on the custom warts and thus transitively on wartremover and scala-compiler
* as well. That is bloat, potential dependency headaches for users, and compilers in runtime could
* be exploited.
*
* This implementation follows [[$tool-deps the pattern for compile time-only tools]] in a
* dedicated configuration whose dependencies won't be on Runtime classpath during dev, nor
* included in POM.
*
* @define custom-warts https://web.archive.org/web/20220823060857/https://www.wartremover.org/doc/adding-your-own-warts.html
* @define tool-deps https://www.scala-sbt.org/1.x/docs/Faq.html#How+should+I+express+a+dependency+on+an+outside+tool+such+as+proguard%3F
*/
object CustomWartsPlugin extends AutoPlugin {
override def requires = WartRemover
override def trigger = allRequirements
object autoImport extends CustomWartsKeys {
val CustomWarts: Configuration = config("custom-warts").hide
}
import autoImport._
override def globalSettings: Seq[Def.Setting[_]] = List(
customWartsVersion := "0.1.0", // Use BuildInfo from your my-warts lib
)
override def projectConfigurations: Seq[Configuration] = Seq(CustomWarts)
override def projectSettings: Seq[Def.Setting[_]] = List(
libraryDependencies += "net.example" %% "my-warts" % customWartsVersion.value % CustomWarts,
// Construct config's classpath from dependencies scoped to it
CustomWarts / managedClasspath := {
val artifactTypes: Set[String] = (CustomWarts / classpathTypes).value
Classpaths.managedJars(CustomWarts, artifactTypes, update.value)
},
wartremoverClasspaths ++= (CustomWarts / managedClasspath).value.files.map(_.toURI.toString).toList
)
}
@ches
Copy link
Author

ches commented Feb 24, 2023

For WartRemover you should use the built-in wartremoverDependencies setting instead of this.

I discovered the existence of that currently undocumented setting, which solves the problem described in the Gist, approximately two minutes after I made my plugin work… 😩

This pattern is reusable though as noted in the sbt FAQ link. Plugins don't need to be published, you can just drop the .scala file in project/.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment