-->

Gradle badass-runtime-plugin and ProGuard Gradle P

2020-07-07 04:00发布

问题:

How to run proguard before jPackage?

Introduction

Im developing an app in JavaFx using gradle plugins and packaging it with jPackager, also using the gradle plugins.

The main plugins im using are:

id 'org.openjfx.javafxplugin' version '0.0.8'
id 'org.beryx.runtime' version '1.7.0'
id "com.github.johnrengelman.shadow" version "5.1.0"

My current gradle version is: gradle-5.6.2-all

Problem description

How do I use proguard so the code gets obfuscated and optimized before jPackage do its job?

I can run the proguard tasks, but when I run jPackage, the code doesnt get obfuscated!

Ive found a tutorial (Tutorial) for an older gradle version however im not sure how to mix this with the current plugins. I've tried a few code snippets but they all failed to build and I dont want to clutter this topic with a bunch of non-working code.

My current working build.gradle

// 1. Include proguard dependency
buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'net.sf.proguard:proguard-gradle:6.2.0'
    }
}

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
    id 'org.beryx.runtime' version '1.7.0'
    id "com.github.johnrengelman.shadow" version "5.1.0"

}


dependencies {
    compile "org.controlsfx:controlsfx:11.0.0"
    compile "eu.hansolo:tilesfx:11.13"
    compile "com.jfoenix:jfoenix:9.0.9"
    compile "org.apache.httpcomponents:httpclient:4.5.9"
    compile "org.json:json:20180813"
    compile "mysql:mysql-connector-java:8.0.17"
    compile "org.jasypt:jasypt:1.9.3"
    compile "com.mchange:c3p0:0.9.5.4"
    compile "com.sun.mail:javax.mail:1.6.2"
    compile "commons-validator:commons-validator:1.6"
    compile 'org.openjfx:javafx-base:11:win'
    compile 'org.openjfx:javafx-controls:11:win'
    compile 'org.openjfx:javafx-fxml:11:win'
    compile 'org.openjfx:javafx-graphics:11:win'

}

repositories {
    mavenCentral()
}

javafx {
    version = "13"
    modules = [ 'javafx.controls','javafx.graphics','javafx.fxml'  ]
}

mainClassName = 'Main'

runtime {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']

   jpackage {
        jpackageHome = 'C:/Program Files/Java/openjdk-14-jpackage+1-49_windows-x64_bin/'



        if(org.gradle.internal.os.OperatingSystem.current().windows) {
            installerType = 'msi'
            imageOptions = []
            installerOptions = ['--win-per-user-install',
                '--win-dir-chooser',
                '--win-menu',
                '--win-shortcut',
                '--verbose',
                '--description','Test of proguard with jPackage',
                '--name', 'Test-ProguardJPackage',
                '--vendor','DoesItMatter']
        }
    }

}

compileJava {
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                 '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

run {
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                 '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}




task cleanClasses(type: Delete) {
    delete "${buildDir}/classes/java/main"
    delete "${buildDir}/resources/java/main"
}

classes.dependsOn(cleanClasses)

// 2.2 Add proguard task
task proguard(type: proguard.gradle.ProGuardTask, dependsOn: classes) {
    injars project.sourceSets.main.output
    outjars "${buildDir}/proguard/output.jar"

    libraryjars project.sourceSets.main.compileClasspath

    configuration 'proguard.conf'
}

// 2.3 Clean after proguard task
task cleanAfterProguard(type: Delete, dependsOn: proguard) {
    delete "${buildDir}/classes/java/main"
    delete "${buildDir}/resources/java/main"
}

// 2.4 Extract output jar to buildDir 
task unpackProguardOutput (type: Copy, dependsOn: cleanAfterProguard) {
    from zipTree("${buildDir}/proguard/output.jar")
    into file("${buildDir}/classes/java/main")
}


// 3. Create a task to run the app with the proguarded buildDir
task runProguard(type: JavaExec, dependsOn: unpackProguardOutput) {
    classpath = sourceSets.main.runtimeClasspath
    jvmArgs = ['--module-path', classpath.asPath,
               '--add-modules', 'javafx.controls,javafx.fxml' ]
    main = 'Main' // <-- this name will depend on the proguard result
}

References

Package a non-modular JavaFX application

JavaFX Proguard Obfuscation


回答1:

Problem

When you run a Gradle task, you have to take into account the tasks that will be previously executed, based on their dependencies.

In the JavaFX Proguard Obfuscation answer you have linked, you can see that the proguard custom tasks are concatenated between them, and when you run ./gradlew runProguard, actually you get this order of tasks:

:cleanClasses
:compileJava
:processResources
:classes
:proguard
:cleanAfterProguard
:unpackProguardOutput
:runProguard

If you want to add now there runtime plugin, for tasks like runtime or jpackage, you will get this order:

:cleanClasses
:compileJava
:processResources
:classes
:jar
:startScripts
:installDist
:jre
:runtime

Do you see the problem? There is no call at all to the proguard tasks, because we haven't modified the runtime task to depend on proguard.

Solution

As you can see, both runtime and jpackage depend on the jar of the project. So one easy fix will be hooking up the proguard task into the jar task, so we create a jar out of the proguarded classes instead of the original ones.

Something like this in your build should work:

jar.dependsOn(unpackProguardOutput)

However there is an issue with the resources (the proguarded FXML file will be overwritten by the original one), because the original resources are copied again to the jar.

So we can modify the jar task instead:

jar {
    dependsOn 'cleanAfterProguard'
    manifest {
        attributes(
                'Main-Class': 'org.openjfx.Launcher'
        )
    }
    from zipTree("${buildDir}/proguard/output.jar")
}

This will be the task order now:

:cleanClasses
:compileJava
:processResources
:classes
:proguard
:cleanAfterProguard
:jar
:startScripts
:installDist
:jre
:runtime

Now running ./gradlew clean runtime will generate a runtime image based on a proguarded hellofx.jar. Running build/image/bin/hellofx should work.

The same applies to jpackage:

:cleanClasses
:compileJava
:processResources
:classes
:proguard
:cleanAfterProguard
:jar
:startScripts
:installDist
:jre
:jpackageImage
:jpackage

In this pic, you see that the jar included in the hellofx.app contains proguarded classes only.



回答2:

See the manual, the buildscript block which provides the dependency looks whole different:

buildscript {
    repositories {
        flatDir dirs: '/usr/local/java/proguard/lib'
    }
    dependencies {
        classpath ':proguard:'
    }
}

While it complains, that it is unable to resolve class proguard.gradle.ProGuardTask, there likely won't be any proguard.gradle.ProGuardTask. And dependsOn: 'obfuscatedJar' is strange, because task myProguardTask is supposed to obfuscate it.

task myProguardTask(type: proguard.gradle.ProGuardTask) {
    ...
}

Also make sure, that /usr/local/java/proguard/lib is even installed eg. with locate proguard, because for Java it's not being provided by the Android SDK - and so one has to provide it as buildscript dependency. Then you'll have to write a custom proguard.txt, based upon all the warnings it throws when obfuscating.


Updating the proguard-gradle plugin might be another possible option:

dependencies {
    classpath 'net.sf.proguard:proguard-gradle:6.2.0'
}

For reference, this would be it's ProGuardTask.java.