Search code examples
scalasbtsbt-plugin

How can I use sbt commands like clean and compile in my custom sbtplugin


I have the following, minimal, custom sbt plugin, where I'd like to implement (override) the standard clean command to do the same action as myTask.

package nigeleke.sbt

import sbt.*
import Keys.*

import scala.sys.process.*

object MyPlugin extends AutoPlugin {

  object autoImport {
    val myTask = taskKey[Unit]("Do something.")
  }

  import autoImport._

  override def requires = empty

  override def trigger = noTrigger

  override lazy val projectSettings = Seq(
    myTask := {
      println(s"project: ${thisProject.value.id}  plugins: ${thisProject.value.plugins}")
    },
    clean  := clean.dependsOn(myTask).value
  )

}

When I enable this plugin a client project, the myTask command works as expected. I would also like clean to invoke the same task, but no output is forthcoming:

> sbt
[info] welcome to sbt 1.8.2 (Eclipse Adoptium Java 17.0.6)
[info] loading global plugins from ... \.sbt\1.0\plugins
[info] loading settings for project sbt-example-client-build from plugins.sbt ...
[info] loading project definition from ... \sbt-example-client\project
[info] loading settings for project root from build.sbt,version.sbt ...
[info] set current project to sbt-example-client (in build file:/ ... /sbt-example-client/)
[info] sbt server started at local:sbt-server-c377c6b81cca5b432adb
[info] started sbt server
sbt:sbt-example-client> myTask
project: client  plugins: nigeleke.sbt.MyPlugin
[success] Total time: 0 s, completed 13 Apr 2023, 5:05:18 pm
sbt:sbt-example-client> clean
[success] Total time: 0 s, completed 13 Apr 2023, 5:05:21 pm
sbt:sbt-example-client>

I'm failing to find the correct syntax to invoke it, so any pointers are appreciated.


Solution

  • The thing is that clean is defined in a default built-in sbt plugin JvmPlugin

    https://github.com/sbt/sbt/blob/v1.8.2/main/src/main/scala/sbt/plugins/JvmPlugin.scala#L44-L46

    object JvmPlugin extends AutoPlugin {
      ...
      override lazy val projectSettings: Seq[Setting[_]] =
          ... ++
          Defaults.baseTasks ++    // (1)
          Defaults.compileBase ++  // (2)
          Defaults.defaultConfigs  // (3)
    }
    

    https://github.com/sbt/sbt/blob/v1.8.2/main/src/main/scala/sbt/Defaults.scala

    object Defaults extends BuildCommon { 
    
      lazy val baseTasks: Seq[Setting[_]] = projectTasks ++ ...
    
      lazy val projectTasks: Seq[Setting[_]] = Seq(
        ...
        clean :=                                 // (1)
          Def.taskDyn(Clean.task(resolvedScoped.value.scope, full = true)).value,
        ...
      )
    
      def compileBase = ... ++ Seq(
        ...
        clean := clean.dependsOn(cleanIvy).value, // (2)
        ...
      )
    
      lazy val defaultConfigs: Seq[Setting[_]] =
        inConfig(Compile)(compileSettings) ++ 
        inConfig(Test)(testSettings) ++ ...
    
      lazy val compileSettings: Seq[Setting[_]] = configSettings + ...
    
      lazy val testSettings: Seq[Setting[_]] = configSettings ++ ...
    
      lazy val configSettings: Seq[Setting[_]] = ... ++ configTasks ++ ...
    
      lazy val configTasks: Seq[Setting[_]] = ... ++ Seq(
        ...
        clean := (compileOutputs / clean).value,  // (3)
        ...
      )
    }
    

    You can't disable JvmPlugin because in such case sbt can't build a project.

    So in order to override clean you should make your plugin depend on JvmPlugin (otherwise JvmPlugin definition of clean overrides yours)

    object MyPlugin extends AutoPlugin {
      ...
    
      override def requires = JvmPlugin
    

    Then any of the following options works:

    clean := clean.dependsOn(myTask).value
    
    clean := {
      myTask.value
      clean.value
    }
    
    clean := Def.sequential(myTask, clean).value
    

    How can a default task be overridden from an AutoPlugin?

    https://www.scala-sbt.org/1.x/docs/Custom-Settings.html#Implementing+a+task

    SBT task dependsOn

    SBT dependsOn usage - migration from 0.12 to 0.13

    Per-project tasks in SBT

    Sbt run command for multiple projects

    Sequencing and overriding tasks in SBT

    How to exclude files in a custom clean task?

    How to execute clean task in dependent projects from a task?