-
-
Save colestanfield/fac042d3108b0c06e952 to your computer and use it in GitHub Desktop.
// 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 |
Thanks @colestanfield... this works great! I would modify the first line to
val aopMerge = new sbtassembly.MergeStrategy {
so folks don't need to import MergeStrategy.
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 under project
folder because sbt
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 add sbt-assembly
plugin in the plugins.sbt
).
anyway, so i created a project/AssemblyMergeStrategies.scala
and import that in the build.sbt
file so i can use the assemblyMergeStrategy in assembly := customMergeStrategy
line
BTW mergeStrategy
is already deprecated; use assemblyMergeStrategy
instead
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))))
}
Thank you @colestanfield. I had a
and it wouldn't work. Your aopMerge strategy works just fine!