Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally include provided scope dependencies with sbt and the universal plugin

I have a project that is a scala & scalatra API. I have two distributables that I build with sbt-native-packager -

  1. RPM & DEB install for on-premise installations
  2. heroku install for cloud installations

I'm currently using provided scope dependencies for items I need to manually manage with the RPM/DEB approach - database libraries that I cannot bundle and distribute due to license restrictions.

"mysql" % "mysql-connector-java" % "5.1.30" % "provided",
"com.microsoft" % "sqlserver.jdbc" % "4.1" % "provided",
..etc..

This has been working great. I use the universal plugin and the dist task, somewhat massaged, and then hook up some package build scripts.

Now I'm building the heroku install, and I don't know how to add back in those provided dependencies. I'm using the universal plugin and running the stage task. However, provided dependencies are being filtered out and I'd like to actually have them included when running stage, because I don't have the license restriction anymore in heroku.

Options that I think I have...

  1. Add a mapping to add back in provided scope dependencies during the stage task, but not during the dist task
  2. Drop provided scope entirely and manually exclude those dependencies from the packaging process during dist

I have some mappings already like this,

//add webapp dir to zip
mappings in Universal ++= directory("src/main/webapp")

//add db dir to zip, but move it into /lib/db instead of /db
mappings in Universal ++= (directory("src/main/resources/db").map{t => 
    (t._1, "lib/"+t._2)
  }
)

So I feel like I could probably figure out how to add/exclude if I really tried, but I'm having problems finding any documentation for this stuff. The examples here don't really help much, or I don't understand enough.

Thanks in advance!

like image 282
lucas Avatar asked Apr 25 '16 16:04

lucas


1 Answers

This is an interesting question. Some time ago we added the possibility to add arbitrary artifacts from the build classpath. ( See scaladocs too )

Option 1 could look like this. Note, that this doesn't work as I expect, as some mappings are not exactly picked up by native-packager:

// `show universal::stage:mappings` works properly
mappings in (Universal, stage) ++= {
  // calculate provided dependencies.
  val compileDep = (managedClasspath in Compile).value.toSet
  val runtimeDep = (managedClasspath in Runtime).value.toSet
  val provided = compileDep -- runtimeDep

  // create mappings
  fromClasspath(provided.toSeq, "jar", artifact => true)
}

I suggest a variation of option 1 by providing a build environment and decided, which mappings to add.

mappings in Universal ++= {
  // a build environment
  val env = buildEnv.value

  // calculate provided dependencies.
  val compileDep = (managedClasspath in Compile).value.toSet
  val runtimeDep = (managedClasspath in Runtime).value.toSet
  val provided = compileDep -- runtimeDep

  // create mappings, depending on build environment
  fromClasspath(provided.toSeq, "jar", _ => env == BuildEnv.Stage)
}

// add the dependencies to the start script as well
scriptClasspath ++= { /* similar code as above */}

What I would like to have done

I hoped to be able to to something like this

libraryDependencies += "com.google.guava" % "guava" % "19.0" % "provided"

mappings in (Universal, stage) ++= fromClasspath(
   (managedClasspath in Compile).value,
   "jar",
    artifact => artifact.scope == "provided"
)

Which wasn't possible, as the sbt Artifact has no way to access the ivy-scope (or I haven't found it).

Then I tried to do this

libraryDependencies += "com.google.guava" % "guava" % "19.0" % "provided" extra("heroku" -> "true")

mappings in (Universal, stage) ++= fromClasspath(
   (managedClasspath in Compile).value,
   "jar",
    artifact => artifact.extra.find(_ == "heroku")
      .map(_.toBoolean)
      .getOrElse(false)
)

which didn't worked either, as it seems that SBT doesn't pass the extra attributes from the ModuleID to the Artifact model. This left me with the solution described above.

like image 104
Muki Avatar answered Nov 18 '22 15:11

Muki