Как изменить имя файла отображения Proguard в Gradle для проекта Android

У меня есть Android-проект, основанный на Gradle, и я хочу изменить имя файла mapping.txt после его создания для моей сборки. Как это можно сделать?

обн

Как это можно сделать в build.gradle? Так как у меня есть доступ к моим версиям и другим жестким файлам, я хотел бы создать имя файла сопоставления на основе варианта варианта flav /build.

13 ответов

Решение

Большое спасибо Sergii Pechenizkyi, который помог мне найти это хорошее решение.

Для реализации копирования файлов сопоставления proguard для каждого варианта мы можем создать "корневую" задачу copyProguardMappingTask и количество динамических задач для каждого аромата

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
        }
    }
}

после этого мы должны выполнить эту задачу после сборки нашего проекта. Я использую Дженкинс, и мой вариант задач выглядит так

gradle clean assembleProjectName copyProguardMapping

Отлично работает.

Более простое решение.

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

На сегодняшний день (май 2020 г.) прежнее решение, использующееvariant.mappingFileбольше не работает в новом плагине Android Gradle (Android Studio) 3.6 и выше.

Вместо variant.mappingFile возвращается null и в журналах отображается следующее:

ВНИМАНИЕ: API-интерфейс "variant.getMappingFile()" устарел и был заменен на "variant.getMappingFileProvider()".

Делюсь своим рабочим решением, в котором используется новый 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)
                }
            }
        }
    }

Обратите внимание, что вариант.getBuildType(). IsMinifyEnabled() не используется, поскольку мы используем DexGuard.

В приведенном выше коде имя файла сопоставления совпадает с именем файла apk.

На всякий случай, если вам нужно изменить имя apk - можно использовать следующее:

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

Используйте эту команду в вашем файле proguard-rules.pro:

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

файл будет написан частично {root}/path/to/your/file с file_name.txt название.

Если вы хотите иметь разные настройки для разных вкусов, вы можете определить для них множество правил proguard

Я нашел еще одну идею, но я не уверен, что это правильный путь.

Вы можете определить свой путь во вкусах:

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

И в качестве следующего вы можете определить новую задачу, прежде чем $asseble{variant.name.capitalize()} задача, как показано ниже:

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);
}

и в сценарии ${root}/utils/test.bash - вы можете изменить proguard-rules.pro,

Но я думаю, что существуют лучшие решения.

С момента последнего обновления option.mappingFile больше не доступен. (Я использую ProGuard версии 4.7, AndroidStudio 2.0)

Это (часть) мой файл build.gradle:

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")
            }
        }
    }

}

variant.assemble в настоящее время не рекомендуется, предлагается решение, включающее предыдущие модификации:

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)
        }
    }
}

Если вы используете пакет приложений (aab) вместо apk, добавьте это после раздела Android:

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")
                    }
                }
            }
        }
    }
}

Своп bundleRelease за assembleRelease для apks в последнем примере тоже.

Обновление: Однако последнее не сработает, если вы попытаетесь создать нормальную отладку прямо на вашем телефоне. Ошибка:

Не удалось получить неизвестное свойство "bundleRelease" для проекта ": приложение" типа org.gradle.api.Project.

Это вариант ответа igorpst, но переименовывается в mapping.txt, чтобы он точно совпадал с именем apk, включая имя версии приложения. Я объединил это с кодом, чтобы назвать APK с номером версии, как описано в этом ответе. Я должен был просмотреть исходный код Gradle, чтобы найти $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")
            }
        }
    }
}

Полное решение, которое сработало для меня

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")       
            }
        }
    }

Предыдущий ответ работал прекрасно для меня, пока Android 3.0/Android Gradle Plugin 3.0 не устарел BuildType.isMinifyEnabled(),

Я разработал следующее решение, которое не использует устаревший метод:

applicationVariants.all { variant ->
    variant.assemble.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)
        }
    }
}

Все эти ответы использовали копию, чтобы переименовать файл. Однако я не хотел перемещать файл, я просто хотел изменить его имя, учитывая тип сборки и ее вид.

Я основывался на коде с других постеров и немного его изменил. Так как Minify может быть ложным, при этом используя proguard, я просто проверяю, присутствует ли файл.

Следующий код выполняет только это.

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)
            }
        }
    }
}

НОТА

Возможно, вы могли бы использовать этот код для перемещения файла.

метод.renameTo() ожидает полный путь. Если вы измените путь, я бы предположил, что вы фактически переместите файл в другое место.

Вот решение, которое мне помогает:

  • 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)
                         }
                     }
                 }
             }
         }
     }
    
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"
                    }
                }
            }
        }
    }
}

Приведенное выше решение Pinhassi прекрасно работает и соответствует последним изменениям Gradle. Есть пара вещей, которые мне пришлось изменить:

  1. Имя модуля жестко закодировано ("приложение"), что не является идеальным, поскольку во многих случаях (включая мой) это не соответствует действительности. Лучше динамически определять имя модуля.
  2. Файл сопоставления также соответствует только файловой системе Windows, имея косые черты в обратном порядке ("\"). Если вы работаете в системе *NIX, такой как Linux или Mac, вам нужно заменить их на прямые не экранированные слеши ("/")
  3. Немного изменилось переименование файла.apk для включения имени проекта и добавлена ​​отметка даты / времени в конце.

Вот готовый код:

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 "";
    }
}
Другие вопросы по тегам