LMnet
LMnet

Reputation: 630

Get resource file from dependency in sbt

I have a sbt-web project with some webjar and common jar dependencies. I want to get resource file from one of my jar dependency and use it in concatenation task. But I don't know how refer to the resource inside dependent jar in my build.sbt.

Upvotes: 4

Views: 2882

Answers (3)

VolatileRig
VolatileRig

Reputation: 2847

The other answers didn't work for me, I think because they're on older versions of SBT. I've been banging my head against this for a while but I finally got something to work. Here's the full script that will do this resource copying before an arbitrary Compile task (mine is guardrail) on SBT v1.5.5:

lazy val myDep = "my-dep-project"
lazy val myFilename = "someDir/myFile.yaml"

val copyResourcesFromJars =
  TaskKey[Unit]("copyResourcesFromJars", "Copy specific resources to be used by this project")

copyResourcesFromJars := {
  def copyResourceFromJar(classpathEntry: Attributed[File], jarName: String, resourceName: String): Unit = {
    classpathEntry.get(artifact.key) match {
      case Some(entryArtifact) =>
        // searching artifact
        if (entryArtifact.name.startsWith(jarName)) {
          // unpack artifact's jar to tmp directory
          val jarFile = classpathEntry.data
          IO.withTemporaryDirectory { tmpDir =>
            IO.unzip(jarFile, tmpDir)
            // copy to project's target directory
            // Instead of copying you can do any other stuff here
            IO.copyFile(
              tmpDir / resourceName,
              baseDirectory.value / s"target/$resourceName"
            )
          }
        }
      case _ =>
    }
  }

  (Compile / dependencyClasspath).value.foreach(entry =>
    copyResourceFromJar(entry, myDep, myFilename)
  )
}

Compile / guardrail := (Compile / guardrail dependsOn copyResourcesFromJars).value

(project in file(".")).settings(
  ...
  Compile / guardrailTasks := List(
    ScalaServer(
      baseDirectory.value / s"target/$myFilename",
      pkg = "my.pkg.guardrail"
    )
  )
)

Upvotes: 1

LMnet
LMnet

Reputation: 630

I finally find the solution with this docs. The main idea is to find the right jar in the classpath dependencies, unzip it to temprorary folder and do what you need with this files. In my case I copy it to my target directory and use it in concatenation task.

I end up with this code:

def copyResourceFromJar(classpathEntry: Attributed[File], jarName: String, resourceName: String) = {
  classpathEntry.get(artifact.key) match {
    case Some(entryArtifact) => {
      // searching artifact
      if (entryArtifact.name.startsWith(jarName)) {
        // unpack artifact's jar to tmp directory
        val jarFile = classpathEntry.data
        IO.withTemporaryDirectory { tmpDir =>
          IO.unzip(jarFile, tmpDir)
          // copy to project's target directory
          // Instead of copying you can do any other stuff here 
          IO.copyFile(
            tmpDir / resourceName,
            (WebKeys.webJarsDirectory in Assets).value / resourceName
          )
        }
      }
    }
    case _ =>
  }
}
for(entry <- (dependencyClasspath in Compile).value) yield {
  copyResourceFromJar(entry, "firstJar", "firstFile.js")
  copyResourceFromJar(entry, "secondJar", "some/path/secondFile.js")
}

This code should be placed in the task. For example:

val copyResourcesFromJar = TaskKey[Unit]("copyResourcesFromJar", "Copy resources from jar dependencies")
copyResourcesFromJar := {
  //your task code here
}
copyResourcesFromJar <<= copyResourcesFromJar dependsOn (dependencyClasspath in Compile)

And don't forget to add this task as dependcy to your build task. In my case it looks like this:

concat <<= concat dependsOn copyResourcesFromJar

Upvotes: 9

Andreas Neumann
Andreas Neumann

Reputation: 10894

Normally getResourceAsStream works:

getClass.getResourceAsStream("/path/to/resource/in/jar")

It is important to read the resource as a Stream getClass.getResource will not work.

Upvotes: 0

Related Questions