aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-03-01 01:19:04 +0000
committerChristopher Vogt <oss.nsp@cvogt.org>2017-03-01 08:48:17 -0500
commit15afa8b71c4a42c1969d58c0d35e1fc3a70b16ad (patch)
tree533646cee8047a796d9f7e3b0c5eedd580c90ba1 /plugins
parent1a2da7da5b003b8b473f67706a04256f49c86c6f (diff)
downloadcbt-15afa8b71c4a42c1969d58c0d35e1fc3a70b16ad.tar.gz
cbt-15afa8b71c4a42c1969d58c0d35e1fc3a70b16ad.tar.bz2
cbt-15afa8b71c4a42c1969d58c0d35e1fc3a70b16ad.zip
add comprehensive, type-safe proguard plugin
this also demonstrates how to programmatically generate an extensive, type-safe api for a stringly-typed interface
Diffstat (limited to 'plugins')
-rw-r--r--plugins/proguard/build/build.scala129
-rw-r--r--plugins/proguard/build/build/build.scala13
-rw-r--r--plugins/proguard/spec/refcard.html259
-rw-r--r--plugins/proguard/src/generated/Proguard.scala226
-rw-r--r--plugins/proguard/templates/Proguard.scala85
5 files changed, 712 insertions, 0 deletions
diff --git a/plugins/proguard/build/build.scala b/plugins/proguard/build/build.scala
new file mode 100644
index 0000000..5edd7a8
--- /dev/null
+++ b/plugins/proguard/build/build.scala
@@ -0,0 +1,129 @@
+package cbt_build.proguard
+import cbt._
+import java.nio.file.Files._
+import java.net._
+import java.io._
+import scala.xml._
+class Build(val context: Context) extends Plugin with Scalafmt{
+ override def dependencies = (
+ super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
+ Resolver( mavenCentral ).bind(
+ MavenDependency("net.sf.proguard","proguard-base","5.3.2")
+ ) :+ libraries.captureArgs
+ )
+
+ def refcard = projectDirectory / "spec/refcard.html"
+
+ /** downloads html proguard parameter specification */
+ def updateSpec = {
+ System.err.println(lib.blue("downloading ")+refcard)
+ lib.download(
+ new URL("https://www.guardsquare.com/en/proguard/manual/refcard"),
+ refcard,
+ None,
+ replace = true
+ )
+ System.err.println("simplifying html")
+ val tables = (
+ loadSloppyHtml( new String( readAllBytes( refcard.toPath ) ) ) \ "body" \\ "table"
+ )
+ val s = (
+ "<html><body>\n" ++ tables.map( table =>
+ " <table>\n" ++ (table \\ "tr").map( tr =>
+ " <tr>\n" ++ (tr \\ "td").map( td =>
+ " <td>" ++ td.text ++ "</td>\n"
+ ).mkString ++ " </tr>\n"
+ ).mkString ++ " </table>\n"
+ ).mkString ++ "</body></html>\n"
+ )
+ System.err.println("writing file")
+ write( refcard.toPath, s.getBytes)
+ }
+
+ private def loadSloppyHtml(html: String): scala.xml.Elem = {
+ object XmlNotDownloadingDTD extends scala.xml.factory.XMLLoader[scala.xml.Elem] {
+ override def parser: javax.xml.parsers.SAXParser = {
+ val f = javax.xml.parsers.SAXParserFactory.newInstance()
+ f.setNamespaceAware(false)
+ f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ f.newSAXParser()
+ }
+ }
+
+ val p = new org.ccil.cowan.tagsoup.Parser
+ val w = new StringWriter
+ p.setContentHandler(new org.ccil.cowan.tagsoup.XMLWriter(w))
+ p.parse(
+ new org.xml.sax.InputSource(
+ new ByteArrayInputStream( "<!DOCTYPE[^<]*>".r.replaceFirstIn( html, "" ).getBytes )
+ )
+ )
+ XmlNotDownloadingDTD.loadString( w.toString )
+ }
+
+ /** generates Scala code from parameter specification html */
+ def generate = {
+ val tables = XML.loadFile(refcard) \\ "table"
+ def cellsToSeq( node: Node ) = (node \\ "tr").map(
+ tr => (tr \\ "td").map( td => td.text ) match {
+ case Seq( k, v ) => k -> v
+ }
+ )
+ val options = cellsToSeq( tables(0) ).collect{
+ case (k, v) if k.startsWith("-") => k.drop(1).split(" ").toList -> v
+ }.map{
+ case (k,description) =>
+ val name = k(0)
+ val tpe = k.drop(1).mkString(" ") match {
+ case "" => "Boolean"
+ case "n" => "Int"
+ case "class_specification" | "version" | "optimization_filter" => "String"
+ case "filename" | "directoryname" => "File"
+ case "class_path" if name === "outjars" => "Seq[File]"
+ case "class_path" => "ClassPath"
+ case "[filename]" => "Option[File]"
+ case "[directory_filter]" | "[package_filter]" | "[package_name]"
+ | "[attribute_filter]" | "[string]" | "[class_filter]" | "[file_filter]"
+ => "Option[String]"
+ case "[,modifier,...] class_specification" => "(Seq[KeepOptionModifier], String)"
+ }
+ (name, tpe, description.split("\n").mkString(" "))
+ }.sortBy(_._1)
+
+ val docs = options.map{
+ case (name, tpe, description) => s" @param $name $description"
+ }.mkString("\n")
+
+ val args = options.map{
+ case v@(_, "Boolean", _) => v -> Some("false")
+ case v@("injars" | "libraryjars" | "keep" | "outjars", _, _) => v -> None
+ case (n, t, d) => (n, s"Option[$t]", d) -> Some("None")
+ }.map{
+ case ((name, tpe, description), default) => s" $name: $tpe" ++ default.map(" = "++_).getOrElse("")
+ }.mkString(",\n")
+
+ val keepModifiers = cellsToSeq( tables(2) ).map{
+ case (k, v) => s""" /** $v */\n object $k extends KeepOptionModifier("$k")"""
+ }.mkString("\n")
+
+ val template = new String(
+ readAllBytes(
+ (projectDirectory / "templates/Proguard.scala").toPath
+ )
+ )
+ val code = (
+ "/* automatically generated by build/build.scala from templates/Proguard.scala */\n" ++
+ template
+ .replace ("/* ${generated-top-level} */", keepModifiers )
+ .replace( "${generated-docs}", docs )
+ .replace( "/* ${generated-args} */", args )
+ )
+
+ val targetFile = projectDirectory / "src/generated/Proguard.scala"
+ targetFile.getParentFile.mkdirs
+ write( targetFile.toPath, code.getBytes )
+
+ scalafmt
+ compile
+ }
+}
diff --git a/plugins/proguard/build/build/build.scala b/plugins/proguard/build/build/build.scala
new file mode 100644
index 0000000..7928cfa
--- /dev/null
+++ b/plugins/proguard/build/build/build.scala
@@ -0,0 +1,13 @@
+package cbt_build.proguard.build
+import cbt._
+class Build(val context: Context) extends BuildBuild{
+ override def dependencies = (
+ super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
+ Resolver( mavenCentral, sonatypeReleases ).bind(
+ ScalaDependency("org.scala-lang.modules","scala-xml","1.0.5"),
+ "org.ccil.cowan.tagsoup" % "tagsoup" % "1.2.1"
+ ) ++ Seq(
+ plugins.scalafmt
+ )
+ )
+}
diff --git a/plugins/proguard/spec/refcard.html b/plugins/proguard/spec/refcard.html
new file mode 100644
index 0000000..9cb0c72
--- /dev/null
+++ b/plugins/proguard/spec/refcard.html
@@ -0,0 +1,259 @@
+<html><body>
+ <table>
+ <tr>
+ <td>@filename</td>
+ <td>Short for '-includefilename'. </td>
+ </tr>
+ <tr>
+ <td>-include filename</td>
+ <td>Read configuration options from the given file.</td>
+ </tr>
+ <tr>
+ <td>-basedirectory directoryname</td>
+ <td>Specifies the base directory for subsequent relative file names.</td>
+ </tr>
+ <tr>
+ <td>-injars class_path</td>
+ <td>Specifies the program jars (or wars, ears, zips, or directories).</td>
+ </tr>
+ <tr>
+ <td>-outjars class_path</td>
+ <td>Specifies the names of the output jars (or wars, ears, zips, or directories).</td>
+ </tr>
+ <tr>
+ <td>-libraryjars class_path</td>
+ <td>Specifies the library jars (or wars, ears, zips, or directories).</td>
+ </tr>
+ <tr>
+ <td>-skipnonpubliclibraryclasses</td>
+ <td>Ignore non-public library classes.</td>
+ </tr>
+ <tr>
+ <td>-dontskipnonpubliclibraryclasses</td>
+ <td>Don't ignore non-public library classes (the default).</td>
+ </tr>
+ <tr>
+ <td>-dontskipnonpubliclibraryclassmembers</td>
+ <td>Don't ignore package visible library class members.</td>
+ </tr>
+ <tr>
+ <td>-keepdirectories [directory_filter]</td>
+ <td>Keep the specified directories in the output jars (or wars, ears, zips, or directories).</td>
+ </tr>
+ <tr>
+ <td>-target version</td>
+ <td>Set the given version number in the processed classes.</td>
+ </tr>
+ <tr>
+ <td>-forceprocessing</td>
+ <td>Process the input, even if the output seems up to date.</td>
+ </tr>
+ <tr>
+ <td>-keep [,modifier,...] class_specification</td>
+ <td>Preserve the specified classes and class members.</td>
+ </tr>
+ <tr>
+ <td>-keepclassmembers [,modifier,...] class_specification</td>
+ <td>Preserve the specified class members, if their classes are preserved as well.</td>
+ </tr>
+ <tr>
+ <td>-keepclasseswithmembers [,modifier,...] class_specification</td>
+ <td>Preserve the specified classes and class members, if all of the specified class members are present.</td>
+ </tr>
+ <tr>
+ <td>-keepnames class_specification</td>
+ <td>Preserve the names of the specified classes and class members (if they aren't removed in the shrinking step).</td>
+ </tr>
+ <tr>
+ <td>-keepclassmembernames class_specification</td>
+ <td>Preserve the names of the specified class members (if they aren't removed in the shrinking step).</td>
+ </tr>
+ <tr>
+ <td>-keepclasseswithmembernames class_specification</td>
+ <td>Preserve the names of the specified classes and class members, if all of the specified class members are present (after the shrinking step).</td>
+ </tr>
+ <tr>
+ <td>-printseeds [filename]</td>
+ <td>List classes and class members matched by the various -keep options, to the standard output or to the given file.</td>
+ </tr>
+ <tr>
+ <td>-dontshrink</td>
+ <td>Don't shrink the input class files.</td>
+ </tr>
+ <tr>
+ <td>-printusage [filename]</td>
+ <td>List dead code of the input class files, to the standard output or to the given file.</td>
+ </tr>
+ <tr>
+ <td>-whyareyoukeeping class_specification</td>
+ <td>Print details on why the given classes and class members are being kept in the shrinking step.</td>
+ </tr>
+ <tr>
+ <td>-dontoptimize</td>
+ <td>Don't optimize the input class files.</td>
+ </tr>
+ <tr>
+ <td>-optimizations optimization_filter</td>
+ <td>The optimizations to be enabled and disabled.</td>
+ </tr>
+ <tr>
+ <td>-optimizationpasses n</td>
+ <td>The number of optimization passes to be performed.</td>
+ </tr>
+ <tr>
+ <td>-assumenosideeffects class_specification</td>
+ <td>Assume that the specified methods don't have any side effects, while optimizing.</td>
+ </tr>
+ <tr>
+ <td>-allowaccessmodification</td>
+ <td>Allow the access modifiers of classes and class members to be modified, while optimizing.</td>
+ </tr>
+ <tr>
+ <td>-mergeinterfacesaggressively</td>
+ <td>Allow any interfaces to be merged, while optimizing.</td>
+ </tr>
+ <tr>
+ <td>-dontobfuscate</td>
+ <td>Don't obfuscate the input class files.</td>
+ </tr>
+ <tr>
+ <td>-printmapping [filename]</td>
+ <td>Print the mapping from old names to new names for classes and class members that have been renamed, to the standard output or to the given file.</td>
+ </tr>
+ <tr>
+ <td>-applymapping filename</td>
+ <td>Reuse the given mapping, for incremental obfuscation.</td>
+ </tr>
+ <tr>
+ <td>-obfuscationdictionary filename</td>
+ <td>Use the words in the given text file as obfuscated field names and method names.</td>
+ </tr>
+ <tr>
+ <td>-classobfuscationdictionary filename</td>
+ <td>Use the words in the given text file as obfuscated class names.</td>
+ </tr>
+ <tr>
+ <td>-packageobfuscationdictionary filename</td>
+ <td>Use the words in the given text file as obfuscated package names.</td>
+ </tr>
+ <tr>
+ <td>-overloadaggressively</td>
+ <td>Apply aggressive overloading while obfuscating.</td>
+ </tr>
+ <tr>
+ <td>-useuniqueclassmembernames</td>
+ <td>Ensure uniform obfuscated class member names for subsequent incremental obfuscation.</td>
+ </tr>
+ <tr>
+ <td>-dontusemixedcaseclassnames</td>
+ <td>Don't generate mixed-case class names while obfuscating.</td>
+ </tr>
+ <tr>
+ <td>-keeppackagenames [package_filter]</td>
+ <td>Keep the specified package names from being obfuscated.</td>
+ </tr>
+ <tr>
+ <td>-flattenpackagehierarchy [package_name]</td>
+ <td>Repackage all packages that are renamed into the single given parent package.</td>
+ </tr>
+ <tr>
+ <td>-repackageclasses [package_name]</td>
+ <td>Repackage all class files that are renamed into the single given package.</td>
+ </tr>
+ <tr>
+ <td>-keepattributes [attribute_filter]</td>
+ <td>Preserve the given optional attributes; typically Exceptions, InnerClasses, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, and *Annotation*.</td>
+ </tr>
+ <tr>
+ <td>-keepparameternames</td>
+ <td>Keep the parameter names and types of methods that are kept.</td>
+ </tr>
+ <tr>
+ <td>-renamesourcefileattribute [string]</td>
+ <td>Put the given constant string in the SourceFile attributes.</td>
+ </tr>
+ <tr>
+ <td>-adaptclassstrings [class_filter]</td>
+ <td>Adapt string constants in the specified classes, based on the obfuscated names of any corresponding classes.</td>
+ </tr>
+ <tr>
+ <td>-adaptresourcefilenames [file_filter]</td>
+ <td>Rename the specified resource files, based on the obfuscated names of the corresponding class files.</td>
+ </tr>
+ <tr>
+ <td>-adaptresourcefilecontents [file_filter]</td>
+ <td>Update the contents of the specified resource files, based on the obfuscated names of the processed classes.</td>
+ </tr>
+ <tr>
+ <td>-dontpreverify</td>
+ <td>Don't preverify the processed class files.</td>
+ </tr>
+ <tr>
+ <td>-microedition</td>
+ <td>Target the processed class files at Java Micro Edition.</td>
+ </tr>
+ <tr>
+ <td>-verbose</td>
+ <td>Write out some more information during processing.</td>
+ </tr>
+ <tr>
+ <td>-dontnote [class_filter]</td>
+ <td>Don't print notes about potential mistakes or omissions in the configuration.</td>
+ </tr>
+ <tr>
+ <td>-dontwarn [class_filter]</td>
+ <td>Don't warn about unresolved references at all.</td>
+ </tr>
+ <tr>
+ <td>-ignorewarnings</td>
+ <td>Print warnings about unresolved references, but continue processing anyhow.</td>
+ </tr>
+ <tr>
+ <td>-printconfiguration [filename]</td>
+ <td>Write out the entire configuration in traditional ProGuard style, to the standard output or to the given file.</td>
+ </tr>
+ <tr>
+ <td>-dump [filename]</td>
+ <td>Write out the internal structure of the processed class files, to the standard output or to the given file.</td>
+ </tr>
+ </table>
+ <table>
+ <tr>
+ <td>From being removed or renamed</td>
+ <td>From being renamed</td>
+ </tr>
+ <tr>
+ <td>Classes and class members</td>
+ <td>-keep</td>
+ <td>-keepnames</td>
+ </tr>
+ <tr>
+ <td>Class members only</td>
+ <td>-keepclassmembers</td>
+ <td>-keepclassmembernames</td>
+ </tr>
+ <tr>
+ <td>Classes and class members, if class members present</td>
+ <td>-keepclasseswithmembers</td>
+ <td>-keepclasseswithmembernames</td>
+ </tr>
+ </table>
+ <table>
+ <tr>
+ <td>includedescriptorclasses</td>
+ <td>Also keep any classes in the descriptors of specified fields and methods.</td>
+ </tr>
+ <tr>
+ <td>allowshrinking</td>
+ <td>Allow the specified entry points to be removed in the shrinking step.</td>
+ </tr>
+ <tr>
+ <td>allowoptimization</td>
+ <td>Allow the specified entry points to be modified in the optimization step.</td>
+ </tr>
+ <tr>
+ <td>allowobfuscation</td>
+ <td>Allow the specified entry points to be renamed in the obfuscation step.</td>
+ </tr>
+ </table>
+</body></html>
diff --git a/plugins/proguard/src/generated/Proguard.scala b/plugins/proguard/src/generated/Proguard.scala
new file mode 100644
index 0000000..553df07
--- /dev/null
+++ b/plugins/proguard/src/generated/Proguard.scala
@@ -0,0 +1,226 @@
+/* automatically generated by build/build.scala from templates/Proguard.scala */
+package cbt
+import java.io.File
+import java.nio.file.Files.deleteIfExists
+
+sealed class KeepOptionModifier(val string: String)
+object KeepOptionModifier {
+
+ /** Also keep any classes in the descriptors of specified fields and methods. */
+ object includedescriptorclasses
+ extends KeepOptionModifier("includedescriptorclasses")
+
+ /** Allow the specified entry points to be removed in the shrinking step. */
+ object allowshrinking extends KeepOptionModifier("allowshrinking")
+
+ /** Allow the specified entry points to be modified in the optimization step. */
+ object allowoptimization extends KeepOptionModifier("allowoptimization")
+
+ /** Allow the specified entry points to be renamed in the obfuscation step. */
+ object allowobfuscation extends KeepOptionModifier("allowobfuscation")
+}
+
+trait Proguard extends BaseBuild {
+ def proguardKeep(keep: (Seq[KeepOptionModifier], String)) = {
+ ProguardLib(context.cbtLastModified, context.paths.mavenCache).proguard(
+ outjars = Seq(scalaTarget / "proguarded.jar"),
+ injars = classpath,
+ libraryjars = Proguard.`rt.jar`,
+ keep = keep
+ )
+ }
+}
+
+object Proguard {
+ val version = "5.3.2"
+ val `rt.jar` = ClassPath(
+ Seq(new File(System.getProperty("java.home"), "lib/rt.jar")))
+}
+
+case class ProguardLib(
+ cbtLastModified: Long,
+ mavenCache: File,
+ dependency: Option[DependencyImplementation] = None
+)(
+ implicit logger: Logger,
+ transientCache: java.util.Map[AnyRef, AnyRef],
+ classLoaderCache: ClassLoaderCache
+) {
+
+ /**
+ Typed interface on top of the proguard command line tool.
+ Check the official ProGuard docs for usage.
+ Use `Some(None)` to call an option without arguments.
+ Use `true` to set a flag.
+
+ @see https://www.guardsquare.com/en/proguard/manual/refcard
+ @see https://www.guardsquare.com/en/proguard/manual/usage
+
+ @param adaptclassstrings Adapt string constants in the specified classes, based on the obfuscated names of any corresponding classes.
+ @param adaptresourcefilecontents Update the contents of the specified resource files, based on the obfuscated names of the processed classes.
+ @param adaptresourcefilenames Rename the specified resource files, based on the obfuscated names of the corresponding class files.
+ @param allowaccessmodification Allow the access modifiers of classes and class members to be modified, while optimizing.
+ @param applymapping Reuse the given mapping, for incremental obfuscation.
+ @param assumenosideeffects Assume that the specified methods don't have any side effects, while optimizing.
+ @param basedirectory Specifies the base directory for subsequent relative file names.
+ @param classobfuscationdictionary Use the words in the given text file as obfuscated class names.
+ @param dontnote Don't print notes about potential mistakes or omissions in the configuration.
+ @param dontobfuscate Don't obfuscate the input class files.
+ @param dontoptimize Don't optimize the input class files.
+ @param dontpreverify Don't preverify the processed class files.
+ @param dontshrink Don't shrink the input class files.
+ @param dontskipnonpubliclibraryclasses Don't ignore non-public library classes (the default).
+ @param dontskipnonpubliclibraryclassmembers Don't ignore package visible library class members.
+ @param dontusemixedcaseclassnames Don't generate mixed-case class names while obfuscating.
+ @param dontwarn Don't warn about unresolved references at all.
+ @param dump Write out the internal structure of the processed class files, to the standard output or to the given file.
+ @param flattenpackagehierarchy Repackage all packages that are renamed into the single given parent package.
+ @param forceprocessing Process the input, even if the output seems up to date.
+ @param ignorewarnings Print warnings about unresolved references, but continue processing anyhow.
+ @param include Read configuration options from the given file.
+ @param injars Specifies the program jars (or wars, ears, zips, or directories).
+ @param keep Preserve the specified classes and class members.
+ @param keepattributes Preserve the given optional attributes; typically Exceptions, InnerClasses, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, and *Annotation*.
+ @param keepclasseswithmembernames Preserve the names of the specified classes and class members, if all of the specified class members are present (after the shrinking step).
+ @param keepclasseswithmembers Preserve the specified classes and class members, if all of the specified class members are present.
+ @param keepclassmembernames Preserve the names of the specified class members (if they aren't removed in the shrinking step).
+ @param keepclassmembers Preserve the specified class members, if their classes are preserved as well.
+ @param keepdirectories Keep the specified directories in the output jars (or wars, ears, zips, or directories).
+ @param keepnames Preserve the names of the specified classes and class members (if they aren't removed in the shrinking step).
+ @param keeppackagenames Keep the specified package names from being obfuscated.
+ @param keepparameternames Keep the parameter names and types of methods that are kept.
+ @param libraryjars Specifies the library jars (or wars, ears, zips, or directories).
+ @param mergeinterfacesaggressively Allow any interfaces to be merged, while optimizing.
+ @param microedition Target the processed class files at Java Micro Edition.
+ @param obfuscationdictionary Use the words in the given text file as obfuscated field names and method names.
+ @param optimizationpasses The number of optimization passes to be performed.
+ @param optimizations The optimizations to be enabled and disabled.
+ @param outjars Specifies the names of the output jars (or wars, ears, zips, or directories).
+ @param overloadaggressively Apply aggressive overloading while obfuscating.
+ @param packageobfuscationdictionary Use the words in the given text file as obfuscated package names.
+ @param printconfiguration Write out the entire configuration in traditional ProGuard style, to the standard output or to the given file.
+ @param printmapping Print the mapping from old names to new names for classes and class members that have been renamed, to the standard output or to the given file.
+ @param printseeds List classes and class members matched by the various -keep options, to the standard output or to the given file.
+ @param printusage List dead code of the input class files, to the standard output or to the given file.
+ @param renamesourcefileattribute Put the given constant string in the SourceFile attributes.
+ @param repackageclasses Repackage all class files that are renamed into the single given package.
+ @param skipnonpubliclibraryclasses Ignore non-public library classes.
+ @param target Set the given version number in the processed classes.
+ @param useuniqueclassmembernames Ensure uniform obfuscated class member names for subsequent incremental obfuscation.
+ @param verbose Write out some more information during processing.
+ @param whyareyoukeeping Print details on why the given classes and class members are being kept in the shrinking step.
+ */
+ case class proguard(
+ adaptclassstrings: Option[Option[String]] = None,
+ adaptresourcefilecontents: Option[Option[String]] = None,
+ adaptresourcefilenames: Option[Option[String]] = None,
+ allowaccessmodification: Boolean = false,
+ applymapping: Option[File] = None,
+ assumenosideeffects: Option[String] = None,
+ basedirectory: Option[File] = None,
+ classobfuscationdictionary: Option[File] = None,
+ dontnote: Option[Option[String]] = None,
+ dontobfuscate: Boolean = false,
+ dontoptimize: Boolean = false,
+ dontpreverify: Boolean = false,
+ dontshrink: Boolean = false,
+ dontskipnonpubliclibraryclasses: Boolean = false,
+ dontskipnonpubliclibraryclassmembers: Boolean = false,
+ dontusemixedcaseclassnames: Boolean = false,
+ dontwarn: Option[Option[String]] = None,
+ dump: Option[Option[File]] = None,
+ flattenpackagehierarchy: Option[Option[String]] = None,
+ forceprocessing: Boolean = false,
+ ignorewarnings: Boolean = false,
+ include: Option[File] = None,
+ injars: ClassPath,
+ keep: (Seq[KeepOptionModifier], String),
+ keepattributes: Option[Option[String]] = None,
+ keepclasseswithmembernames: Option[String] = None,
+ keepclasseswithmembers: Option[(Seq[KeepOptionModifier], String)] = None,
+ keepclassmembernames: Option[String] = None,
+ keepclassmembers: Option[(Seq[KeepOptionModifier], String)] = None,
+ keepdirectories: Option[Option[String]] = None,
+ keepnames: Option[String] = None,
+ keeppackagenames: Option[Option[String]] = None,
+ keepparameternames: Boolean = false,
+ libraryjars: ClassPath,
+ mergeinterfacesaggressively: Boolean = false,
+ microedition: Boolean = false,
+ obfuscationdictionary: Option[File] = None,
+ optimizationpasses: Option[Int] = None,
+ optimizations: Option[String] = None,
+ outjars: Seq[File],
+ overloadaggressively: Boolean = false,
+ packageobfuscationdictionary: Option[File] = None,
+ printconfiguration: Option[Option[File]] = None,
+ printmapping: Option[Option[File]] = None,
+ printseeds: Option[Option[File]] = None,
+ printusage: Option[Option[File]] = None,
+ renamesourcefileattribute: Option[Option[String]] = None,
+ repackageclasses: Option[Option[String]] = None,
+ skipnonpubliclibraryclasses: Boolean = false,
+ target: Option[String] = None,
+ useuniqueclassmembernames: Boolean = false,
+ verbose: Boolean = false,
+ whyareyoukeeping: Option[String] = None
+ ) extends (() => ClassPath) {
+
+ // type class rendering scala values into string arguments
+ private class valueToStrings[T](val apply: T => Option[Seq[String]])
+ private object valueToStrings {
+ def apply[T: valueToStrings](value: T) =
+ implicitly[valueToStrings[T]].apply(value)
+ implicit object SeqFile
+ extends valueToStrings[Seq[File]](v => Some(v.map(_.string)))
+ implicit object ClassPath
+ extends valueToStrings[ClassPath](v => Some(Seq(v.string)))
+ implicit object File
+ extends valueToStrings[File](v => Some(Seq(v.string)))
+ implicit object String extends valueToStrings[String](v => Some(Seq(v)))
+ implicit object Int
+ extends valueToStrings[Int](i => Some(Seq(i.toString)))
+ implicit object Boolean
+ extends valueToStrings[Boolean]({
+ case false => None
+ case true => Some(Nil)
+ })
+ implicit def Option2[T: valueToStrings]: valueToStrings[Option[T]] =
+ new valueToStrings(
+ _.map(implicitly[valueToStrings[T]].apply(_).toSeq.flatten)
+ )
+ implicit def Option3[T: valueToStrings]
+ : valueToStrings[Option[Option[String]]] =
+ new valueToStrings(_.map(_.toSeq))
+ implicit def SpecWithModifiers: valueToStrings[(Seq[KeepOptionModifier],
+ String)] =
+ new valueToStrings({
+ case (modifiers, spec) =>
+ Some(
+ Seq(modifiers.map(_.string).map("," ++ _).mkString)
+ .filterNot(_ == "") :+ spec)
+ })
+ }
+
+ // capture string argument values and names
+ val capturedArgs = capture_args.captureArgs
+
+ def apply: ClassPath = {
+ val args = capturedArgs.args
+ .map(arg => arg.copy(name = "-" ++ arg.name))
+ .flatMap(_.toSeqOption)
+ .flatten
+ outjars.map(_.toPath).map(deleteIfExists)
+ val c = dependency getOrElse MavenResolver(cbtLastModified,
+ mavenCache,
+ mavenCentral).bindOne(
+ MavenDependency("net.sf.proguard", "proguard-base", Proguard.version)
+ ) runMain (
+ "proguard.ProGuard",
+ args: _*
+ )
+ if (c != ExitCode.Success) throw new Exception
+ ClassPath(outjars)
+ }
+ }
+}
diff --git a/plugins/proguard/templates/Proguard.scala b/plugins/proguard/templates/Proguard.scala
new file mode 100644
index 0000000..23a3117
--- /dev/null
+++ b/plugins/proguard/templates/Proguard.scala
@@ -0,0 +1,85 @@
+package cbt
+import java.io.File
+import java.nio.file.Files.deleteIfExists
+
+sealed class KeepOptionModifier(val string: String)
+object KeepOptionModifier{
+/* ${generated-top-level} */
+}
+
+trait Proguard extends BaseBuild {
+ def proguard( keep: (Seq[KeepOptionModifier], String) ) = {
+ ProguardLib(context.cbtLastModified, context.paths.mavenCache).proguard(
+ outjars = Seq( scalaTarget / "proguarded.jar" ),
+ injars = classpath,
+ libraryjars = Proguard.`rt.jar`,
+ keep = keep
+ )
+ }
+}
+
+object Proguard{
+ val version = "5.3.2"
+ val `rt.jar` = ClassPath( Seq( new File(System.getProperty("java.home"),"lib/rt.jar") ) )
+}
+
+case class ProguardLib(
+ cbtLastModified: Long, mavenCache: File,
+ dependency: Option[DependencyImplementation] = None
+)(
+ implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef], classLoaderCache: ClassLoaderCache
+){
+ /**
+ Typed interface on top of the proguard command line tool.
+ Check the official ProGuard docs for usage.
+ Use `Some(None)` to call an option without arguments.
+ Use `true` to set a flag.
+
+ @see https://www.guardsquare.com/en/proguard/manual/refcard
+ @see https://www.guardsquare.com/en/proguard/manual/usage
+
+${generated-docs}
+ */
+ case class proguard(
+/* ${generated-args} */
+ ) extends ( () => ClassPath ){
+
+ // type class rendering scala values into string arguments
+ private class valueToStrings[T]( val apply: T => Option[Seq[String]] )
+ private object valueToStrings{
+ def apply[T:valueToStrings](value: T) = implicitly[valueToStrings[T]].apply(value)
+ implicit object SeqFile extends valueToStrings[Seq[File]](v => Some(v.map(_.string)))
+ implicit object ClassPath extends valueToStrings[ClassPath](v => Some(Seq(v.string)))
+ implicit object File extends valueToStrings[File](v => Some(Seq(v.string)))
+ implicit object String extends valueToStrings[String](v => Some(Seq(v)))
+ implicit object Int extends valueToStrings[Int](i => Some(Seq(i.toString)))
+ implicit object Boolean extends valueToStrings[Boolean]({
+ case false => None
+ case true => Some(Nil)
+ })
+ implicit def Option2[T:valueToStrings]: valueToStrings[Option[T]] = new valueToStrings(
+ _.map(implicitly[valueToStrings[T]].apply(_).toSeq.flatten)
+ )
+ implicit def Option3[T:valueToStrings]: valueToStrings[Option[Option[String]]] = new valueToStrings(_.map(_.toSeq))
+ implicit def SpecWithModifiers: valueToStrings[(Seq[KeepOptionModifier], String)] = new valueToStrings({
+ case (modifiers, spec) => Some( Seq( modifiers.map(_.string).map(","++_).mkString ).filterNot(_ == "") :+ spec )
+ })
+ }
+
+ // capture string argument values and names
+ val capturedArgs = capture_args.captureArgs
+
+ def apply: ClassPath = {
+ val args = capturedArgs.args.map(arg => arg.copy(name="-"++arg.name)).flatMap(_.toSeqOption).flatten
+ outjars.map(_.toPath).map(deleteIfExists)
+ val c = dependency getOrElse MavenResolver( cbtLastModified, mavenCache, mavenCentral ).bindOne(
+ MavenDependency("net.sf.proguard","proguard-base",Proguard.version)
+ ) runMain (
+ "proguard.ProGuard",
+ args : _*
+ )
+ if(c != ExitCode.Success) throw new Exception
+ ClassPath(outjars)
+ }
+ }
+}