Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shading support #162

Merged
merged 5 commits into from
Aug 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,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,
Expand Down
31 changes: 31 additions & 0 deletions src/main/scala/org/pantsbuild/jarjar/JJProcessor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.pantsbuild.jarjar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move this into package sbtassembly? If Pants is using that name, I don't want to collide with it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid we can't, since process method in JarProcessor is package private.
I'm not sure why they use such a method signature.


import org.pantsbuild.jarjar.ext_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))

}
76 changes: 38 additions & 38 deletions src/main/scala/sbtassembly/Assembly.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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) {
Expand Down Expand Up @@ -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")
})
Expand All @@ -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))
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
Expand All @@ -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
}

Expand Down Expand Up @@ -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
Expand Down
12 changes: 7 additions & 5 deletions src/main/scala/sbtassembly/AssemblyKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,32 @@ 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

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

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[ShadeRuleConfigured]]("shading rules backed by jarjar")
}
object AssemblyKeys extends AssemblyKeys

Expand Down
20 changes: 12 additions & 8 deletions src/main/scala/sbtassembly/AssemblyPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ object AssemblyPlugin extends sbt.AutoPlugin {
val MergeStrategy = sbtassembly.MergeStrategy
val PathList = sbtassembly.PathList
val baseAssemblySettings = AssemblyPlugin.baseAssemblySettings
val ShadeRule = sbtassembly.ShadeRule
}
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
Expand All @@ -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
Expand All @@ -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 := {
Expand Down Expand Up @@ -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
}

Expand All @@ -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[ShadeRuleConfigured] = Seq())
21 changes: 18 additions & 3 deletions src/main/scala/sbtassembly/AssemblyUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -69,7 +69,7 @@ object AssemblyUtils {
}
}
if(preserveLastModified)
target.setLastModified(entry.getTime)
target.setLastModified(entry.getTime)
} catch {
case e: Throwable => log.warn(e.getMessage)
}
Expand All @@ -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
}
}
Loading