Adding unmanagedJars to proguardLibraryJars in SBT - scala

I am attempting to use sbt with the following plugin https://github.com/siasia/xsbt-proguard-plugin. So far I haven't had any issues with the plugin, apart from the fact that proguard puts all of the unmanaged jars into the final min.jar file (causing problems with multiple jar's that conflict). Proguard has the proguardLibraryJars flag which allows you to specify jars for proguard to exclude
Essentially I want to add all of the jars from the TaskKey unamangedJars to proguardLibraryJars using the plugin, i.e. do something like this
lazy val proguard = proguardSettings ++ Seq(
proguardOptions := Seq(
keepMain("com.test.FacebookPostScheduler"),
keepMain("org.postgresql.Driver")
),
proguardLibraryJars <++= unmanagedClasspath
)
The problem is the above obviously doesn't compile at this line
proguardLibraryJars <++= unmanagedClasspath
with the
No implicit for Append.Values[Seq[java.io.File], sbt.Keys.Classpath] found
error.
How would you code what I am attempting to do using the latest SBT (0.11.3-2) using a Build.scala (not a build.sbt)

I have a public repository of an SBT plugin that manages to pass jars to proguard. It doesn't use the proguard plugin but the code might help you figure how to gather dependencies.
https://github.com/tlazaro/xsbt-plugin-deployer/blob/master/src/main/scala/Deployer.scala
Look for:
private def getDepsJars(project: ProjectRef, bs: BuildStructure) = forAllProjects(project, bs) {p =>
artifactPath in Compile in packageBin in p get bs.data
}
That might give you a way to get started. It gathers every needed jar which is what you usually want, not just the unmanaged.
Alternatively you can just use this plugin and maybe collaborate. The code is a bit sloppy, it not intended to be released yet. The plugin does some other neat stuff like compressing everything with pack200 into a jar and having a custom ClassLoader that loads the compressed classes from there in runtime.

adamw/xsbt-proguard-plugin, which is the successor of siasia/xsbt-proguard-plugin seems to have the very option:
By default Proguard will be instructed to include everything except classes from the Java runtime. To treat additional libraries as external (i.e. to add them to the list of -libraryjars passed to Proguard), do the following. Here comes the example how to select a module named "httpclient" from the library dependencies:
proguardLibraryJars <++= (update) map (_.select(module = moduleFilter(name = "httpclient")))

Related

Sbt Plugins vs Compiler Plugins

I am trying to understand why Adding sbt plugins in plugins.sbt in the project, works perfectly fine, but if I add the compiler plugins in that file it does not work ?
I thought any .sbt or .scala file in project, is made available for the build definition.
The only place where compiler plugins works is in the build.sbt. Hence i am confused as to why ?
In particular i am working with Kind-Projector
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.3" cross CrossVersion.full)
I see the following alias for the function
/** Adds `dependency` to `libraryDependencies` in the auto-compiler plugin configuration. */
def addCompilerPlugin(dependency: ModuleID): Setting[Seq[ModuleID]] =
libraryDependencies += compilerPlugin(dependency)
Hence just trying to understanding, what makes it that it can only be added to the build.sbt and not plugins.sbt in project/
Remeber sbt is recursive.
.sbt define things that are available in the current layer.
.scala files define thins that will be available in the next layer.
Adding an sbt plugin in project/bar.sbt is adding that plugin to the meta layer, as such the meta-layer that compiles the sbt you are using to compile your project adds those plugins to the next sbt layer.
So if you add a compiler plugin in project/foo.sbt then you are adding that compiler plugin to the compiler used to compile the project (meta) layer of sbt, but it will not be available in the current layer of sbt. That is the reason why compiler plugins are added in the build.sbt file, so they are added to the compiler used to compile your code.

How to use sbt-assembly in sbt-plugin?

I am writing an sbt-plugin to abstract away some boilerplate.
Let's call it sbt-redux
then there is one more plugin sbt-assembly.
In this quest, my plugin(sbt-redux) needs to know about where the project ( Project which is using sbt-redux ) will create Uber jar using sbt-assembly and what will be the name of jar.
I tried adding sbt-assembly in plugins of sbt-redux, but for the obvious reasons it will not add dependencies in my src folder as it has limitations only in build.sbt.
I tried using .dependsOn(assembly) but still no luck.
So, How can I use other plugins into src?
P.S. Please let me know if the question is not clear.
There I found a solution and it is working for me.
If you want to read assembly's settings, you have to make sure your plugins depend on it. In your build.sbt file, you can add:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7").
And then in your AutoPlugin implementation, you should override requires this way:
override def requires = super.requires && sbtassembly.AssemblyPlugin
After that, you'll have access to assembly settings and tasks.
Thanks to gpoirier.
To expand on the correct answer from #arglee, when you have a multi-module project, the addSbtPlugin line needs to be part of the .settings entries for the module that needs the dependency, like:
lazy val mod = (project in file("mod"))
.settings(
...,
addSbtPlugin( ... ),
libraryDependencies ++= Seq( ... ),
...
)

SBT plugin to forbid use of auto-imported dependencies

I have this line of code in my build.sbt file:
libraryDependencies ++= Seq("com.foo" %% "lib" % "1.2.3")
Imagine that this library depends on "com.bar.lib" lib. Now in my code I can import com.bar.lib._ and it'll work. But I don't want this to compile, so maybe there is SBT plugin out there just for this purpose?
One of libraries I'm using depends on old cats version. I spent really long time to understand why mapN method not works... I just never imported a newer version of cats in the subproject.
SBT offers the intransitive and exclude features to deal with issues like this, as #earldouglas points out. See: https://www.scala-sbt.org/1.x/docs/Library-Management.html
You replied:
I tried to do so, but intransitive() don't import transitive dependencies (so I need to import all of them by hand to make it compile).
Yes, that is what it is for
What I want is something that will warn me about using libraries not directly imported in SBT file.
So you want transitive dependencies on your classpath, but you want the compiler to reject uses of transitive classes in your project code while allowing them in library code?
That is not a sensible approach: at runtime, these transitive dependencies will be on the classpath. The JVM classpath does not distinguish between different kinds of dependencies; such distinction only exists in SBT at build time.
You would be much better served by either
including a newer version of the cats library, overriding the transitive dep or
excluding the transitively included cats library, if it is broken.
However, I think you probably could achieve what you want by setting different dependencies at different build stages:
at Compile stage, include the dependency with intransitive. Your code should compile against your direct dependencies, but fail if you referenced any transitive dependencies
at Runtime stage, include the dependency with its transitive deps
the SBT code might look like this (untested):
(libraryDependencies in Compile) ++= Seq("com.foo" %% "lib" % "1.2.3" intransitive())
(libraryDependencies in Runtime) ++= Seq("com.foo" %% "lib" % "1.2.3")

Add an extra lib folder dependency to build sbt in a lift project

I have an external java project that my lift project depends on. I have been able to add the dependency to the classes in that project by adding the following line to my sbt:
unmanagedClasspath in Compile += file("[Path to My Project]/classes")
But this project also has a lib folder with a set of jars that it references and I cannot figure out what the correct syntax should be to add these dependencies. Have tried the following but it does not work:
unmanagedJars in Compile += file("[Path to My Project]/lib/*.jar")
Any pointers greatly appreciated
Regards
Des
You can use sbt's Path API to get all jars in your directory.
Edit: a shorter version using .classpath:
unmanagedJars in Compile ++=
(file("[Path to My Project]/lib/") * "*.jar").classpath
which is more or less equivalent to:
unmanagedJars in Compile ++=
Attributed.blankSeq((file("[Path to My Project]/lib/") * "*.jar").get)
(Attributed is necessary because unmanagedJars is a setting of type Seq[Attributed[File]] and not Seq[File])

How to get Intellij to use dependencies from SBT scala

I am trying to figure out how idea will recognize thrid party dependencies when using SBT. When I use the sbt plugin gen-idea it seems to download all the necessary dependencies which get put into my ~/.ivy/ directory as expected. How can intellij use these deps?
EDIT:
One thing I noticed is if I make a new idea project instead of just a module then this works? Any idea why this would be? I would like to be able to have multiple sbt modules in the same project.
The sbt-idea plugin works with multi-module sbt project. We have been using it since somewhere around sbt-0.10.0, and currently are at sbt-0.11.2. It seems like you have the dependency part of the build file set up ok, so here's an example of how we do the project setup from a full specification Build.scala file:
object Vcaf extends Build {
import Resolvers._
import Dependencies._
import BuildSettings._
lazy val vcafDb = Project(
id = "vcaf-db",
base = file("./vcaf-db"),
dependencies = Seq(),
settings = buildSettings ++ /* proguard */ SbtOneJar.oneJarSettings ++ Seq(libraryDependencies := dbDeps, resolvers := cseResolvers)
)
lazy val vcaf = Project(
"vcaf",
file("."),
dependencies = Seq(vcafDb),
aggregate = Seq(vcafDb),
settings = buildSettings ++ Seq(libraryDependencies := vcafDeps, resolvers := cseResolvers) ++ webSettings
)
}
In the example, the vcaf-db project is in the a folder within the vcaf project folder. The vcaf-db project does not have it's own build.sbt or Build.scala file. You'll notice that we are specifying libraryDependencies for each project, which may or may not be your missing link.
As ChrisJamesC mentioned, you need to do a "reload" from within SBT (or exit sbt and come back in) to pick up changes to your build definition. After the project is reloaded, you should be able to do a "gen-idea no-classifiers no-sbt-classifiers" and get an intellij project that has the main project, modules, and library access as defined in the build file.
Hope it helps!
If you want multiple SBT modules in one IDEA project, you can use sbt multi-project builds (aka subprojects). Just create a master project that refers to the modules as sub-projects, then run gen-idea on the master. To specify dependencies among the modules you have to use Build.scala (not build.sbt), as in jxstanford's answer or like this:
lazy val foo = Project(id = "foo", base = file("foo"))
lazy val bar = Project(id = "bar", base = file("bar")) dependsOn(foo)
One level of subprojects works fine (with the dependencies correctly reflected in the resulting IDEA project), but nested subprojects don't seem to work. Also, it seems to be an sbt restriction that the subprojects must live in subdirectories of the master project (i.e., file("../foo") is not allowed).
See also How to manage multiple interdependent modules with SBT and IntelliJ IDEA?.