All Downloads are FREE. Search and download functionalities are using the official Maven repository.

coursier.launcher.AssemblyGenerator.scala Maven / Gradle / Ivy

There is a newer version: 2.1.13
Show newest version
package coursier.launcher

import java.io.{ByteArrayInputStream, File, OutputStream}
import java.nio.file.Path
import java.util.jar.{Attributes => JarAttributes, JarOutputStream}
import java.util.zip.{CRC32, ZipEntry, ZipFile, ZipOutputStream}

import coursier.launcher.internal.{FileUtil, WrappedZipInputStream, Zip}

import scala.collection.mutable

object AssemblyGenerator extends Generator[Parameters.Assembly] {

  def generate(parameters: Parameters.Assembly, output: Path): Unit = {

    FileUtil.withOutputStream(output) { os =>

      for (p <- parameters.preambleOpt.map(_.value))
        os.write(p)

      make(
        parameters.files,
        os,
        parameters.finalAttributes,
        parameters.rules,
        parameters.extraZipEntries,
        parameters.baseManifest
      )
    }

    FileUtil.tryMakeExecutable(output)
  }

  private def make(
    jars: Seq[File],
    output: OutputStream,
    attributes: Seq[(JarAttributes.Name, String)],
    rules: Seq[MergeRule],
    extraZipEntries: Seq[(ZipEntry, Array[Byte])],
    baseManifestOpt: Option[Array[Byte]]
  ): Unit = {

    val manifest = new java.util.jar.Manifest
    for (baseManifest <- baseManifestOpt)
      manifest.read(new ByteArrayInputStream(baseManifest))
    manifest.getMainAttributes.put(JarAttributes.Name.MANIFEST_VERSION, "1.0")
    for ((k, v) <- attributes)
      manifest.getMainAttributes.put(k, v)

    var zos: ZipOutputStream = null

    try {
      zos = new JarOutputStream(output, manifest)
      writeEntries(jars.map(Right(_)), zos, rules, extraZipEntries)
    }
    finally if (zos != null)
        zos.close()
  }

  def writeEntries(
    jars: Seq[Either[() => WrappedZipInputStream, File]],
    zos: ZipOutputStream,
    rules: Seq[MergeRule]
  ): Unit =
    writeEntries(jars, zos, rules, Nil)

  private def writeEntries(
    jars: Seq[Either[() => WrappedZipInputStream, File]],
    zos: ZipOutputStream,
    rules: Seq[MergeRule],
    extraZipEntries: Seq[(ZipEntry, Array[Byte])]
  ): Unit = {

    val rulesMap        = rules.collect { case r: MergeRule.PathRule => r.path -> r }.toMap
    val excludePatterns = rules.collect { case e: MergeRule.ExcludePattern => e.path }
    val appendPatterns  = rules.collect { case a: MergeRule.AppendPattern => a.path }

    for ((ent, content) <- extraZipEntries) {
      zos.putNextEntry(ent)
      zos.write(content)
      zos.closeEntry()
    }

    val concatenatedEntries = new mutable.HashMap[String, ::[(ZipEntry, Array[Byte])]]

    var ignore = Set.empty[String]

    for (jar <- jars) {
      var zif: ZipFile               = null
      var zis: WrappedZipInputStream = null

      try {
        val entries =
          jar match {
            case Left(f) =>
              zis = f()
              zis.entriesWithData()
            case Right(f) =>
              zif = new ZipFile(f)
              Zip.zipEntries(zif)
          }

        for ((ent, content) <- entries) {

          def append(): Unit =
            concatenatedEntries += ent.getName -> ::(
              (ent, content),
              concatenatedEntries.getOrElse(ent.getName, Nil)
            )

          rulesMap.get(ent.getName) match {
            case Some(e: MergeRule.Exclude) =>
            // ignored

            case Some(a: MergeRule.Append) =>
              append()

            case None =>
              if (!excludePatterns.exists(_.matcher(ent.getName).matches()))
                if (appendPatterns.exists(_.matcher(ent.getName).matches()))
                  append()
                else if (!ignore(ent.getName)) {
                  ent.setCompressedSize(-1L)
                  zos.putNextEntry(ent)
                  zos.write(content)
                  zos.closeEntry()

                  ignore += ent.getName
                }
          }
        }

      }
      finally {
        if (zif != null)
          zif.close()
        if (zis != null)
          zis.close()
      }
    }

    for ((_, entries) <- concatenatedEntries) {
      val (ent, _) = entries.head

      ent.setCompressedSize(-1L)

      val content = entries.reverse.toArray.flatMap(_._2)

      if (entries.tail.nonEmpty) {
        ent.setSize(entries.map(_._2.length).sum)
        val crc = new CRC32
        crc.update(content)
        ent.setCrc(crc.getValue)
      }

      zos.putNextEntry(ent)
      zos.write(content)
      zos.closeEntry()
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy