Created
August 8, 2014 22:55
-
-
Save colestanfield/fac042d3108b0c06e952 to your computer and use it in GitHub Desktop.
sbt-assembly merge strategy for aop.xml files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create a new MergeStrategy for aop.xml files | |
val aopMerge: MergeStrategy = new MergeStrategy { | |
val name = "aopMerge" | |
import scala.xml._ | |
import scala.xml.dtd._ | |
def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = { | |
val dt = DocType("aspectj", PublicID("-//AspectJ//DTD//EN", "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"), Nil) | |
val file = MergeStrategy.createMergeTarget(tempDir, path) | |
val xmls: Seq[Elem] = files.map(XML.loadFile) | |
val aspectsChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "aspects" \ "_") | |
val weaverChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "weaver" \ "_") | |
val options: String = xmls.map(x => (x \\ "aspectj" \ "weaver" \ "@options").text).mkString(" ").trim | |
val weaverAttr = if (options.isEmpty) Null else new UnprefixedAttribute("options", options, Null) | |
val aspects = new Elem(null, "aspects", Null, TopScope, false, aspectsChildren: _*) | |
val weaver = new Elem(null, "weaver", weaverAttr, TopScope, false, weaverChildren: _*) | |
val aspectj = new Elem(null, "aspectj", Null, TopScope, false, aspects, weaver) | |
XML.save(file.toString, aspectj, "UTF-8", xmlDecl = false, dt) | |
IO.append(file, IO.Newline.getBytes(IO.defaultCharset)) | |
Right(Seq(file -> path)) | |
} | |
} | |
// Use defaultMergeStrategy with a case for aop.xml | |
// I like this better than the inline version mentioned in assembly's README | |
val customMergeStrategy: String => MergeStrategy = { | |
case PathList("META-INF", "aop.xml") => | |
aopMerge | |
case s => | |
defaultMergeStrategy(s) | |
} | |
// Use the customMergeStrategy in your settings | |
mergeStrategy in assembly := customMergeStrategy |
It worked .. thanks @colestanfield for nice finding
I've tried using this snippet and for some reason sbt assembly
suspends while trying to read an XML file from a directory target/streams/$global
making us unable to use that - does anyone knows what may be causing this to try and read this directory or why is it created?
Hello @colestanfield
I notice that with sbt 1.4.9
this cause some problem
The error message during the build is
org.xml.sax.SAXParseExceptionpublicId: -//AspectJ//DTD//EN; systemId: http://www.eclipse.org/aspectj/dtd/aspectj.dtd; lineNumber: 1; columnNumber: 2; The markup declarations contained or pointed to by the document type declaration must be well-formed.
With sbt 1.10.11 and sbt-assembly 2.3.1 this can be
lazy val aopMerge = CustomMergeStrategy("aopMerge") { (conflicts: Vector[Dependency]) =>
import scala.xml.*
import scala.xml.dtd.*
val parser = {
val factory = javax.xml.parsers.SAXParserFactory.newInstance
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
factory.newSAXParser
}
val xmls: Seq[Elem] = conflicts.map { conflict =>
Using.resource((dependency: Dependency) => dependency.stream())(conflict) { is: InputStream =>
XML.loadXML(Source.fromInputStream(is), parser)
// XML.load(is)
}
}
val dt = DocType("aspectj", PublicID("-//AspectJ//DTD//EN", "https://www.eclipse.org/aspectj/dtd/aspectj.dtd"), Nil)
val aspectsChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "aspects" \ "_")
val weaverChildren: Seq[Node] = xmls.flatMap(_ \\ "aspectj" \ "weaver" \ "_")
val options: String = xmls.map(x => (x \\ "aspectj" \ "weaver" \ "@options").text).mkString(" ").trim
val weaverAttr = if (options.isEmpty) Null else new UnprefixedAttribute("options", options, Null)
val aspects = new Elem(null, "aspects", Null, TopScope, false, aspectsChildren: _*)
val weaver = new Elem(null, "weaver", weaverAttr, TopScope, false, weaverChildren: _*)
val aspectj = new Elem(null, "aspectj", Null, TopScope, false, aspects, weaver)
val bytes = scala.util.Using.resource(new CharArrayWriter()) { writer =>
XML.write(writer, aspectj, "UTF-8", xmlDecl = false, dt)
(writer.toString + IO.Newline).getBytes(IO.defaultCharset)
}
Right(Vector(JarEntry(conflicts.head.target, () => new java.io.ByteArrayInputStream(bytes))))
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ok first of all: major kudos to @colestanfield! 'found this thread because i ran into the same issue with aop.xml with Kamon libraries ...this is probably a stupid question: i copied the
defaultMergeStrategy
from the sbt-assembly README file because of missing reference to this function error...am i supposed to do that or am i supposed to add some import?I wasn't too thrilled about putting this code in
build.sbt
...you can't add this to any .sbt file underproject
folder becausesbt
don't allow imports (at least for the current 0.13.11 version)...also, with the autoPlugins support...having a separate .sbt file (except for specific plugins that don't support autoplugin trait) is actually discouraged (i.e. you addsbt-assembly
plugin in theplugins.sbt
).anyway, so i created a
project/AssemblyMergeStrategies.scala
and import that in thebuild.sbt
file so i can use theassemblyMergeStrategy in assembly := customMergeStrategy
lineBTW
mergeStrategy
is already deprecated; useassemblyMergeStrategy
instead