diff --git a/build.sbt b/build.sbt index 75bde663d..a91837e83 100644 --- a/build.sbt +++ b/build.sbt @@ -133,19 +133,13 @@ lazy val `sbt-pgp-coursier` = project lazy val `sbt-shading` = project .in(file("modules/sbt-shading")) - .enablePlugins(ScriptedPlugin, ShadingPlugin) + .enablePlugins(ScriptedPlugin) .dependsOn(`sbt-coursier`) .settings( plugin, - shading, - libraryDependencies += "io.get-coursier.jarjar" % "jarjar-core" % "1.0.1-coursier-1" % "shaded", - // dependencies of jarjar-core - directly depending on these so that they don't get shaded - libraryDependencies ++= Seq( - "com.google.code.findbugs" % "jsr305" % "3.0.2", - "org.ow2.asm" % "asm-commons" % "7.0", - "org.ow2.asm" % "asm-util" % "7.0", - "org.slf4j" % "slf4j-api" % "1.7.26" - ), + libraryDependencies += ("org.pantsbuild" % "jarjar" % "1.7.2") + .exclude("org.apache.maven", "maven-plugin-api") + .exclude("org.apache.ant", "ant"), scriptedDependencies := { scriptedDependencies.value // TODO Get dependency projects automatically diff --git a/modules/sbt-shading/src/main/java/org/pantsbuild/jarjar/util/CoursierJarProcessor.java b/modules/sbt-shading/src/main/java/org/pantsbuild/jarjar/util/CoursierJarProcessor.java new file mode 100644 index 000000000..51032b56f --- /dev/null +++ b/modules/sbt-shading/src/main/java/org/pantsbuild/jarjar/util/CoursierJarProcessor.java @@ -0,0 +1,74 @@ +package org.pantsbuild.jarjar.util; + +// adapted from https://github.com/pantsbuild/jarjar/blob/57845dc73d3e2c9b916ae4a788cfa12114fd7df1/src/main/java/org/pantsbuild/jarjar/util/StandaloneJarProcessor.java +// - made it accepted a List rather than a single File +// - added argument ignoreDuplicateEntries + +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.Enumeration; +import java.io.*; +import java.util.*; + +public class CoursierJarProcessor +{ + public static void run(File[] from, File to, JarProcessor proc, boolean ignoreDuplicateEntries) throws IOException { + byte[] buf = new byte[0x2000]; + + final File tmpTo = File.createTempFile("jarjar", ".jar"); + Set entries = new HashSet<>(); + + FileOutputStream fos = null; + JarOutputStream out = null; + try { + fos = new FileOutputStream(tmpTo); + BufferedOutputStream buffered = new BufferedOutputStream(fos); + out = new JarOutputStream(buffered); + + for (File from0 : from) { + JarFile in = null; + try { + in = new JarFile(from0); + Enumeration e = in.entries(); + while (e.hasMoreElements()) { + EntryStruct struct = new EntryStruct(); + JarEntry entry = e.nextElement(); + struct.name = entry.getName(); + struct.time = entry.getTime(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IoUtil.pipe(in.getInputStream(entry), baos, buf); + struct.data = baos.toByteArray(); + if (proc.process(struct)) { + if (entries.add(struct.name)) { + entry = new JarEntry(struct.name); + entry.setTime(struct.time); + entry.setCompressedSize(-1); + out.putNextEntry(entry); + out.write(struct.data); + } else if (struct.name.endsWith("/")) { + // TODO(chrisn): log + } else if (!ignoreDuplicateEntries) { + throw new DuplicateJarEntryException(from0.getAbsolutePath(), struct.name); + } + } + } + + } finally { + if (in != null) + in.close(); + } + } + } finally { + if (out != null) + out.close(); + if (fos != null) + fos.close(); + } + + // delete the empty directories + IoUtil.copyZipWithoutEmptyDirectories(tmpTo, to); + tmpTo.delete(); + + } +} diff --git a/modules/sbt-shading/src/main/scala/coursier/Shading.scala b/modules/sbt-shading/src/main/scala/coursier/Shading.scala index dfeb45bcc..1ebb73b74 100644 --- a/modules/sbt-shading/src/main/scala/coursier/Shading.scala +++ b/modules/sbt-shading/src/main/scala/coursier/Shading.scala @@ -4,12 +4,9 @@ import java.io.{File, FileInputStream} import java.util.jar.JarInputStream import java.util.zip.{ZipEntry, ZipInputStream} -import com.tonicsystems.jarjar.classpath.ClassPath -import com.tonicsystems.jarjar.transform.JarTransformer -import com.tonicsystems.jarjar.transform.config.ClassRename -import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor import coursier.core.{Configuration, Orders} -import sbt.file +import org.pantsbuild.jarjar._ +import org.pantsbuild.jarjar.util.CoursierJarProcessor object Shading { @@ -191,17 +188,22 @@ object Shading { baseJar.getName.stripSuffix(".jar") + "-shading.jar" ) - val processor = new DefaultJarProcessor + def rename(from: String, to: String): Rule = { + val rule = new Rule + rule.setPattern(from) + rule.setResult(to) + rule + } - for (namespace <- shadeNamespaces) - processor.addClassRename(new ClassRename(namespace + ".**", shadingNamespace + ".@0")) + val nsRules = shadeNamespaces.toVector.sorted.map { namespace => + rename(namespace + ".**", shadingNamespace + ".@0") + } + val clsRules = toShadeClasses.map { cls => + rename(cls, shadingNamespace + ".@0") + } - for (cls <- toShadeClasses) - processor.addClassRename(new ClassRename(cls, shadingNamespace + ".@0")) - - val transformer = new JarTransformer(outputJar, processor) - val cp = new ClassPath(file(sys.props("user.dir")), (baseJar +: toShadeJars).toArray) - transformer.transform(cp) + val processor = JJProcessor(nsRules ++ clsRules, verbose = true, skipManifest = false) + CoursierJarProcessor.run((baseJar +: toShadeJars).toArray, outputJar, processor.proc, true) outputJar } diff --git a/modules/sbt-shading/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala b/modules/sbt-shading/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala new file mode 100644 index 000000000..90062933a --- /dev/null +++ b/modules/sbt-shading/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala @@ -0,0 +1,33 @@ +package org.pantsbuild.jarjar + +// from https://github.com/sbt/sbt-assembly/blob/17786404117889e5a8225c97b9b7639160fb91e8/src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala + +import org.pantsbuild.jarjar.util.{EntryStruct, JarProcessor} + +import scala.collection.JavaConverters._ + +class JJProcessor(val proc: JarProcessor) { + + 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 + } + } + +} + +object JJProcessor { + + def apply(patterns: Seq[PatternElement], verbose: Boolean, skipManifest: Boolean): JJProcessor = + new JJProcessor(new MainProcessor(patterns.asJava, verbose, skipManifest)) + +}