I have a submodule, that is compiled by invoking external command. I would like to include generated file into jar. So I wrote a task: ```
myTask := {
import sys.process.stringSeqToProcess
Seq("my", "command") !
}
unmanagedResourceDirectories in Compile += baseDirectory.value / "dist"
cleanFiles <+= baseDirectory { base => base / "dist" }
Keys.`package` <<= (Keys.`package` in Compile) dependsOn npmBuildTask.toTask
and when I invoke mySubmodule/package
task it works well. But when I invoke stage
task from sbt-native-packager
my task is ignored(is not executed).
There are a couple of options to solve this issue. I assume you want to add the dist
folder to your resulting application jar.
Your configuration doesn't work because stage
doesn't depend on package
. This results npmBuildTask
not being called.
The easiest way to fix this is by simply adding the npmBuildTask
as a dependency to stage
stage <<= stage dependsOn npmBuildTask.toTask
I wouldn't recommend this approach.
SBTs Resoure Generators are exactly defined for this purpose. An inline version could look like this
resourceGenerators in Compile += Def.task {
streams.value.log.info("running npm generator")
val base = (resourceManaged in Compile).value / "dist"
// A resource generator returns a Seq[File]. This is just an example
List("index.js", "test.js").map { file =>
IO.writeLines(base / file, List("var x = 1"))
base / file
}
}.taskValue
Or you could extract this in an AutoPlugin
to separate the "what" and "how.
Create project/NpmPlugin.scala
and add the following content
import sbt._
import sbt.Keys._
import sbt.plugins.JvmPlugin
object NpmPlugin extends AutoPlugin {
override val requires = JvmPlugin
override val trigger = AllRequirements
object autoImport {
val npmBuildTask = TaskKey[Seq[File]]("npm-build-task", "Runs npm and builds the application")
}
import autoImport._
override def projectSettings: Seq[Setting[_]] = Seq(
// define a custom target directory for npm
target in npmBuildTask := target.value / "npm",
// the actual build task
npmBuildTask := {
val npmSource = (target in npmBuildTask).value
val npmTarget = (resourceManaged in Compile).value / "dist"
// run npm here, which generates the necessary values
streams.value.log.info("running npm generator")
// move generated sources to target folder
IO.copyDirectory(npmSource, npmTarget)
// recursively get all files in the npmTarget
(npmTarget ***).get
},
resourceGenerators in Compile += npmBuildTask.taskValue
)
}
The build.sbt
will then look like this
name := "resource-gen-test"
version := "1.0"
enablePlugins(JavaAppPackaging)
Pretty clean :)
Last but not least you could use mappings
. They are the low level detail that drives a lot of the package-generation in sbt. The main idea of this solution is to
Seq[(File, String)]
)The advantage of this approach is that you are more flexible where you want to put your mappings.
import sbt._
import sbt.Keys._
import sbt.plugins.JvmPlugin
import com.typesafe.sbt.SbtNativePackager.Universal
import com.typesafe.sbt.SbtNativePackager.autoImport.NativePackagerHelper._
object NpmMappingsPlugin extends AutoPlugin {
override val requires = JvmPlugin
override val trigger = AllRequirements
object autoImport {
val npmBuildTask = TaskKey[Seq[(File, String)]]("npm-build-task", "Runs npm and builds the application")
}
import autoImport._
override def projectSettings: Seq[Setting[_]] = Seq(
// define a custom target directory for npm
target in npmBuildTask := target.value / "npm" / "dist",
// the actual build task
npmBuildTask := {
val npmTarget = (target in npmBuildTask).value
// run npm here, which generates the necessary values
streams.value.log.info("running npm generator")
// recursively get all files in the npmTarget
// contentOf(npmTarget) would skip the top-level-directory
directory(npmTarget)
},
// add npm resources to the generated jar
mappings in (Compile, packageBin) ++= npmBuildTask.value,
// add npm resources to resulting package
mappings in Universal ++= npmBuildTask.value
)
}
As you can see in this approach we can easily add the resulting files to different mappings.
However I only recommend this approach if you need this kind of flexibility as it requires a bit more knowledge of native-packager.