Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proguard contrib module #972

Merged
merged 9 commits into from
Nov 2, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ object contrib extends MillModule {
}
}

object proguard extends MillModule {
override def compileModuleDeps = Seq(scalalib)
}

object tut extends MillModule {
override def compileModuleDeps = Seq(scalalib)
def testArgs = T{
Expand Down
129 changes: 129 additions & 0 deletions contrib/proguard/src/Proguard.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package mill.contrib.proguard

import coursier.Repositories
import mill.T
import mill.Agg
import mill.api.{Logger, Loose, PathRef, Result}
import mill.define.{Sources, Target}
import mill.scalalib.Lib.resolveDependencies
import mill.scalalib.{Dep, DepSyntax, Lib, ScalaModule}
import os.proc
import os.Path
import os.PathChunk

/** Adds proguard capabilities when mixed-in to a module
*
* The target name is `proguard`. This runs proguard on the output jar of `asssembly`
* and outputs a shrunk/obfuscated/optimized jar under `out.jar` in the `dest/` folder.
*
* Sensible defaults are provided, so no members require overriding..
*
*/
trait Proguard extends ScalaModule {

/** The version of proguard to download from Maven.
*
* Note that currently this is the only available version. */
def proguardVersion: T[String] = T { "7.0.0" }

/** Run the "shrink" step in the proguard pipeline. Defaults to true. */
def shrink: T[Boolean] = T { true }

/** Run the "optimize" step in the proguard pipeline. Defaults to true. */
def optimize: T[Boolean] = T { true }

/** Run the "obfuscate" step in the proguard pipeline. Defaults to true. */
def obfuscate: T[Boolean] = T { true }

/** Run the "optimize" step in the proguard pipeline. Defaults to true.
*
* Note that this is required for Java 7 and above.
*/
def preverify: T[Boolean] = T { true }

/** The path to JAVA_HOME.
*
* This is used for both the `java` command binary,
* as well as the standard library jars.
* Defaults to the `java.home` system property. */
def javaHome: T[PathRef] = T {
PathRef(Path(System.getProperty("java.home")))
}

/** Specifies the input jar to proguard. Defaults to the output of the `assembly` task. */ */
def inJar: T[PathRef] = T { assembly() }

/** The library jars proguard requires
* Defaults the jars under `javaHome`. */
def libraryJars: T[Seq[PathRef]] = T {
val javaJars = os.list(javaHome().path / "lib", sort = false).filter(_.ext == "jar")
javaJars.toSeq.map(PathRef(_))
}

/** Run the proguard task.
*
* The full command will be printed when run.
* The stdout and stderr of the command are written to the `dest/` folder.
* The output jar is written to `dest/our.jar`. */
def proguard: T[PathRef] = T {
val outJar = PathRef(T.dest / "out.jar")
lefou marked this conversation as resolved.
Show resolved Hide resolved
val java = javaHome().path / "bin" / "java"

val cmd = os.proc(
java,
"-cp",
proguardClasspath().map(_.path).mkString(":"),
"proguard.ProGuard",
steps(),
"-injars",
inJar().path,
"-outjars",
outJar.path,
"-libraryjars",
libraryJars().map(_.path).mkString(":"),
entryPoint(),
additionalOptions()
)
System.out.println(cmd.command.flatMap(_.value).mkString(" "))
cmd.call(stdout = T.dest / "stdout.txt", stderr = T.dest / "stderr.txt")

// the call above already throws an exception on a non-zero exit code,
// so if we reached this point we've succeeded!
outJar
lefou marked this conversation as resolved.
Show resolved Hide resolved
}

/** The location of the proguard jar files.
* These are downloaded from JCenter and fed to `java -cp`
*/
def proguardClasspath: T[Loose.Agg[PathRef]] = T {
resolveDependencies(
Seq(Repositories.jcenter),
Lib.depToDependencyJava(_),
Seq(ivy"com.guardsquare:proguard-base:${proguardVersion()}"))
}

private def steps: T[Seq[String]] = T {
(if (optimize()) Seq() else Seq("-dontoptimize")) ++
(if (obfuscate()) Seq() else Seq("-dontobfuscate")) ++
(if (shrink()) Seq() else Seq("-dontshrink")) ++
(if (preverify()) Seq() else Seq("-dontpreverify"))
}

/** The default `entrypoint` to proguard.
*
* Defaults to the `main` method of `finalMainClass`.
* Can be overriden to specify a different entrypoint,
* or additional entrypoints can be specified with `additionalOptions`. */
def entryPoint: T[String] = T {
s"""|-keep public class ${finalMainClass()} {
| public static void main(java.lang.String[]);
|}
|""".stripMargin
}

/** Specify any additional options to proguard.
*
* These are fed as-is to the proguard command.
* */
def additionalOptions: T[Seq[String]] = T { Seq[String]() }
}
32 changes: 32 additions & 0 deletions docs/pages/9 - Contrib Modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,38 @@ object app extends ScalaModule with RouterModule {
}
```

## Proguard

This module allows [Proguard](https://www.guardsquare.com/en/products/proguard/manual/introduction) to be used in Mill builds.
ProGuard is a Java class file shrinker, optimizer, obfuscator, and preverifier.

By default, all four steps - shrink, optimize, obfuscate, verify - are run, but this can be configured through task options.
Any additional options can be specified as a list of strings with `additionalOptions`. The full list of proguard options
can be found [here](https://www.guardsquare.com/en/products/proguard/manual/usage).

The output of `assembly` is used as the input jar and the output is written to `out.jar` in the `dest` folder.

The `stdout` and `stderr` from the proguard command can be found under the `dest` folder.

The only default entrypoint is the main class (i.e. `finalMainClass` task). Additional entrypoints can be configured using `additionalOptions` as well.

Here is a simple example:

```
import $ivy.`com.lihaoyi::mill-contrib-proguard:$MILL_VERSION`
import contrib.proguard._

object foo extends ScalaModule with Proguard {
def scalaVersion = "2.12.0"

override def shrink: T[Boolean] = T { true }
override def optimize: T[Boolean] = T { false }
override def obfuscate: T[Boolean] = T { false }
}
```

Also, please note that Proguard doesn't seem to work with scala 2.13 yet.

## ScalaPB

This module allows [ScalaPB](https://scalapb.github.io) to be used in Mill builds. ScalaPB is a [Protocol Buffers](https://developers.google.com/protocol-buffers/) compiler plugin that generates Scala case classes, encoders and decoders for protobuf messages.
Expand Down