ProGuard Project Dependencies with Plugins

Hey

Apologies if this is a dumb question. I am new to both ProGuard and Gradle. I am also new to posting here, so I am hoping I got this right. I have a project that contains the following:

  • core library
  • N Plugins - each of which depend on the core library
  • Sample Application - uses Core Library + Plugins

What we typically due is we obfuscate the core library and plugins (we formally used Maven/ZKM). When we obfuscated the core library, we would create a mapping file and apply it when we obfuscated the plugins. This allowed the plugins to use methods we had obfuscated.

We are doing (attempting to do) the same in Gradle with ProGuard. We have our core Library defined with:

// Called Core:
android {
    publishNonDefault true
    ...
    sourceSets {
        ...
    }
    buildTypes {
        debug {
            debuggable true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
        }
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
        }
    }

}

configurations {
    debugCompile
    releaseCompile
    configuration.exclude group: 'com.android.support', module: 'support-v4'
}

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile "com.android.support:support-v4:$supportLibraryVersion"
    compile "com.google.code.gson:gson:$googleGsonVersion"
}

In the above, our release builds will be obfuscated, but are debug builds will not be.
Our plugins are then defined as follows:

android {
    publishNonDefault true
    ...
    sourceSets {
       ...
    }
    buildTypes {
        debug {
            debuggable true
            minifyEnabled false
        }
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
        }
    }
}

configurations {
    debugCompile
    releaseCompile
    configuration.exclude group: 'com.android.support', module: 'support-v4'
}


dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(path: ':core', configuration:'debug')
}

Of note - we force it to compile with the ‘debug’ (unobfuscated) version of the library. The obfuscation rules will then be applied to the result with the mapping file from the core (in theory allowing the obfuscated core and obfuscated plugins to work together.

We then have a sample app that references plugins + core as follows (for the sample app we don’t care if its obfuscated, we only care that it use the obfuscated library for demo/testing purposes).

android {
    defaultConfig {
        ...
        multiDexEnabled true
    }
    buildTypes {
        debug {
            debuggable true
            minifyEnabled false
        }
        release {
            minifyEnabled false
            //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

configurations {
    debugCompile
    releaseCompile
}

dependencies {
    debugCompile project(path: ':core', configuration:'debug')
    debugCompile project(path: ':plugins:plugin-eventlogger', configuration:'debug')
    debugCompile project(path: ':plugins:plugin-openvidservice', configuration:'debug')

    releaseCompile project(path: ':core', configuration:'release')
    releaseCompile project(path: ':plugins:plugin-eventlogger', configuration:'release')
    releaseCompile project(path: ':plugins:plugin-openvidservice', configuration:'release')

    compile 'com.android.support:multidex:1.0.1'
    compile "com.android.support:appcompat-v7:$supportLibraryVersion"
    compile "com.android.support:support-v4:$supportLibraryVersion"
}

Note that for release we try to use the obfuscated release builds.

What were encountering is that our debug builds compile/build correctly. However, are release builds fail:

  • What went wrong:

Execution failed for task ‘:sample-app:packageAllDebugClassesForMultiDex’.
java.util.zip.ZipException: duplicate entry: com/quickplay/vstb/exposed/BuildDefinitions$BuildType.class

We suspect that whats happening is when we ‘compile’ a release build - it uses the obfuscated project plugin-eventlogger, but also picks up the debug core that the plugin-eventlogger depends on.

I looked into disabling transitive dependencies, but I could not figure out how to do it for something that has ‘compile project(…)’ as one of the dependencies.

So I am at a bit of a loss. I can see us trying to exclude the dependencies, or somehow having are sample app release build depend on the outputted libs instead of compilation.

Does any one have any thoughts or suggestions as to if my hypothesis above is correct, and if so, how I can go about excluding a compile dependency?

Completly unrelated question: In ProGuard - does anyone know how to use ProGuard to access gradle properties (E.g. project.projectDir). We noticed the relative path when using gradle from the command line vs from within android studio is different. In our proguard we do:

-applymapping …/mapfiles/core-mapping.txt
-printmapping …/mapfiles/plugin-eventlogger-mapping.txt

The ‘relative’ path being different breaks this and I could not figure out how to make proguard aware of the gradle root path :smile:

Thanks for any guidance.

Hello,

In case anyone else sees this, what we ended up doing for are obfuscated build with our test app is we set it up so that it directly used the generated aar files.

So our Debug Dependencies look like:

debugCompile project(path: ‘:plugins:plugin-eventlogger’, configuration:‘dev’)

While our release one looks like:

releaseCompile (name:‘plugin-eventlogger-release’, ext:‘aar’)

AND we include:

flatDir {
    dirs '../generated/releaseLibs'
}

In reality we could have probably just done a dirs for every build output folder, but we avoided that as there are a lot of plugins.

So for our release libraries we have:

task copyReleaseAarFiles {
    copy {
        from ("build/outputs/aar"){
            include(project.name + '-release.aar')
        }
        into "$rootDir/generated/releaseLibs"
    }
    dependsOn ':plugin-eventlogger:assembleRelease'
}

NOTE: I have no idea how good a solution this is. Its one we came up with and it seems to work. Hope it helps anyone else that gets stuck.

Lastly, for the relative path thing, you can pass in a define via -D from the gradle command line, but I was looking for something that was compatible with Android Studio builds as well.

We did the following:

task updateProgaurdProperty << {
    System.properties.setProperty("base.dir", project.projectDir.absolutePath + '/../../')
    println "ProGuard Project Dir:" + project.projectDir
}
preBuild.dependsOn updateProgaurdProperty

Then in our proguard files we can now do:

-applymapping <base.dir>/mapfiles/core-mapping.txt
-printmapping <base.dir>/mapfiles/plugin-eventlogger-mapping.txt