Skip to content

Commit 6692a5e

Browse files
committed
Assembly: Added keepRules which runs on the final jar
This allows users of this lib/plugin to keep only files from libraries they use in their project without specifying them in zap
1 parent 9b59275 commit 6692a5e

File tree

11 files changed

+225
-19
lines changed

11 files changed

+225
-19
lines changed

src/main/contraband/AssemblyOption.contra

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type AssemblyOption {
2626

2727
shadeRules: sbtassembly.Assembly.SeqShadeRules! = raw"sbtassembly.Assembly.defaultShadeRules" @since("0.15.0")
2828

29+
keepRules: sbtassembly.Assembly.SeqString! = raw"sbtassembly.Assembly.defaultKeepRules" @since("2.0.1")
30+
2931
scalaVersion: String! = "" @since("0.15.0")
3032

3133
level: sbt.Level.Value! = raw"sbt.Level.Info" @since("0.15.0")

src/main/scala/sbtassembly/Assembly.scala

+50-12
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,29 @@ package sbtassembly
33
import com.eed3si9n.jarjarabrams._
44
import sbt.Def.Initialize
55
import sbt.Keys._
6-
import sbt.Package.{ manifestFormat, JarManifest, MainClass, ManifestAttributes }
6+
import sbt.Package.{JarManifest, MainClass, ManifestAttributes, manifestFormat}
77
import sbt.internal.util.HListFormats._
88
import sbt.internal.util.HNil
99
import sbt.internal.util.Types.:+:
10-
import sbt.io.{ DirectoryFilter => _, IO => _, Path => _, Using }
10+
import sbt.io.{Using, DirectoryFilter => _, IO => _, Path => _}
1111
import sbt.util.FileInfo.lastModified
12-
import sbt.util.Tracked.{ inputChanged, lastOutput }
13-
import sbt.util.{ FilesInfo, Level, ModifiedFileInfo }
14-
import sbt.{ File, Logger, _ }
12+
import sbt.util.Tracked.{inputChanged, lastOutput}
13+
import sbt.util.{FilesInfo, Level, ModifiedFileInfo}
14+
import sbt.{File, Logger, _}
1515
import sbt.Tags.Tag
1616
import CacheImplicits._
17-
import sbtassembly.AssemblyPlugin.autoImport.{ Assembly => _, _ }
17+
import com.eed3si9n.jarjar.util.EntryStruct
18+
import com.eed3si9n.jarjar.{JJProcessor, Keep}
19+
import sbtassembly.AssemblyPlugin.autoImport.{Assembly => _, _}
1820
import sbtassembly.PluginCompat.ClasspathUtilities
1921

2022
import java.io._
2123
import java.net.URI
22-
import java.nio.file.attribute.{ BasicFileAttributeView, FileTime, PosixFilePermission }
23-
import java.nio.file.{ Path, _ }
24+
import java.nio.file.attribute.{BasicFileAttributeView, FileTime, PosixFilePermission}
25+
import java.nio.file.{Path, _}
2426
import java.security.MessageDigest
2527
import java.time.Instant
26-
import java.util.jar.{ Attributes => JAttributes, JarFile, Manifest => JManifest }
28+
import java.util.jar.{JarFile, Attributes => JAttributes, Manifest => JManifest}
2729
import scala.annotation.tailrec
2830
import scala.collection.GenSeq
2931
import scala.collection.JavaConverters._
@@ -36,7 +38,8 @@ object Assembly {
3638
type SeqShadeRules = Seq[com.eed3si9n.jarjarabrams.ShadeRule]
3739
type LazyInputStream = () => InputStream
3840

39-
val defaultShadeRules: Seq[com.eed3si9n.jarjarabrams.ShadeRule] = Nil
41+
val defaultShadeRules: SeqShadeRules = Nil
42+
val defaultKeepRules: SeqString = Nil
4043
val newLine: String = "\n"
4144
val indent: String = " " * 2
4245
val newLineIndented: String = newLine + indent
@@ -336,11 +339,15 @@ object Assembly {
336339
timed(Level.Debug, "Finding remaining conflicts that were not merged") {
337340
reportConflictsMissedByTheMerge(mergedEntries, log)
338341
}
342+
val finalEntries = timed(Level.Debug, "Applying keep rules") {
343+
val entries = mergedEntries.flatMap(_.entries)
344+
if (ao.keepRules.isEmpty) entries else keepShader(ao.keepRules, log, entries)
345+
}
339346
val jarEntriesToWrite = timed(Level.Debug, "Sort/Parallelize merged entries") {
340347
if (ao.repeatableBuild) // we need the jars in a specific order to have a consistent hash
341-
mergedEntries.flatMap(_.entries).seq.sortBy(_.target)
348+
finalEntries.seq.sortBy(_.target)
342349
else // we actually gain performance when creating the jar in parallel, but we won't have a consistent hash
343-
mergedEntries.flatMap(_.entries).par
350+
finalEntries.par
344351
}
345352
val localTime = timestamp
346353
.map(t => t - java.util.TimeZone.getDefault.getOffset(t))
@@ -565,6 +572,37 @@ object Assembly {
565572
}
566573
}
567574

575+
private[sbtassembly] def keepShader(keepRules: SeqString, log: Logger, entries: Seq[JarEntry]): Seq[JarEntry] = {
576+
val jjRules = keepRules.map(pattern => {
577+
val jRule = new Keep()
578+
jRule.setPattern(pattern)
579+
jRule
580+
})
581+
582+
val proc = new JJProcessor(jjRules, true, true, null)
583+
584+
val entryStructs = entries.map({ entry =>
585+
val stream = entry.stream()
586+
val entryStruct = new EntryStruct()
587+
val mapping = entry.target
588+
entryStruct.name = if (mapping.contains('\\')) mapping.replace('\\', '/') else mapping
589+
entryStruct.data = Streamable.bytes(stream)
590+
entryStruct.time = -1
591+
entryStruct.skipTransform = false
592+
stream.close()
593+
entryStruct
594+
})
595+
596+
val itemsToExclude = proc.getExcludes
597+
log.info(s"items to exclude: ${itemsToExclude.size}")
598+
entryStructs.filterNot(entry => itemsToExclude.contains(entry.name))
599+
.map(entryStruct => {
600+
val mapping = entryStruct.name
601+
val name = if (mapping.contains('/')) mapping.replace('/', '\\') else mapping
602+
JarEntry(name, () => new ByteArrayInputStream(entryStruct.data))
603+
})
604+
}
605+
568606
private[sbtassembly] def createManifest(po: Seq[PackageOption], log: Logger): (JManifest, Option[Long]) = {
569607
import scala.language.reflectiveCalls
570608
val manifest = new JManifest

src/main/scala/sbtassembly/AssemblyKeys.scala

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ trait AssemblyKeys {
1616
lazy val assemblyExcludedJars = taskKey[Classpath]("list of excluded jars")
1717
lazy val assemblyMergeStrategy = settingKey[String => MergeStrategy]("mapping from archive member path to merge strategy")
1818
lazy val assemblyShadeRules = settingKey[Seq[jarjarabrams.ShadeRule]]("shading rules backed by jarjar")
19+
lazy val assemblyKeepRules = settingKey[Seq[String]]("Keep rules backed by jarjar to run on the final assembled JAR")
1920
lazy val assemblyAppendContentHash = settingKey[Boolean]("Appends SHA-1 fingerprint to the assembly file name")
2021
lazy val assemblyMaxHashLength = settingKey[Int]("Length of SHA-1 fingerprint used for the assembly file name")
2122
lazy val assemblyCacheOutput = settingKey[Boolean]("Enables (true) or disables (false) cacheing the output if the content has not changed")

src/main/scala/sbtassembly/AssemblyOption.scala

+14-7
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,26 @@ final class AssemblyOption private (
2020
val prependShellScript: Option[sbtassembly.Assembly.SeqString],
2121
val maxHashLength: Option[Int],
2222
val shadeRules: sbtassembly.Assembly.SeqShadeRules,
23+
val keepRules: sbtassembly.Assembly.SeqString,
2324
val scalaVersion: String,
2425
val level: sbt.Level.Value) extends Serializable {
2526

26-
private def this() = this(true, true, true, Nil, true, sbtassembly.MergeStrategy.defaultMergeStrategy, true, false, None, None, sbtassembly.Assembly.defaultShadeRules, "", sbt.Level.Info)
27-
private def this(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: Option[sbtassembly.Assembly.SeqString], maxHashLength: Option[Int], shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value) = this(includeBin, includeScala, includeDependency, excludedJars, true, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, scalaVersion, level)
27+
private def this() = this(true, true, true, Nil, true, sbtassembly.MergeStrategy.defaultMergeStrategy, true, false, None, None, sbtassembly.Assembly.defaultShadeRules, sbtassembly.Assembly.defaultKeepRules, "", sbt.Level.Info)
28+
private def this(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: Option[sbtassembly.Assembly.SeqString], maxHashLength: Option[Int], shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value) = this(includeBin, includeScala, includeDependency, excludedJars, true, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, sbtassembly.Assembly.defaultKeepRules, scalaVersion, level)
29+
private def this(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, repeatableBuild: Boolean, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: Option[sbtassembly.Assembly.SeqString], maxHashLength: Option[Int], shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value) = this(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, sbtassembly.Assembly.defaultKeepRules, scalaVersion, level)
2830

2931
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
30-
case x: AssemblyOption => (this.includeBin == x.includeBin) && (this.includeScala == x.includeScala) && (this.includeDependency == x.includeDependency) && (this.excludedJars == x.excludedJars) && (this.repeatableBuild == x.repeatableBuild) && (this.mergeStrategy == x.mergeStrategy) && (this.cacheOutput == x.cacheOutput) && (this.appendContentHash == x.appendContentHash) && (this.prependShellScript == x.prependShellScript) && (this.maxHashLength == x.maxHashLength) && (this.shadeRules == x.shadeRules) && (this.scalaVersion == x.scalaVersion) && (this.level == x.level)
32+
case x: AssemblyOption => (this.includeBin == x.includeBin) && (this.includeScala == x.includeScala) && (this.includeDependency == x.includeDependency) && (this.excludedJars == x.excludedJars) && (this.repeatableBuild == x.repeatableBuild) && (this.mergeStrategy == x.mergeStrategy) && (this.cacheOutput == x.cacheOutput) && (this.appendContentHash == x.appendContentHash) && (this.prependShellScript == x.prependShellScript) && (this.maxHashLength == x.maxHashLength) && (this.shadeRules == x.shadeRules) && (this.keepRules == x.keepRules) && (this.scalaVersion == x.scalaVersion) && (this.level == x.level)
3133
case _ => false
3234
})
3335
override def hashCode: Int = {
34-
37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbtassembly.AssemblyOption".##) + includeBin.##) + includeScala.##) + includeDependency.##) + excludedJars.##) + repeatableBuild.##) + mergeStrategy.##) + cacheOutput.##) + appendContentHash.##) + prependShellScript.##) + maxHashLength.##) + shadeRules.##) + scalaVersion.##) + level.##)
36+
37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbtassembly.AssemblyOption".##) + includeBin.##) + includeScala.##) + includeDependency.##) + excludedJars.##) + repeatableBuild.##) + mergeStrategy.##) + cacheOutput.##) + appendContentHash.##) + prependShellScript.##) + maxHashLength.##) + shadeRules.##) + keepRules.##) + scalaVersion.##) + level.##)
3537
}
3638
override def toString: String = {
37-
"AssemblyOption(" + includeBin + ", " + includeScala + ", " + includeDependency + ", " + excludedJars + ", " + repeatableBuild + ", " + mergeStrategy + ", " + cacheOutput + ", " + appendContentHash + ", " + prependShellScript + ", " + maxHashLength + ", " + shadeRules + ", " + scalaVersion + ", " + level + ")"
39+
"AssemblyOption(" + includeBin + ", " + includeScala + ", " + includeDependency + ", " + excludedJars + ", " + repeatableBuild + ", " + mergeStrategy + ", " + cacheOutput + ", " + appendContentHash + ", " + prependShellScript + ", " + maxHashLength + ", " + shadeRules + ", " + keepRules + ", " + scalaVersion + ", " + level + ")"
3840
}
39-
private[this] def copy(includeBin: Boolean = includeBin, includeScala: Boolean = includeScala, includeDependency: Boolean = includeDependency, excludedJars: sbt.Keys.Classpath = excludedJars, repeatableBuild: Boolean = repeatableBuild, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy = mergeStrategy, cacheOutput: Boolean = cacheOutput, appendContentHash: Boolean = appendContentHash, prependShellScript: Option[sbtassembly.Assembly.SeqString] = prependShellScript, maxHashLength: Option[Int] = maxHashLength, shadeRules: sbtassembly.Assembly.SeqShadeRules = shadeRules, scalaVersion: String = scalaVersion, level: sbt.Level.Value = level): AssemblyOption = {
40-
new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, scalaVersion, level)
41+
private[this] def copy(includeBin: Boolean = includeBin, includeScala: Boolean = includeScala, includeDependency: Boolean = includeDependency, excludedJars: sbt.Keys.Classpath = excludedJars, repeatableBuild: Boolean = repeatableBuild, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy = mergeStrategy, cacheOutput: Boolean = cacheOutput, appendContentHash: Boolean = appendContentHash, prependShellScript: Option[sbtassembly.Assembly.SeqString] = prependShellScript, maxHashLength: Option[Int] = maxHashLength, shadeRules: sbtassembly.Assembly.SeqShadeRules = shadeRules, keepRules: sbtassembly.Assembly.SeqString = keepRules, scalaVersion: String = scalaVersion, level: sbt.Level.Value = level): AssemblyOption = {
42+
new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, keepRules, scalaVersion, level)
4143
}
4244
def withIncludeBin(includeBin: Boolean): AssemblyOption = {
4345
copy(includeBin = includeBin)
@@ -78,6 +80,9 @@ final class AssemblyOption private (
7880
def withShadeRules(shadeRules: sbtassembly.Assembly.SeqShadeRules): AssemblyOption = {
7981
copy(shadeRules = shadeRules)
8082
}
83+
def withKeepRules(keepRules: sbtassembly.Assembly.SeqString): AssemblyOption = {
84+
copy(keepRules = keepRules)
85+
}
8186
def withScalaVersion(scalaVersion: String): AssemblyOption = {
8287
copy(scalaVersion = scalaVersion)
8388
}
@@ -92,4 +97,6 @@ object AssemblyOption {
9297
def apply(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: sbtassembly.Assembly.SeqString, maxHashLength: Int, shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value): AssemblyOption = new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, mergeStrategy, cacheOutput, appendContentHash, Option(prependShellScript), Option(maxHashLength), shadeRules, scalaVersion, level)
9398
def apply(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, repeatableBuild: Boolean, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: Option[sbtassembly.Assembly.SeqString], maxHashLength: Option[Int], shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value): AssemblyOption = new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, scalaVersion, level)
9499
def apply(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, repeatableBuild: Boolean, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: sbtassembly.Assembly.SeqString, maxHashLength: Int, shadeRules: sbtassembly.Assembly.SeqShadeRules, scalaVersion: String, level: sbt.Level.Value): AssemblyOption = new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, Option(prependShellScript), Option(maxHashLength), shadeRules, scalaVersion, level)
100+
def apply(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, repeatableBuild: Boolean, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: Option[sbtassembly.Assembly.SeqString], maxHashLength: Option[Int], shadeRules: sbtassembly.Assembly.SeqShadeRules, keepRules: sbtassembly.Assembly.SeqString, scalaVersion: String, level: sbt.Level.Value): AssemblyOption = new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, prependShellScript, maxHashLength, shadeRules, keepRules, scalaVersion, level)
101+
def apply(includeBin: Boolean, includeScala: Boolean, includeDependency: Boolean, excludedJars: sbt.Keys.Classpath, repeatableBuild: Boolean, mergeStrategy: sbtassembly.MergeStrategy.StringToMergeStrategy, cacheOutput: Boolean, appendContentHash: Boolean, prependShellScript: sbtassembly.Assembly.SeqString, maxHashLength: Int, shadeRules: sbtassembly.Assembly.SeqShadeRules, keepRules: sbtassembly.Assembly.SeqString, scalaVersion: String, level: sbt.Level.Value): AssemblyOption = new AssemblyOption(includeBin, includeScala, includeDependency, excludedJars, repeatableBuild, mergeStrategy, cacheOutput, appendContentHash, Option(prependShellScript), Option(maxHashLength), shadeRules, keepRules, scalaVersion, level)
95102
}

src/main/scala/sbtassembly/AssemblyPlugin.scala

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ object AssemblyPlugin extends sbt.AutoPlugin {
2929
override lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
3030
assemblyMergeStrategy := MergeStrategy.defaultMergeStrategy,
3131
assemblyShadeRules := Nil,
32+
assemblyKeepRules := Nil,
3233
assemblyExcludedJars := Nil,
3334
assembleArtifact in packageBin := true,
3435
assembleArtifact in assemblyPackageScala := true,
@@ -118,6 +119,7 @@ object AssemblyPlugin extends sbt.AutoPlugin {
118119
.withPrependShellScript(assemblyPrependShellScript.value)
119120
.withMaxHashLength(assemblyMaxHashLength.?.value)
120121
.withShadeRules(assemblyShadeRules.value)
122+
.withKeepRules(assemblyKeepRules.value)
121123
.withScalaVersion(scalaVersion.value)
122124
.withLevel(logLevel.?.value.getOrElse(Level.Info))
123125
.withRepeatableBuild(assemblyRepeatableBuild.value)

0 commit comments

Comments
 (0)