olegflo
olegflo

Reputation: 1115

How to change the proguard mapping file name in gradle for Android project

I have android project based on gradle and I want to change mapping.txt file name after it's generated for my build. How can it be done?

upd

How it can be done in build.gradle? Since I have access there to my flavors and other stiff, I would like to create mapping file name based on flavor/build variant version.

Upvotes: 29

Views: 18500

Answers (13)

pogo
pogo

Reputation: 71

Here is solution that helps me:

  • compileSdkVersion 30

  • JavaVersion.VERSION_1_8

  • kotlin_version = '1.5.31'

  • com.android.tools.build:gradle:7.0.2

    buildTypes {
     release {
         minifyEnabled true
         shrinkResources true
         proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    
         applicationVariants.all { variant ->
             // Generating apk file for each flavour release build
             variant.outputs.all {
                 outputFileName = "${variant.flavorName}-${variant.versionCode}.apk"
             }
    
             // Generating mapping file for each flavour release build
             if (variant.getBuildType().isMinifyEnabled()) {
                 variant.assembleProvider.get().doLast {
                     def files = variant.getMappingFileProvider().get().getFiles()
                     for (file in files) {
                         if (file != null && file.exists()) {
                             def newName = "mapping-${variant.flavorName}-${variant.versionCode}.txt"
                             def newFile = new File(file.parent, newName)
                             newFile.delete()
                             file.renameTo(newName)
                         }
                     }
                 }
             }
         }
     }
    

Upvotes: 0

Jon
Jon

Reputation: 9823

For Android Studio Gradle Plugin Version 4.1.0 and newer (since about May 2020)

This version fixes the following warning:

WARNING: API 'variant.getMappingFile()' is obsolete and has been replaced with 'variant.getMappingFileProvider()'.

applicationVariants.all { variant ->
    variant.assembleProvider.get().doLast {
        def mappingFileProvider = variant.getMappingFileProvider().get()
        if (mappingFileProvider != null) {
            try {
                def mappingFiles = mappingFileProvider.getFiles()

                for (mappingFile in mappingFiles) {
                    if (mappingFile != null && mappingFile.exists()) {
                        def newMappingFileName = "$archivesBaseName-$variant.baseName-$mappingFile.name"
                        project.logger.lifecycle("Renaming '${mappingFile.name}' to '${newMappingFileName}'")
                        def newMappingFile = new File(mappingFile.parent, newMappingFileName)

                        newMappingFile.delete()
                        mappingFile.renameTo(newMappingFile)
                    }
                }
            } catch (Exception ignored) {
                project.logger.lifecycle("No mapping files found to rename")
            }
        }
    }
}

For Android Studio Gradle Plugin Version 3.3.0 (January 2019) through about May 2020 This overcomes previous issues where Android 3.0/Android Gradle Plugin 3.0 deprecated BuildType.isMinifyEnabled() and the gradle plugin deprecated variant.getAssemble().

applicationVariants.all { variant ->
    variant.assembleProvider.get().doLast {
        if (variant.mappingFile != null && variant.mappingFile.exists()) {
            def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
            (new File(variant.mappingFile.parent, mappingFilename)).delete()
            variant.mappingFile.renameTo(variant.mappingFile.parent +
                    "/" + mappingFilename)
        }
    }
}

Upvotes: 2

Alex
Alex

Reputation: 566

As of today (May 2020) former solution, which uses variant.mappingFile is not working anymore in new Android Gradle plugin (Android Studio) 3.6 and higher.

Instead variant.mappingFile returns null and following is displayed in the logs:

WARNING: API 'variant.getMappingFile()' is obsolete and has been replaced with 'variant.getMappingFileProvider()'.

I am sharing my working solution, which uses new api:


    applicationVariants.all { variant ->
        variant.assembleProvider.get().doLast {
            def mappingFiles = variant.getMappingFileProvider().get().files

            for (file in mappingFiles) {
                if (file != null && file.exists()) {
                    def nameMatchingApkFile = "$archivesBaseName-$variant.baseName-$file.name"
                    def newMappingFile = new File(file.parent, nameMatchingApkFile)

                    newMappingFile.delete() //clean-up if exists already
                    file.renameTo(newMappingFile)
                }
            }
        }
    }

Note, that variant.getBuildType().isMinifyEnabled() is not used since we are using DexGuard.

The code above makes mapping file's name match apk's file name.

Just in case, if you need to change apk name - following could be used:

android {
    defaultConfig {
        //resulting apk will looks like: "archive base name" + -<flavour>-<buildType>.apk
        archivesBaseName = "$applicationId-$versionName"
    }
}

Upvotes: 8

Ashish Kshirsagar
Ashish Kshirsagar

Reputation: 79

A complete solution that worked for me

applicationVariants.all { variant ->
        def variantType = variant.buildType.name
        if (variantType == "release") {
            variant.assemble.doLast {
                def mappingFile = variant.mappingFile
                mappingFile.renameTo(mappingFile.parent + "/mapping-${variant.name}.txt")       
            }
        }
    }

Upvotes: 2

Jon
Jon

Reputation: 9823

This is a variation of igorpst's answer but renames mapping.txt to match the apk's name exactly including the app version name. I've combined this with code to name the APK with a version number as described in this answer. I had to snoop through the gradle source code to find $variant.baseName

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"
    defaultConfig {
        applicationId "com.company.app"
        minSdkVersion 13
        targetSdkVersion 21
        versionCode 14       // increment with every release
        versionName '1.4.8'   // change with every release
        archivesBaseName = "MyCompany-MyAppName-$versionName"
    }
    applicationVariants.all { variant ->
        if (variant.getBuildType().isMinifyEnabled()) {
            variant.assemble.doLast {
                (new File(variant.mappingFile.parent, "$archivesBaseName-$variant.baseName-mapping.txt")).delete();
                variant.mappingFile.renameTo(variant.mappingFile.parent +
                        "/$archivesBaseName-$variant.baseName-mapping.txt")
            }
        }
    }
}

Upvotes: 4

Mattias Isegran Bergander
Mattias Isegran Bergander

Reputation: 11909

variant.assemble is now deprecated, suggested solution incorporating previous modifications:

archivesBaseName = "MyCompany-MyAppName-$versionName"
...
applicationVariants.all { variant ->
    variant.assembleProvider.get().doLast {
        if (variant.mappingFile != null && variant.mappingFile.exists()) {
            def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
        (new File(variant.mappingFile.parent, mappingFilename)).delete()
        variant.mappingFile.renameTo(variant.mappingFile.parent +
                "/" + mappingFilename)
        }
    }
}

If using app bundle (aab) instead of apk, add this to after the android section:

afterEvaluate {
    bundleRelease.doLast {
        android.applicationVariants.all { variant ->
            if (variant.buildType.name == 'release') {
                tasks.create(name: "renameMappingFile") {
                    if (variant.mappingFile != null && variant.mappingFile.exists()) {
                        variant.mappingFile.renameTo(variant.mappingFile.parent + "/$variant.baseName-$versionName-${new Date().format('yyyy-MM-dd_HHmm')}-mapping.txt")
                    }
                }
            }
        }
    }
}

Swap bundleRelease for assembleRelease for apks in the last example too.

Update: However that last doesn't work if you try and build a normal debug directly to your phone then. Error:

Could not get unknown property 'bundleRelease' for project ':app' of type org.gradle.api.Project.

Upvotes: 4

SnyersK
SnyersK

Reputation: 1296

All these answers used copy to rename the file. I didn't want to move the file however, I just wanted to change it's name, keeping in mind the build type and flavor.

I based myself on the code from the other posters here and changed it up a bit. Since Minify can be false, while still using proguard, I just check if the file is present.

Following code accomplishes just that.

android {
    applicationVariants.all { variant ->
        variant.assemble.doLast {
            def mappingFolderUrl = "${project.buildDir.path}/outputs/mapping/"

            if (variant.buildType.name) {
                mappingFolderUrl += variant.buildType.name + "/"
            }

            if (variant.flavorName) {
                mappingFolderUrl += variant.flavorName + "/"
            }

            def mappingFileUrl = mappingFolderUrl + "mapping.txt"
            logger.lifecycle("mappingFile path: ${mappingFileUrl}")

            File mappingFile = file(mappingFileUrl)
            if (mappingFile.exists()) {
                def newFileName = mappingFolderUrl + "mapping-${variant.name}.txt"
                mappingFile.renameTo(newFileName)
            }
        }
    }
}

NOTE

You could probably use this code to move the file as well.

the .renameTo() method expects a full path, If you change the path, I would suppose you effectively move the File to another place.

Upvotes: 2

jp1017
jp1017

Reputation: 81

applicationVariants.all { variant ->
    variant.outputs.each { output ->
        if (variant.getBuildType().isMinifyEnabled()) {
            variant.assemble.doLast{
                copy {
                    from variant.mappingFile
                    into "${rootDir}/mapping"
                    rename { String fileName ->
                        "mapping-${variant.name}-${new Date().format('yyyy_MM_dd')}.txt"
                    }
                }
            }
        }
    }
}

Upvotes: 0

Dimitar Darazhanski
Dimitar Darazhanski

Reputation: 2326

Pinhassi's solution above works great and it is conforms to the latest Gradle changes. There are a couple of things though that I had to change:

  1. The module name is hardcoded ("app"), which is not ideal since in a lot of cases (including mine) that will not be true. It is better to dynamically detect the module name.
  2. The mapping file also only conforms to the Windows file system by having backward escaped slashes ("\"). If you are on a *NIX system like Linux or Mac, you need to replace those with forward non escaped slashes ("/")
  3. Changed a bit the renaming of the .apk file to include the project name and added a date/time stamp at the end.

Here is the finished code:

import java.util.regex.Matcher
import java.util.regex.Pattern

buildTypes {
        release {
        debuggable false
        minifyEnabled true
        proguardFiles 'proguard.cfg'

        // Rename the apk file and copy the ProGuard mapping file to the root of the project
        applicationVariants.all { variant ->
            if (variant.getBuildType().name.equals("release")) {
                def formattedDate = new Date().format('yyyyMMddHHmmss')
                def projectName = ""
                variant.outputs.each { output ->
                    def fullName = output.outputFile.name
                    projectName = fullName.substring(0, fullName.indexOf('-'))
                    // ${variant.name} has the value of "paidRelease"
                    output.outputFile = new File((String) output.outputFile.parent, (String) output.outputFile.name.replace(".apk", "-v${variant.versionName}-${formattedDate}.apk"))
                }
                def mappingFile = "${rootDir}/${projectName}/build/outputs/mapping/${getCurrentFlavor()}/release/mapping.txt"
                println("mappingFile:  ${mappingFile}")
                if (variant.getBuildType().isMinifyEnabled()) {
                    variant.assemble.doLast {
                        copy {
                            from "${mappingFile}"
                            into "${rootDir}"
                            rename { String fileName ->
                                "mapping-${variant.name}.txt"
                            }
                        }
                    }
                }
            }
        }
    }

        debug {
            debuggable true
        }
    }

def getCurrentFlavor() {
    Gradle gradle = getGradle()
    String  tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
    Pattern pattern;

    if( tskReqStr.contains( "assemble" ) )
        pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
    else
        pattern = Pattern.compile("generate(\\w+)(Release|Debug)")

    Matcher matcher = pattern.matcher( tskReqStr )

    if( matcher.find() )
        return matcher.group(1).toLowerCase()
    else {
        println "NO MATCH FOUND"
        return "";
    }
}

Upvotes: 0

Asaf Pinhassi
Asaf Pinhassi

Reputation: 15573

Since the last update variant.mappingFile is not longer available. (I use ProGuard version 4.7, AndroidStudio 2.0)

This is (part of) my build.gradle file:

import java.util.regex.Matcher
import java.util.regex.Pattern

def getCurrentFlavor() {
    Gradle gradle = getGradle()
    String  tskReqStr = gradle.getStartParameter().getTaskRequests().toString()

    Pattern pattern;

    if( tskReqStr.contains( "assemble" ) )
        pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
    else
        pattern = Pattern.compile("generate(\\w+)(Release|Debug)")

    Matcher matcher = pattern.matcher( tskReqStr )

    if( matcher.find() )
        return matcher.group(1).toLowerCase()
    else
    {
        println "NO MATCH FOUND"
        return "";
    }
}

buildTypes {
    release {
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        minifyEnabled true

        applicationVariants.all { variant ->
            variant.outputs.each { output ->
                output.outputFile = new File(output.outputFile.parent, "${variant.name}_v${variant.versionName}.apk")
            }
            def mappingFile = "${rootDir}\\app\\build\\outputs\\mapping\\${getCurrentFlavor()}\\release\\mapping.txt"
            println("mappingFile:  ${mappingFile}")
            if (variant.getBuildType().isMinifyEnabled()) {
                variant.assemble.doLast {
                    copy {
                        from "${mappingFile}"
                        into "${rootDir}"
                        rename { String fileName ->
                            "mapping-${variant.name}.txt"
                        }
                    }
                }
            }
        }

    }

    debug {
        minifyEnabled false
        useProguard false

        applicationVariants.all { variant ->
            variant.outputs.each { output ->
                output.outputFile = new File(output.outputFile.parent, "${variant.name}_v${variant.versionName}.apk")
            }
        }
    }

}

Upvotes: 4

igorepst
igorepst

Reputation: 1240

Simpler solution.

applicationVariants.all { variant ->
        if (variant.getBuildType().isMinifyEnabled()) {
            variant.assemble.doLast {
                copy {
                    from variant.mappingFile
                    into "${rootDir}/proguardTools"
                    rename { String fileName ->
                        "mapping-${variant.name}.txt"
                    }
                }
            }
        }
    }

Upvotes: 49

olegflo
olegflo

Reputation: 1115

Many thanx to Sergii Pechenizkyi who helped me to found this good solution.

To implement copying of proguard mapping files for each flavor we can create "root" task copyProguardMappingTask and number of dynamic tasks for each flavor

def copyProguardMappingTask = project.tasks.create("copyProguardMapping")
applicationVariants.all { variant ->
    variant.outputs.each { output ->
        ...
        if (variant.getBuildType().isMinifyEnabled()) {
            def copyProguardMappingVariantTask = project.tasks.create("copyProguardMapping${variant.name.capitalize()}", Copy)

            def fromPath = variant.mappingFile;
            def intoPath = output.outputFile.parent;

            copyProguardMappingVariantTask.from(fromPath)
            copyProguardMappingVariantTask.into(intoPath)
            copyProguardMappingVariantTask.rename('mapping.txt', "mapping-${variant.name}.txt")

            copyProguardMappingVariantTask.mustRunAfter variant.assemble
            copyProguardMappingTask.dependsOn copyProguardMappingVariantTask
        }
    }
}

afterwards we should run this task after assembling our project. I use jenkins and my tasks option looks like

gradle clean assembleProjectName copyProguardMapping

It works like a charm.

Upvotes: 5

Konrad Krakowiak
Konrad Krakowiak

Reputation: 12365

Use this command in your proguard-rules.pro file:

-printmapping path/to/your/file/file_name.txt

the file will be written in part {root}/path/to/your/file with file_name.txt name.

If you want to have different setting for different flavors you can define many proguard-rules for them

I found one more idea but I am not sure that it is right way.

You can define your path in flavors:

productFlavors {
    test1 {
        applicationId "com.android.application.test"
        project.ext."${name}Path" = 'path/one/mapp.txt'
    }
    test2 {
        project.ext."${name}Path" = 'path/two/mapp.txt'
    }
}

And as next you can define new task before $asseble{variant.name.capitalize()} task as is shown below:

android.applicationVariants.all { variant ->
    def envFlavor = variant.productFlavors.get(0).name

    def modifyProguardPath = tasks.create(name: "modifyProguardFor${variant.name.capitalize()}", type: Exec) {
        def pathToMap = project."${envFlavor}Test1"
        doFirst {
            println "==== Edit start: $pathToMap ===="
        }
        doLast {
            println "==== Edit end: $pathToMap ===="
        }
        executable "${rootDir}/utils/test.bash"
        args pathToMap
    }

    project.tasks["assemble${variant.name.capitalize()}"].dependsOn(modifyProguardPath);
}

and in script ${root}/utils/test.bash - you can modify proguard-rules.pro.

But I think that exist better solution.

Upvotes: 7

Related Questions