From aa5963805e0714fc59c7da38c6034bc9ac626696 Mon Sep 17 00:00:00 2001 From: Wu Xiang Date: Sat, 15 Aug 2015 21:36:15 +0800 Subject: [PATCH 1/5] Added shading rules backed by jarjar --- build.sbt | 8 +- .../org/pantsbuild/jarjar/JJProcessor.scala | 18 ++++ src/main/scala/sbtassembly/Assembly.scala | 76 +++++++-------- src/main/scala/sbtassembly/AssemblyKeys.scala | 12 ++- .../scala/sbtassembly/AssemblyPlugin.scala | 20 ++-- .../scala/sbtassembly/AssemblyUtils.scala | 21 ++++- src/main/scala/sbtassembly/Shader.scala | 93 +++++++++++++++++++ 7 files changed, 192 insertions(+), 56 deletions(-) create mode 100644 src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala create mode 100644 src/main/scala/sbtassembly/Shader.scala diff --git a/build.sbt b/build.sbt index 29005472..68f1ef7e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,11 @@ lazy val commonSettings: Seq[Setting[_]] = Seq( + version in ThisBuild := "0.13.1-xw-SNAPSHOT", git.baseVersion in ThisBuild := "0.13.1", organization in ThisBuild := "com.eed3si9n" ) lazy val root = (project in file(".")). - enablePlugins(GitVersioning). + // enablePlugins(GitVersioning). settings(commonSettings: _*). settings( sbtPlugin := true, @@ -12,7 +13,10 @@ lazy val root = (project in file(".")). description := "sbt plugin to create a single fat jar", licenses := Seq("MIT License" -> url("https://github.com/sbt/sbt-assembly/blob/master/LICENSE")), scalacOptions := Seq("-deprecation", "-unchecked", "-Dscalac.patmat.analysisBudget=1024"), - libraryDependencies += "org.scalactic" %% "scalactic" % "2.2.1", + libraryDependencies ++= Seq( + "org.scalactic" %% "scalactic" % "2.2.1", + "org.pantsbuild.jarjar" % "jarjar" % "1.5" + ), publishArtifact in (Compile, packageBin) := true, publishArtifact in (Test, packageBin) := false, publishArtifact in (Compile, packageDoc) := false, diff --git a/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala b/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala new file mode 100644 index 00000000..0dbc6a51 --- /dev/null +++ b/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala @@ -0,0 +1,18 @@ +package org.pantsbuild.jarjar + +import org.pantsbuild.jarjar.ext_util.{EntryStruct, JarProcessor} + +import scala.collection.JavaConverters._ + +class JJProcessor(val proc: JarProcessor) { + + def process(entry: EntryStruct): Unit = proc.process(entry) + +} + +object JJProcessor { + + def apply(patterns: Seq[PatternElement], verbose: Boolean, skipManifest: Boolean): JJProcessor = + new JJProcessor(new MainProcessor(patterns.asJava, verbose, skipManifest)) + +} diff --git a/src/main/scala/sbtassembly/Assembly.scala b/src/main/scala/sbtassembly/Assembly.scala index cf5ef6b8..2dfcd607 100644 --- a/src/main/scala/sbtassembly/Assembly.scala +++ b/src/main/scala/sbtassembly/Assembly.scala @@ -10,7 +10,7 @@ import Def.Initialize object Assembly { import AssemblyPlugin.autoImport.{ Assembly => _, _ } - val defaultExcludedFiles: Seq[File] => Seq[File] = (base: Seq[File]) => Nil + val defaultExcludedFiles: Seq[File] => Seq[File] = (base: Seq[File]) => Nil def apply(out0: File, ao: AssemblyOption, po: Seq[PackageOption], mappings: Seq[MappingSet], cacheDir: File, log: Logger): File = { @@ -21,7 +21,7 @@ object Assembly { import java.util.jar.{Attributes, Manifest} lazy val (ms: Vector[(File, String)], stratMapping: List[(String, MergeStrategy)]) = { - log.info("Merging files...") + log.info("Merging files...") applyStrategies(mappings, ao.mergeStrategy, ao.assemblyDirectory, log) } def makeJar(outPath: File) { @@ -56,14 +56,14 @@ object Assembly { } lazy val inputs = { log.info("Checking every *.class/*.jar file's SHA-1.") - val rawHashBytes = + val rawHashBytes = (mappings.toVector.par flatMap { m => m.sourcePackage match { case Some(x) => hash(x).hash case _ => (m.mappings map { x => hash(x._1).hash }).flatten } }) - val pathStratBytes = + val pathStratBytes = (stratMapping.par flatMap { case (path, strat) => (path + strat.name).getBytes("UTF-8") }) @@ -72,12 +72,12 @@ object Assembly { lazy val out = if (ao.appendContentHash) doAppendContentHash(inputs, out0, log) else out0 val cachedMakeJar = inputChanged(cacheDir / "assembly-inputs") { (inChanged, inputs: Seq[Byte]) => - outputChanged(cacheDir / "assembly-outputs") { (outChanged, jar: PlainFileInfo) => + outputChanged(cacheDir / "assembly-outputs") { (outChanged, jar: PlainFileInfo) => if (inChanged) { log.info("SHA-1: " + bytesToString(inputs)) } // if if (inChanged || outChanged) makeJar(out) - else log.info("Assembly up to date: " + jar.file) + else log.info("Assembly up to date: " + jar.file) } } if (ao.cacheOutput) cachedMakeJar(inputs)(() => exists(out)) @@ -147,7 +147,7 @@ object Assembly { }) + (strat.detailLogLevel match { case Level.Debug => " (Run the task at debug level to see details)" case _ => "" - })) + })) } (mod.toVector, stratMapping.toList) } @@ -162,14 +162,22 @@ object Assembly { if (!ao.cacheUnzip) IO.delete(tempDir) if (!tempDir.exists) tempDir.mkdir() - val (libs, dirs) = classpath.map(_.data).toVector.partition(ClasspathUtilities.isArchive) + val shadingRules = ao.shadingRules + + val (libs, dirs) = classpath.toVector.partition(c => ClasspathUtilities.isArchive(c.data)) + + val dirRules = shadingRules.filter(_.isApplicableToCompiling) + if (!dirRules.isEmpty) { + dirs.foreach(d => Shader.shadeDirectory(dirRules, d.data, log)) + } + val depLibs = dependencies.map(_.data).toSet.filter(ClasspathUtilities.isArchive) val excludedJars = ao.excludedJars map {_.data} val libsFiltered = (libs flatMap { - case jar if excludedJars contains jar.asFile => None - case jar if isScalaLibraryFile(jar.asFile) => + case jar if excludedJars contains jar.data.asFile => None + case jar if isScalaLibraryFile(jar.data.asFile) => if (ao.includeScala) Some(jar) else None - case jar if depLibs contains jar.asFile => + case jar if depLibs contains jar.data.asFile => if (ao.includeDependency) Some(jar) else None case jar => if (ao.includeBin) Some(jar) else None @@ -180,59 +188,51 @@ object Assembly { if (ao.includeBin) Some(dir) else None } map { dir => - val hash = sha1name(dir) - IO.write(tempDir / (hash + "_dir.dir"), dir.getCanonicalPath, IO.utf8, false) + val hash = sha1name(dir.data) + IO.write(tempDir / (hash + "_dir.dir"), dir.data.getCanonicalPath, IO.utf8, false) val dest = tempDir / (hash + "_dir") if (dest.exists) { IO.delete(dest) } dest.mkdir() - IO.copyDirectory(dir, dest) + IO.copyDirectory(dir.data, dest) dest } val jarDirs = (for(jar <- libsFiltered.par) yield { - val jarName = jar.asFile.getName - val hash = sha1name(jar) + "_" + sha1content(jar) + val jarName = jar.data.asFile.getName + val hash = sha1name(jar.data) + "_" + sha1content(jar.data) val jarNamePath = tempDir / (hash + ".jarName") val dest = tempDir / hash // If the jar name path does not exist, or is not for this jar, unzip the jar - if (!ao.cacheUnzip || !jarNamePath.exists || IO.read(jarNamePath) != jar.getCanonicalPath ) + if (!ao.cacheUnzip || !jarNamePath.exists || IO.read(jarNamePath) != jar.data.getCanonicalPath ) { log.info("Including: %s".format(jarName)) IO.delete(dest) dest.mkdir() - AssemblyUtils.unzip(jar, dest, log) + AssemblyUtils.unzip(jar.data, dest, log) IO.delete(ao.excludedFiles(Seq(dest))) - + + val jarRules = shadingRules + .filter(_.isApplicableTo(jar.metadata.get(moduleID.key).get)) + if (jarRules.nonEmpty) { + Shader.shadeDirectory(jarRules, dest, log) + } + // Write the jarNamePath at the end to minimise the chance of having a // corrupt cache if the user aborts the build midway through - IO.write(jarNamePath, jar.getCanonicalPath, IO.utf8, false) + IO.write(jarNamePath, jar.data.getCanonicalPath, IO.utf8, false) } else log.info("Including from cache: %s".format(jarName)) - (dest, jar) + (dest, jar.data) }) log.debug("Calculate mappings...") val base: Vector[File] = dirsFiltered.seq ++ (jarDirs map { _._1 }) val excluded = (ao.excludedFiles(base) ++ base).toSet - def getMappings(rootDir : File): Vector[(File, String)] = - if(!rootDir.exists) Vector() - else { - val sysFileSep = System.getProperty("file.separator") - def loop(dir: File, prefix: String, acc: Seq[(File, String)]): Seq[(File, String)] = { - val children = (dir * new SimpleFileFilter(f => !excluded(f))).get - children.flatMap { f => - val rel = (if(prefix.isEmpty) "" else prefix + sysFileSep) + f.getName - val pairAcc = (f -> rel) +: acc - if(f.isDirectory) loop(f, rel, pairAcc) else pairAcc - } - } - loop(rootDir, "", Nil).toVector - } - val retval = (dirsFiltered map { d => MappingSet(None, getMappings(d)) }).seq ++ - (jarDirs map { case (d, j) => MappingSet(Some(j), getMappings(d)) }) + val retval = (dirsFiltered map { d => MappingSet(None, AssemblyUtils.getMappings(d, excluded)) }).seq ++ + (jarDirs map { case (d, j) => MappingSet(Some(j), AssemblyUtils.getMappings(d, excluded)) }) retval.toVector } @@ -295,7 +295,7 @@ object Assembly { private[sbtassembly] def sha1content(f: File): String = bytesToSha1String(IO.readBytes(f)) private[sbtassembly] def sha1name(f: File): String = sha1string(f.getCanonicalPath) private[sbtassembly] def sha1string(s: String): String = bytesToSha1String(s.getBytes("UTF-8")) - private[sbtassembly] def bytesToSha1String(bytes: Array[Byte]): String = + private[sbtassembly] def bytesToSha1String(bytes: Array[Byte]): String = bytesToString(sha1.digest(bytes)) private[sbtassembly] def bytesToString(bytes: Seq[Byte]): String = bytes map {"%02x".format(_)} mkString diff --git a/src/main/scala/sbtassembly/AssemblyKeys.scala b/src/main/scala/sbtassembly/AssemblyKeys.scala index 9860ba6b..c8cfc6ef 100644 --- a/src/main/scala/sbtassembly/AssemblyKeys.scala +++ b/src/main/scala/sbtassembly/AssemblyKeys.scala @@ -12,7 +12,7 @@ trait AssemblyKeys { lazy val assemblyPackageScala = taskKey[File]("Produces the scala artifact.") @deprecated("Use assemblyPackageScala", "0.12.0") lazy val packageScala = assemblyPackageScala - + lazy val assemblyPackageDependency = taskKey[File]("Produces the dependency artifact.") @deprecated("Use assemblyPackageDependency", "0.12.0") lazy val packageDependency = assemblyPackageDependency @@ -20,11 +20,11 @@ trait AssemblyKeys { lazy val assemblyJarName = taskKey[String]("name of the fat jar") @deprecated("Use assemblyJarName", "0.12.0") lazy val jarName = assemblyJarName - + lazy val assemblyDefaultJarName = taskKey[String]("default name of the fat jar") @deprecated("Use assemblyDefaultJarName", "0.12.0") lazy val defaultJarName = assemblyDefaultJarName - + lazy val assemblyOutputPath = taskKey[File]("output path of the fat jar") @deprecated("Use assemblyOutputPath", "0.12.0") lazy val outputPath = assemblyOutputPath @@ -32,10 +32,12 @@ trait AssemblyKeys { lazy val assemblyExcludedJars = taskKey[Classpath]("list of excluded jars") @deprecated("Use assemblyExcludedJars", "0.12.0") lazy val excludedJars = assemblyExcludedJars - + lazy val assemblyMergeStrategy = settingKey[String => MergeStrategy]("mapping from archive member path to merge strategy") @deprecated("Use assemblyMergeStrategy", "0.12.0") - lazy val mergeStrategy = assemblyMergeStrategy + lazy val mergeStrategy = assemblyMergeStrategy + + lazy val assemblyShadingRules = settingKey[Seq[ShadeRule]]("shading rules backed by jarjar") } object AssemblyKeys extends AssemblyKeys diff --git a/src/main/scala/sbtassembly/AssemblyPlugin.scala b/src/main/scala/sbtassembly/AssemblyPlugin.scala index 5297605e..e8e6079b 100644 --- a/src/main/scala/sbtassembly/AssemblyPlugin.scala +++ b/src/main/scala/sbtassembly/AssemblyPlugin.scala @@ -13,9 +13,10 @@ object AssemblyPlugin extends sbt.AutoPlugin { val MergeStrategy = sbtassembly.MergeStrategy val PathList = sbtassembly.PathList val baseAssemblySettings = AssemblyPlugin.baseAssemblySettings + val Shader = sbtassembly.Shader } import autoImport.{ Assembly => _, baseAssemblySettings => _, _ } - + val defaultShellScript: Seq[String] = Seq("#!/usr/bin/env sh", """exec java -jar "$0" "$@"""") // " override lazy val projectSettings: Seq[Def.Setting[_]] = assemblySettings @@ -32,12 +33,13 @@ object AssemblyPlugin extends sbt.AutoPlugin { test in assembly := (test in Test).value, test in assemblyPackageScala := (test in assembly).value, test in assemblyPackageDependency := (test in assembly).value, - + // assemblyOption assembleArtifact in packageBin := true, assembleArtifact in assemblyPackageScala := true, assembleArtifact in assemblyPackageDependency := true, assemblyMergeStrategy in assembly := MergeStrategy.defaultMergeStrategy, + assemblyShadingRules in assembly := Seq(), assemblyExcludedJars in assembly := Nil, assemblyOption in assembly := { val s = streams.value @@ -52,7 +54,8 @@ object AssemblyPlugin extends sbt.AutoPlugin { cacheOutput = true, cacheUnzip = true, appendContentHash = false, - prependShellScript = None) + prependShellScript = None, + shadingRules = (assemblyShadingRules in assembly).value) }, assemblyOption in assemblyPackageScala := { @@ -86,14 +89,14 @@ object AssemblyPlugin extends sbt.AutoPlugin { assemblyDefaultJarName in assemblyPackageScala <<= (scalaVersion) map { (scalaVersion) => "scala-library-" + scalaVersion + "-assembly.jar" }, assemblyDefaultJarName in assemblyPackageDependency <<= (name, version) map { (name, version) => name + "-assembly-" + version + "-deps.jar" }, assemblyDefaultJarName in assembly <<= (name, version) map { (name, version) => name + "-assembly-" + version + ".jar" }, - + mainClass in assembly <<= mainClass or (mainClass in Runtime), - + fullClasspath in assembly <<= fullClasspath or (fullClasspath in Runtime), - + externalDependencyClasspath in assembly <<= externalDependencyClasspath or (externalDependencyClasspath in Runtime) ) - + lazy val assemblySettings: Seq[sbt.Def.Setting[_]] = baseAssemblySettings } @@ -109,4 +112,5 @@ case class AssemblyOption(assemblyDirectory: File, cacheOutput: Boolean = true, cacheUnzip: Boolean = true, appendContentHash: Boolean = false, - prependShellScript: Option[Seq[String]] = None) + prependShellScript: Option[Seq[String]] = None, + shadingRules: Seq[ShadeRule] = Seq()) diff --git a/src/main/scala/sbtassembly/AssemblyUtils.scala b/src/main/scala/sbtassembly/AssemblyUtils.scala index 817c4a3e..bd80d1c2 100644 --- a/src/main/scala/sbtassembly/AssemblyUtils.scala +++ b/src/main/scala/sbtassembly/AssemblyUtils.scala @@ -11,7 +11,7 @@ object AssemblyUtils { private val PathRE = "([^/]+)/(.*)".r /** Find the source file (and possibly the entry within a jar) whence a conflicting file came. - * + * * @param tempDir The temporary directory provided to a `MergeStrategy` * @param f One of the files provided to a `MergeStrategy` * @return The source jar or dir; the path within that dir; and true if it's from a jar. @@ -57,7 +57,7 @@ object AssemblyUtils { { val target = new File(toDirectory, name) //log.debug("Extracting zip entry '" + name + "' to '" + target + "'") - + try { if(entry.isDirectory) IO.createDirectory(target) @@ -69,7 +69,7 @@ object AssemblyUtils { } } if(preserveLastModified) - target.setLastModified(entry.getTime) + target.setLastModified(entry.getTime) } catch { case e: Throwable => log.warn(e.getMessage) } @@ -85,4 +85,19 @@ object AssemblyUtils { next() Set() ++ set } + + def getMappings(rootDir : File, excluded: Set[File]): Vector[(File, String)] = + if(!rootDir.exists) Vector() + else { + val sysFileSep = System.getProperty("file.separator") + def loop(dir: File, prefix: String, acc: Seq[(File, String)]): Seq[(File, String)] = { + val children = (dir * new SimpleFileFilter(f => !excluded(f))).get + children.flatMap { f => + val rel = (if(prefix.isEmpty) "" else prefix + sysFileSep) + f.getName + val pairAcc = (f -> rel) +: acc + if(f.isDirectory) loop(f, rel, pairAcc) else pairAcc + } + } + loop(rootDir, "", Nil).toVector + } } diff --git a/src/main/scala/sbtassembly/Shader.scala b/src/main/scala/sbtassembly/Shader.scala new file mode 100644 index 00000000..075f3a83 --- /dev/null +++ b/src/main/scala/sbtassembly/Shader.scala @@ -0,0 +1,93 @@ +package sbtassembly + +import java.io.File + +import org.pantsbuild.jarjar.ext_util.EntryStruct +import org.pantsbuild.jarjar.{JJProcessor, Keep, Zap, Rule} + +import sbt._ + +object Shader { + + def rename(patterns: (String, String)*): ShadeRule = + ShadeRule(rule = "rename", renames = patterns.toMap) + + def remove(patterns: String*): ShadeRule = + ShadeRule(rule = "remove", patterns = patterns.toSet) + + def keepOnly(patterns: String*): ShadeRule = + ShadeRule(rule = "keepOnly", patterns = patterns.toSet) + + private[sbtassembly] def shadeDirectory(rules: Seq[ShadeRule], dir: File, log: Logger): Unit = { + val jjrules = rules flatMap { r => r.rule match { + case "rename" => + r.renames.map { case (from, to) => + val jrule = new Rule() + jrule.setPattern(from) + jrule.setResult(to) + jrule + } + case "remove" => + r.patterns.map { case pattern => + val jrule = new Zap() + jrule.setPattern(pattern) + jrule + } + case "keepOnly" => + r.patterns.map { case pattern => + val jrule = new Keep() + jrule.setPattern(pattern) + jrule + } + case _ => Nil + }} + + val proc = JJProcessor(jjrules, true, true) + val files = AssemblyUtils.getMappings(dir, Set()) + + val entry = new EntryStruct + files filter (!_._1.isDirectory) foreach { f => + entry.data = IO.readBytes(f._1) + entry.name = f._2 + entry.time = -1 + + proc.process(entry) + + IO.write(dir / entry.name, entry.data) + if (f._2 != entry.name) IO.delete(f._1) + } + } + +} + +case class ShadeTarget(toCompiling: Boolean = false, + group: Option[String] = None, + artifact: Option[String] = None, + version: Option[String] = None) { + private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = + group.isDefined && group.get == mod.organization && + artifact.isDefined && artifact.get == mod.name && + (version.isEmpty || version.get == mod.revision) +} + +case class ShadeRule(rule: String, + renames: Map[String, String] = Map(), + patterns: Set[String] = Set(), + targets: Seq[ShadeTarget] = Seq()) { + + def applyToCompiling: ShadeRule = + this.copy(targets = targets :+ ShadeTarget(true)) + + def applyTo(group: String, artifact: String): ShadeRule = + this.copy(targets = targets :+ ShadeTarget(group = Some(group), artifact = Some(artifact))) + + def applyTo(group: String, artifact: String, version: String): ShadeRule = + this.copy(targets = targets :+ ShadeTarget(group = Some(group), artifact = Some(artifact), version = Some(version))) + + private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = + targets.exists(_.isApplicableTo(mod)) + + private[sbtassembly] def isApplicableToCompiling: Boolean = + targets.exists(_.toCompiling) + +} From 281150ae357425a9d437b55447793a22929b904d Mon Sep 17 00:00:00 2001 From: Wu Xiang Date: Sat, 15 Aug 2015 21:46:52 +0800 Subject: [PATCH 2/5] Remove debug settings --- build.sbt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 68f1ef7e..ee154c0e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,11 +1,10 @@ lazy val commonSettings: Seq[Setting[_]] = Seq( - version in ThisBuild := "0.13.1-xw-SNAPSHOT", git.baseVersion in ThisBuild := "0.13.1", organization in ThisBuild := "com.eed3si9n" ) lazy val root = (project in file(".")). - // enablePlugins(GitVersioning). + enablePlugins(GitVersioning). settings(commonSettings: _*). settings( sbtPlugin := true, From c4fdaa370a0be9546192e91e2271a92344c55fdd Mon Sep 17 00:00:00 2001 From: Wu Xiang Date: Tue, 18 Aug 2015 18:13:33 +0800 Subject: [PATCH 3/5] refactor ShadeRule to case classes --- src/main/scala/sbtassembly/AssemblyKeys.scala | 2 +- .../scala/sbtassembly/AssemblyPlugin.scala | 4 +- src/main/scala/sbtassembly/Shader.scala | 97 ++++++++++--------- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/main/scala/sbtassembly/AssemblyKeys.scala b/src/main/scala/sbtassembly/AssemblyKeys.scala index c8cfc6ef..0acbad05 100644 --- a/src/main/scala/sbtassembly/AssemblyKeys.scala +++ b/src/main/scala/sbtassembly/AssemblyKeys.scala @@ -37,7 +37,7 @@ trait AssemblyKeys { @deprecated("Use assemblyMergeStrategy", "0.12.0") lazy val mergeStrategy = assemblyMergeStrategy - lazy val assemblyShadingRules = settingKey[Seq[ShadeRule]]("shading rules backed by jarjar") + lazy val assemblyShadingRules = settingKey[Seq[ShadeRuleConfigured]]("shading rules backed by jarjar") } object AssemblyKeys extends AssemblyKeys diff --git a/src/main/scala/sbtassembly/AssemblyPlugin.scala b/src/main/scala/sbtassembly/AssemblyPlugin.scala index e8e6079b..ccdc9db1 100644 --- a/src/main/scala/sbtassembly/AssemblyPlugin.scala +++ b/src/main/scala/sbtassembly/AssemblyPlugin.scala @@ -13,7 +13,7 @@ object AssemblyPlugin extends sbt.AutoPlugin { val MergeStrategy = sbtassembly.MergeStrategy val PathList = sbtassembly.PathList val baseAssemblySettings = AssemblyPlugin.baseAssemblySettings - val Shader = sbtassembly.Shader + val ShadeRule = sbtassembly.ShadeRule } import autoImport.{ Assembly => _, baseAssemblySettings => _, _ } @@ -113,4 +113,4 @@ case class AssemblyOption(assemblyDirectory: File, cacheUnzip: Boolean = true, appendContentHash: Boolean = false, prependShellScript: Option[Seq[String]] = None, - shadingRules: Seq[ShadeRule] = Seq()) + shadingRules: Seq[ShadeRuleConfigured] = Seq()) diff --git a/src/main/scala/sbtassembly/Shader.scala b/src/main/scala/sbtassembly/Shader.scala index 075f3a83..ead78ab0 100644 --- a/src/main/scala/sbtassembly/Shader.scala +++ b/src/main/scala/sbtassembly/Shader.scala @@ -3,42 +3,75 @@ package sbtassembly import java.io.File import org.pantsbuild.jarjar.ext_util.EntryStruct -import org.pantsbuild.jarjar.{JJProcessor, Keep, Zap, Rule} +import org.pantsbuild.jarjar._ import sbt._ -object Shader { +case class ShadeRuleConfigured(rule: ShadeRule, targets: Seq[ShadeTarget] = Seq()) { - def rename(patterns: (String, String)*): ShadeRule = - ShadeRule(rule = "rename", renames = patterns.toMap) + def applyTo(moduleID: ModuleID): ShadeRuleConfigured = + this.copy(targets = targets :+ ShadeTarget(moduleID = Some(moduleID))) - def remove(patterns: String*): ShadeRule = - ShadeRule(rule = "remove", patterns = patterns.toSet) + def applyToCompiling(): ShadeRuleConfigured = + this.copy(targets = targets :+ ShadeTarget(toCompiling = true)) - def keepOnly(patterns: String*): ShadeRule = - ShadeRule(rule = "keepOnly", patterns = patterns.toSet) + private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = + targets.exists(_.isApplicableTo(mod)) + + private[sbtassembly] def isApplicableToCompiling: Boolean = + targets.exists(_.toCompiling) + +} + +sealed trait ShadeRule + +object ShadeRule { + + case class Rename(patterns: (String, String)*) extends ShadeRule + + case class Remove(patterns: String*) extends ShadeRule + + case class KeepOnly(patterns: String*) extends ShadeRule + + implicit def toShadeRuleConfigured(rule: ShadeRule): ShadeRuleConfigured = ShadeRuleConfigured(rule) + +} + +private[sbtassembly] case class ShadeTarget(toCompiling: Boolean = false, moduleID: Option[ModuleID] = None) { + + private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = + moduleID.isDefined && mod.equals(moduleID) + +} + +private[sbtassembly] object Shader { - private[sbtassembly] def shadeDirectory(rules: Seq[ShadeRule], dir: File, log: Logger): Unit = { + import ShadeRule._ + + private[sbtassembly] def shadeDirectory(rules: Seq[ShadeRuleConfigured], dir: File, log: Logger): Unit = { val jjrules = rules flatMap { r => r.rule match { - case "rename" => - r.renames.map { case (from, to) => + case Rename(patterns @ _*) => + patterns.map { case (from, to) => val jrule = new Rule() jrule.setPattern(from) jrule.setResult(to) jrule } - case "remove" => - r.patterns.map { case pattern => + + case Remove(patterns @ _*) => + patterns.map { case pattern => val jrule = new Zap() jrule.setPattern(pattern) jrule } - case "keepOnly" => - r.patterns.map { case pattern => + + case KeepOnly(patterns @ _*) => + patterns.map { case pattern => val jrule = new Keep() jrule.setPattern(pattern) jrule } + case _ => Nil }} @@ -58,36 +91,4 @@ object Shader { } } -} - -case class ShadeTarget(toCompiling: Boolean = false, - group: Option[String] = None, - artifact: Option[String] = None, - version: Option[String] = None) { - private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = - group.isDefined && group.get == mod.organization && - artifact.isDefined && artifact.get == mod.name && - (version.isEmpty || version.get == mod.revision) -} - -case class ShadeRule(rule: String, - renames: Map[String, String] = Map(), - patterns: Set[String] = Set(), - targets: Seq[ShadeTarget] = Seq()) { - - def applyToCompiling: ShadeRule = - this.copy(targets = targets :+ ShadeTarget(true)) - - def applyTo(group: String, artifact: String): ShadeRule = - this.copy(targets = targets :+ ShadeTarget(group = Some(group), artifact = Some(artifact))) - - def applyTo(group: String, artifact: String, version: String): ShadeRule = - this.copy(targets = targets :+ ShadeTarget(group = Some(group), artifact = Some(artifact), version = Some(version))) - - private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = - targets.exists(_.isApplicableTo(mod)) - - private[sbtassembly] def isApplicableToCompiling: Boolean = - targets.exists(_.toCompiling) - -} +} \ No newline at end of file From 8bfe3e7b6c9dcfaf0cc54dddc333a55baae12848 Mon Sep 17 00:00:00 2001 From: Wu Xiang Date: Tue, 18 Aug 2015 18:36:21 +0800 Subject: [PATCH 4/5] fix for moduleID comparasion --- src/main/scala/sbtassembly/Shader.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/sbtassembly/Shader.scala b/src/main/scala/sbtassembly/Shader.scala index ead78ab0..490ff39e 100644 --- a/src/main/scala/sbtassembly/Shader.scala +++ b/src/main/scala/sbtassembly/Shader.scala @@ -40,7 +40,7 @@ object ShadeRule { private[sbtassembly] case class ShadeTarget(toCompiling: Boolean = false, moduleID: Option[ModuleID] = None) { private[sbtassembly] def isApplicableTo(mod: ModuleID): Boolean = - moduleID.isDefined && mod.equals(moduleID) + moduleID.isDefined && mod.equals(moduleID.get) } From 9a4522120997387be227b593f3b0f1ed465ea89c Mon Sep 17 00:00:00 2001 From: Wu Xiang Date: Thu, 27 Aug 2015 00:36:26 +0800 Subject: [PATCH 5/5] Added scripted tests & bugfix for KeepOnly rule --- .../org/pantsbuild/jarjar/JJProcessor.scala | 15 ++++++++++- src/main/scala/sbtassembly/Shader.scala | 14 ++++++++--- src/sbt-test/shading/keeponly/build.sbt | 23 +++++++++++++++++ .../shading/keeponly/project/plugins.sbt | 7 ++++++ .../keeponly/src/main/scala/keep/Keeped.scala | 3 +++ .../src/main/scala/removed/ShadeClass.scala | 3 +++ .../src/main/scala/removed/ShadePackage.scala | 3 +++ src/sbt-test/shading/keeponly/test | 6 +++++ src/sbt-test/shading/shading/build.sbt | 25 +++++++++++++++++++ .../shading/shading/project/plugins.sbt | 7 ++++++ .../src/main/scala/remove/Removed.scala | 3 +++ .../src/main/scala/toshade/ShadeClass.scala | 3 +++ .../src/main/scala/toshade/ShadePackage.scala | 3 +++ src/sbt-test/shading/shading/test | 6 +++++ 14 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 src/sbt-test/shading/keeponly/build.sbt create mode 100644 src/sbt-test/shading/keeponly/project/plugins.sbt create mode 100644 src/sbt-test/shading/keeponly/src/main/scala/keep/Keeped.scala create mode 100644 src/sbt-test/shading/keeponly/src/main/scala/removed/ShadeClass.scala create mode 100644 src/sbt-test/shading/keeponly/src/main/scala/removed/ShadePackage.scala create mode 100644 src/sbt-test/shading/keeponly/test create mode 100644 src/sbt-test/shading/shading/build.sbt create mode 100644 src/sbt-test/shading/shading/project/plugins.sbt create mode 100644 src/sbt-test/shading/shading/src/main/scala/remove/Removed.scala create mode 100644 src/sbt-test/shading/shading/src/main/scala/toshade/ShadeClass.scala create mode 100644 src/sbt-test/shading/shading/src/main/scala/toshade/ShadePackage.scala create mode 100644 src/sbt-test/shading/shading/test diff --git a/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala b/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala index 0dbc6a51..560ac188 100644 --- a/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala +++ b/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala @@ -6,7 +6,20 @@ import scala.collection.JavaConverters._ class JJProcessor(val proc: JarProcessor) { - def process(entry: EntryStruct): Unit = proc.process(entry) + def process(entry: EntryStruct): Boolean = proc.process(entry) + + def getExcludes(): Set[String] = { + val field = proc.getClass().getDeclaredField("kp") + field.setAccessible(true) + val keepProcessor = field.get(proc) + + if (keepProcessor == null) Set() + else { + val method = proc.getClass().getDeclaredMethod("getExcludes") + method.setAccessible(true) + method.invoke(proc).asInstanceOf[java.util.Set[String]].asScala.toSet + } + } } diff --git a/src/main/scala/sbtassembly/Shader.scala b/src/main/scala/sbtassembly/Shader.scala index 490ff39e..f70e83a5 100644 --- a/src/main/scala/sbtassembly/Shader.scala +++ b/src/main/scala/sbtassembly/Shader.scala @@ -48,7 +48,7 @@ private[sbtassembly] object Shader { import ShadeRule._ - private[sbtassembly] def shadeDirectory(rules: Seq[ShadeRuleConfigured], dir: File, log: Logger): Unit = { + def shadeDirectory(rules: Seq[ShadeRuleConfigured], dir: File, log: Logger): Unit = { val jjrules = rules flatMap { r => r.rule match { case Rename(patterns @ _*) => patterns.map { case (from, to) => @@ -84,11 +84,17 @@ private[sbtassembly] object Shader { entry.name = f._2 entry.time = -1 - proc.process(entry) + if (proc.process(entry)) { + IO.write(dir / entry.name, entry.data) + } else { + IO.delete(f._1) + } - IO.write(dir / entry.name, entry.data) - if (f._2 != entry.name) IO.delete(f._1) } + + val excludes = proc.getExcludes() + excludes.foreach(exclude => IO.delete(dir / exclude)) + } } \ No newline at end of file diff --git a/src/sbt-test/shading/keeponly/build.sbt b/src/sbt-test/shading/keeponly/build.sbt new file mode 100644 index 00000000..4d6d1e2a --- /dev/null +++ b/src/sbt-test/shading/keeponly/build.sbt @@ -0,0 +1,23 @@ +lazy val testkeep = (project in file(".")). + settings( + version := "0.1", + assemblyJarName in assembly := "foo.jar", + scalaVersion := "2.9.1", + assemblyShadingRules in assembly := Seq( + ShadeRule.KeepOnly("keep.**").applyToCompiling + ), + TaskKey[Unit]("check") <<= (crossTarget) map { (crossTarget) ⇒ + IO.withTemporaryDirectory { dir ⇒ + IO.unzip(crossTarget / "foo.jar", dir) + mustNotExist(dir / "removed" / "ShadeClass.class") + mustNotExist(dir / "removed" / "ShadePackage.class") + mustExist(dir / "keep" / "Keeped.class") + } + }) + +def mustNotExist(f: File): Unit = { + if (f.exists) sys.error("file" + f + " exists!") +} +def mustExist(f: File): Unit = { + if (!f.exists) sys.error("file" + f + " does not exist!") +} diff --git a/src/sbt-test/shading/keeponly/project/plugins.sbt b/src/sbt-test/shading/keeponly/project/plugins.sbt new file mode 100644 index 00000000..b7bb6c0d --- /dev/null +++ b/src/sbt-test/shading/keeponly/project/plugins.sbt @@ -0,0 +1,7 @@ +{ + val pluginVersion = System.getProperty("plugin.version") + if(pluginVersion == null) + throw new RuntimeException("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) + else addSbtPlugin("com.eed3si9n" % "sbt-assembly" % pluginVersion) +} diff --git a/src/sbt-test/shading/keeponly/src/main/scala/keep/Keeped.scala b/src/sbt-test/shading/keeponly/src/main/scala/keep/Keeped.scala new file mode 100644 index 00000000..789b5f46 --- /dev/null +++ b/src/sbt-test/shading/keeponly/src/main/scala/keep/Keeped.scala @@ -0,0 +1,3 @@ +package keep + +class Keeped \ No newline at end of file diff --git a/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadeClass.scala b/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadeClass.scala new file mode 100644 index 00000000..bfacff85 --- /dev/null +++ b/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadeClass.scala @@ -0,0 +1,3 @@ +package removed + +class ShadeClass diff --git a/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadePackage.scala b/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadePackage.scala new file mode 100644 index 00000000..0171cf08 --- /dev/null +++ b/src/sbt-test/shading/keeponly/src/main/scala/removed/ShadePackage.scala @@ -0,0 +1,3 @@ +package removed + +class ShadePackage diff --git a/src/sbt-test/shading/keeponly/test b/src/sbt-test/shading/keeponly/test new file mode 100644 index 00000000..defb9db1 --- /dev/null +++ b/src/sbt-test/shading/keeponly/test @@ -0,0 +1,6 @@ +# check if the file gets created +> assembly +$ exists target/scala-2.9.1/foo.jar + +# check if it says hello +> check \ No newline at end of file diff --git a/src/sbt-test/shading/shading/build.sbt b/src/sbt-test/shading/shading/build.sbt new file mode 100644 index 00000000..40f3040b --- /dev/null +++ b/src/sbt-test/shading/shading/build.sbt @@ -0,0 +1,25 @@ +lazy val testshade = (project in file(".")). + settings( + version := "0.1", + assemblyJarName in assembly := "foo.jar", + scalaVersion := "2.9.1", + assemblyShadingRules in assembly := Seq( + ShadeRule.Remove("remove.*").applyToCompiling, + ShadeRule.Rename("toshade.ShadeClass" -> "toshade.ShadedClass").applyToCompiling, + ShadeRule.Rename("toshade.ShadePackage" -> "shaded_package.ShadePackage").applyToCompiling + ), + TaskKey[Unit]("check") <<= (crossTarget) map { (crossTarget) ⇒ + IO.withTemporaryDirectory { dir ⇒ + IO.unzip(crossTarget / "foo.jar", dir) + mustNotExist(dir / "remove" / "Removed.class") + mustExist(dir / "shaded_package" / "ShadePackage.class") + mustExist(dir / "toshade" / "ShadedClass.class") + } + }) + +def mustNotExist(f: File): Unit = { + if (f.exists) sys.error("file" + f + " exists!") +} +def mustExist(f: File): Unit = { + if (!f.exists) sys.error("file" + f + " does not exist!") +} diff --git a/src/sbt-test/shading/shading/project/plugins.sbt b/src/sbt-test/shading/shading/project/plugins.sbt new file mode 100644 index 00000000..b7bb6c0d --- /dev/null +++ b/src/sbt-test/shading/shading/project/plugins.sbt @@ -0,0 +1,7 @@ +{ + val pluginVersion = System.getProperty("plugin.version") + if(pluginVersion == null) + throw new RuntimeException("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) + else addSbtPlugin("com.eed3si9n" % "sbt-assembly" % pluginVersion) +} diff --git a/src/sbt-test/shading/shading/src/main/scala/remove/Removed.scala b/src/sbt-test/shading/shading/src/main/scala/remove/Removed.scala new file mode 100644 index 00000000..7a1a9885 --- /dev/null +++ b/src/sbt-test/shading/shading/src/main/scala/remove/Removed.scala @@ -0,0 +1,3 @@ +package remove + +class Removed \ No newline at end of file diff --git a/src/sbt-test/shading/shading/src/main/scala/toshade/ShadeClass.scala b/src/sbt-test/shading/shading/src/main/scala/toshade/ShadeClass.scala new file mode 100644 index 00000000..0d66b5fd --- /dev/null +++ b/src/sbt-test/shading/shading/src/main/scala/toshade/ShadeClass.scala @@ -0,0 +1,3 @@ +package toshade + +class ShadeClass \ No newline at end of file diff --git a/src/sbt-test/shading/shading/src/main/scala/toshade/ShadePackage.scala b/src/sbt-test/shading/shading/src/main/scala/toshade/ShadePackage.scala new file mode 100644 index 00000000..8e66cfc6 --- /dev/null +++ b/src/sbt-test/shading/shading/src/main/scala/toshade/ShadePackage.scala @@ -0,0 +1,3 @@ +package toshade + +class ShadePackage \ No newline at end of file diff --git a/src/sbt-test/shading/shading/test b/src/sbt-test/shading/shading/test new file mode 100644 index 00000000..defb9db1 --- /dev/null +++ b/src/sbt-test/shading/shading/test @@ -0,0 +1,6 @@ +# check if the file gets created +> assembly +$ exists target/scala-2.9.1/foo.jar + +# check if it says hello +> check \ No newline at end of file