From 04176454bf15c9eb59158cedf1b1357d7afa2045 Mon Sep 17 00:00:00 2001 From: Yu Changyuan Date: Fri, 21 Dec 2012 18:25:31 +0800 Subject: [PATCH] Add option to preserve service registry Add preserveServiceRegistry, serviceRegistryInclude & serviceRegistryExclude keys. When preserveServiceRegistry is true, files in META-INF/services will be merged, filtered and then insert into the final apk. Any class name that match any regexp in serviceRegistryInclude and not match any regexp in serviceRegistryExclude will be included. --- src/main/scala/AndroidBase.scala | 4 ++ src/main/scala/AndroidInstall.scala | 90 ++++++++++++++++++++++++++++- src/main/scala/AndroidKeys.scala | 6 +- 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/main/scala/AndroidBase.scala b/src/main/scala/AndroidBase.scala index 679b56e..c65b7e8 100644 --- a/src/main/scala/AndroidBase.scala +++ b/src/main/scala/AndroidBase.scala @@ -207,6 +207,10 @@ object AndroidBase { useProguard := true, proguardOptimizations := Seq.empty, + preserveServiceRegistry := false, + serviceRegistryInclude := Seq(".*"), + serviceRegistryExclude := Seq.empty, + jarPath <<= (platformPath, jarName) (_ / _), libraryJarPath <<= (jarPath (_ get)), diff --git a/src/main/scala/AndroidInstall.scala b/src/main/scala/AndroidInstall.scala index b923667..691cca8 100644 --- a/src/main/scala/AndroidInstall.scala +++ b/src/main/scala/AndroidInstall.scala @@ -162,13 +162,101 @@ object AndroidInstall { } } - private def packageTask(debug: Boolean):Project.Initialize[Task[File]] = (packageConfig, streams) map { (c, s) => + private def packageTask(debug: Boolean):Project.Initialize[Task[File]] = (packageConfig, fullClasspath, preserveServiceRegistry, serviceRegistryInclude, serviceRegistryExclude, streams) map { (c, cp, p, in, ex, s) => val builder = new ApkBuilder(c, debug) builder.build.fold(sys.error(_), s.log.info(_)) s.log.debug(builder.outputStream.toString) + + if (p) { + val services = extractServiceRegistry(cp.files, in, ex) + insertServiceRegistry(c.packageApkPath, services) + } + c.packageApkPath } + private def extractServiceRegistry( + cp: Seq[JFile], in: Seq[String], ex: Seq[String] + ): Map[String, List[String]] = { + import java.util.jar.JarFile + import scala.io.Source + import scala.collection.JavaConversions._ + + var ret = Map[String, List[String]]() + cp.filter(_.toString.matches(".*\\.jar")).foreach {f => { + val jarfile = new JarFile(f) + try { + for (entry <- jarfile.entries()) { + val name = entry.getName() + if (name.matches("META-INF/services/..*")) { + val is = jarfile.getInputStream(entry) + try { + val lns = Source.fromInputStream(is).getLines(). + filter(cls => in.exists(r => cls.matches(r))). + filter(cls => ex.forall(r => !cls.matches(r))) + ret += name -> (ret.getOrElse(name, List()) ++ lns) + } + finally { + is.close() + } + } + } + } + finally { + jarfile.close() + } + }} + + ret + } + + private def insertServiceRegistry(apk: File, + services: Map[String, List[String]]) = { + import java.util.zip.ZipOutputStream + import java.util.zip.ZipInputStream + import java.util.zip.ZipEntry + import java.io.FileOutputStream + import java.io.FileInputStream + import java.io.File + + val tmp = new File(apk.getAbsolutePath + ".tmp") + tmp.delete() + + if (!apk.renameTo(tmp)) { + throw new RuntimeException("could not rename file " + apk) + } + val buf = Array.ofDim[Byte](1024) + + val in = new ZipInputStream(new FileInputStream(tmp)) + val out = new ZipOutputStream(new FileOutputStream(apk)) + + try { + var entry = in.getNextEntry() + + while (entry != null) { + out.putNextEntry(new ZipEntry(entry.getName)) + var len = in.read(buf) + while (len > 0) { + out.write(buf, 0, len) + len = in.read(buf) + } + entry = in.getNextEntry() + } + + for ((name, value) <- services) { + if (value.size > 0) { + out.putNextEntry(new ZipEntry(name)) + out.write(value.mkString("\n").getBytes) + } + } + } + finally { + tmp.delete() + in.close() + out.close() + } + } + lazy val installerTasks = Seq ( installEmulator <<= installTask(emulator = true) dependsOn packageDebug, installDevice <<= installTask(emulator = false) dependsOn packageDebug diff --git a/src/main/scala/AndroidKeys.scala b/src/main/scala/AndroidKeys.scala index f8de3b5..62d5d9f 100644 --- a/src/main/scala/AndroidKeys.scala +++ b/src/main/scala/AndroidKeys.scala @@ -72,6 +72,10 @@ object AndroidKeys { val packageApkLibPath = TaskKey[File]("package-apklib-path") val useProguard = SettingKey[Boolean]("use-proguard") + val preserveServiceRegistry = SettingKey[Boolean]("preserve-service-registry") + val serviceRegistryInclude = SettingKey[Seq[String]]("service-registry-include") + val serviceRegistryExclude = SettingKey[Seq[String]]("service-registry-exclude") + /** Install Settings */ val packageConfig = TaskKey[ApkConfig]("package-config", "Generates a Apk Config") @@ -81,7 +85,7 @@ object AndroidKeys { val typedResource = TaskKey[File]("typed-resource", """Typed resource file to be generated, also includes interfaces to access these resources.""") - val layoutResources = TaskKey[Seq[File]]("layout-resources", + val layoutResources = TaskKey[Seq[File]]("layout-resources", """All files that are in res/layout. They will be accessable through TR.layouts._""")