Search code examples
scalasbtsbt-assemblysbt-plugin

How to make a SBT task depend on a module defined in the same SBT project?


I have module A and module B in a multi-module SBT project. I want to write a resource generator task for module B that invokes code from module A. One way to do this is to pull all the code from module A under project/ but that is unfeasible as module A is massive and I would like to keep it where it is (see https://stackoverflow.com/a/47323703/471136). How do I do this in SBT?

Secondly, is it possible to get rid of module B altogether i.e. I want the resource generator task for module A actually invoke code from module A but module A is a root module and does not live in SBT's project?

Note: This question is not a duplicate of this: Defining sbt task that invokes method from project code? since that one resolves to moving the code into SBT's project which is specifically something I am seeking to avoid here.


Solution

  • Based on @0__ answer above, here is my simplified version:

      /**
        * Util to run the main of a sub-module from within SBT
        *
        * @param cmd The cmd to run with the main class with args (if any)
        * @param module The sub-module
        */
      def runModuleMain(cmd: String, module: Reference) = Def.task {
        val log = streams.value.log
        log.info(s"Running $cmd ...")
        val classPath = (fullClasspath in Runtime in module).value.files
        val opt = ForkOptions(bootJars = classPath, outputStrategy = Some(LoggedOutput(log)))
        val res = Fork.scala(config = opt, arguments = cmd.split(' '))
        require(res == 0, s"$cmd exited with code $res")
      }
    

    You can then invoke it as:

    resourceGenerators in Compile += Def.taskDyn {
      val dest = (resourceManaged in Compile).value
      IO.createDirectory(dest)
      runModuleMain(
        cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ...",
        module = $referenceToModule // submodule containing com.mycompany.foo.ResourceGen
      ).taskValue
      dest.listFiles()
    }
    

    There is one more way to do this:

    resourceGenerators in Compile += Def.taskDyn {
      val dest = (resourceManaged in Compile).value
      IO.createDirectory(dest)      
      Def.task {
        val cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ..."        
        (runMain in Compile).toTask(" " + $cmd).value 
        dest.listFiles()
      }
    }.taskValue