Guardsquare / proguard

ProGuard, Java optimizer and obfuscator
https://www.guardsquare.com/en/products/proguard
GNU General Public License v2.0
2.84k stars 407 forks source link

Mapping file not included in App Bundle (AAB) #288

Open venator85 opened 1 year ago

venator85 commented 1 year ago

Hi, I have integrated the latest version 7.2.2 of Proguard in my app, replacing R8. I'm using the Android Gradle Plugin version 7.2.2. I have followed exactly the instructions in the Quick Start - ProGuard Gradle Plugin section.

When I produce an AAB, running for example ./gradlew bundleRelease:

  1. the produced AAB doesn't include the mapping file
  2. Google Play shows a warning that the AAB doesn't include the mapping file
  3. the mapping file is not automatically uploaded to Crashlytics

This is a serious issue especially for Crashlytics, since it doesn't support manual upload of the mapping file (unlike Google Play Console where this is possible).

I think all of these issues originate from the fact that the mappingFile is not correctly 'injected' in the resolved application variant, so the Android Gradle Plugin doesn't include it in the AAB:

android.applicationVariants.all { variant ->
    if (variant.buildType.name == 'release') {
        println "the mapping file: ${variant.mappingFile}" // --> prints null
    }
}

The issue can be easily reproduced by using the sample app under examples/android-plugin. If you run ./gradlew bundleRelease, the produced AAB file at path build/outputs/bundle/release/android-plugin-release.aab doesn't include the BUNDLE-METADATA/com.android.tools.build.obfuscation directory.

Is there any way to workaround this issue? Is a proper fix in development? Thanks

venator85 commented 1 year ago

While we wait for a proper fix, here are some workarounds:

android { buildTypes { debug { minifyEnabled false } release { minifyEnabled false firebaseCrashlytics { mappingFileUploadEnabled true } } } }

proguard { configurations { release { defaultConfiguration 'proguard-android-optimize.txt' configuration 'proguard-rules.pro' } } }

project.afterEvaluate { // Iterate over all variants for which obfuscation is enabled. Note this example // only checks the flag set in the buildType, not defaultConfig or product flavors. def variants = project.android.applicationVariants.findAll { it.buildType.extensions.firebaseCrashlytics.mappingFileUploadEnabled } variants.each { variant -> def uploadTaskName = "uploadCrashlyticsMappingFile${variant.name.capitalize()}" TaskProvider uploadCrashlyticsMappingFileTask try { uploadCrashlyticsMappingFileTask = tasks.named(uploadTaskName) } catch (UnknownTaskException e) { throw new GradleException("${uploadTaskName} does not exist for variant ${variant.name}. " + "Make sure the Crashlytics plugin has been applied and " + "firebaseCrashlytics.mappingFileUploadEnabled is set to true.", e) }

    uploadCrashlyticsMappingFileTask.configure { t ->
        t.mappingFileProvider = project.objects.property(FileCollection.class)
        t.mappingFileProvider.value(project.files(file("${project.buildDir}/outputs/proguard/${variant.name}/mapping/mapping.txt")))
        dependsOn "transformClassesAndResourcesWithProguardTransformFor${variant.name.capitalize()}"
    }

    // Manually set a dependency on the upload task for whatever the parent
    // build task is (assemble, bundle, etc). If this is not set,
    // uploadCrashlyticsMappingFileRelease will need to be explicitly invoked
    tasks.findByName("assemble${variant.name.capitalize()}").configure {
        dependsOn uploadCrashlyticsMappingFileTask
    }
}

}

peterdk commented 1 year ago

I have the same issue, and this solved the missing proguard map in a bundle file. Note that it's hardcoded on release and no flavors. It's a bit hacky, but not too much. And since this runs before signing the bundle, there are no issues uploading it to Google Play.

In your app build.gradle: (not the toplevel one):

task addProguardMappingToReleaseIntermediateBundle(type: Zip) {
    description = "Add proguard mapping to bundle file for Google Play upload"

    archiveName = "intermediary-bundle.tmp.aab"
    destinationDirectory = file("${project.buildDir}/intermediates/intermediary_bundle/release/")

    from {
        zipTree("${project.buildDir}/intermediates/intermediary_bundle/release/intermediary-bundle.aab")
    }

    into("BUNDLE-METADATA/com.android.tools.build.obfuscation") {
        from {
            file("${project.buildDir}/outputs/proguard/release/mapping/mapping.txt")
        }
    }

    rename { file ->
        if (file == "mapping.txt") {
            return "proguard.map"
        } else {
            return null
        }
    }
    doLast {
        var source = "${project.buildDir}/intermediates/intermediary_bundle/release/intermediary-bundle.tmp.aab"
        var target = source.replace(".tmp.aab", ".aab")
        file(target).delete()
        file(source).renameTo(file(target))
    }
}
project.afterEvaluate
        {
            packageReleaseBundle.finalizedBy(addProguardMappingToReleaseIntermediateBundle)
        }