4

I have to add the Analytics tool Sentry to our Android project. In order to make it work, one needs to create mappings for the obfuscated code (from Proguard/R8) and upload it later to Sentry.

On the website https://docs.sentry.io/platforms/android/ it is even described how to do that. There it is written that one needs to create a gradle task looking like this:

gradle.projectsEvaluated {
    android.applicationVariants.each { variant ->
        def variantName = variant.name.capitalize();
        def proguardTask = project.tasks.findByName(
            "transformClassesAndResourcesWithProguardFor${variantName}")
        def dexTask = project.tasks.findByName(
            "transformClassesWithDexFor${variantName}")
        def task = project.tasks.create(
                name: "processSentryProguardFor${variantName}",
                type: Exec) {
            workingDir project.rootDir
            commandLine *[
                "sentry-cli",
                "upload-proguard",
                "--write-properties",
                "${project.rootDir.toPath()}/app/build/intermediates/assets" +
                    "/${variant.dirName}/sentry-debug-meta.properties",
                variant.getMappingFile(),
                "--no-upload"
            ]
        }
        dexTask.dependsOn task
        task.dependsOn proguardTask
    }
}

This shall wait until Proguard is finished, than copy this properties file to the assets. However, when I add this to my Android gradle script I get the error:

Could not create task ':app:processSentryProguardForPlayStoreStagingDebug'.

No signature of method: java.util.ArrayList.multiply() is applicable for argument types: (ArrayList) values: [[sentry-cli, upload-proguard, --write-properties, {Application-Path}/app/build/intermediates/assets/playStoreStaging/debug/sentry-debug-meta.properties, ...]] Possible solutions: multiply(java.lang.Number), multiply(java.lang.Number)

I assume there is something wrong with the multiplication symbol * before the commandLine array. But when I remove it I get the error

Could not create task ':app:processSentryProguardForPlayStoreStagingDebug'.

Cannot cast object 'sentry-cli' with class 'java.lang.String' to class 'int'

So I tried to test this with only that line

commandLine "sentry-cli", ...

Which gave me another error

What went wrong: Cannot invoke method dependsOn() on null object

Thus I assume something went really wrong with that gradle script since it seems the dependend task can't be found. Does anyone have any idea how to fix this (or optionally have any other idea how to copy that sentry-debug-meta.properties file to my assets in another way, once Proguard/R8 is finished)?

Thanks!

-------- EDIT --------

I noticed something important. The gradle tasks are defined in a different name than what was defined in the manual. Looking at my tasks I have them named

transformClassesAndResourcesWithR8For...

and

transformClassesWithDexBuilderFor...

However, I print the variantName then for checking but it seems my tasks are incomplete.

In my tasks list there exist

transformClassesAndResourcesWithR8ForPlayStoreStagingDebug

but not

transformClassesAndResourcesWithR8ForPlayStoreStagingRelease

and thus the task can't be found. I think that is the real problem here. So where are these gradle tasks defined?

List of my project gradle tasks

------- EDIT 2 --------

Okay I noticed something strange here. Some variants don't have tasks. It makes sense that DEBUG tasks don't have R8 tasks but I found this here:

Variant: PlayStoreStagingRelease DexTask is null

Variant: PlayStorePreviewRelease DexTask is null

Variant: HockeyAppRelease DexTask is null

Variant: LocalServerRelease DexTask is null

Variant: PlayStoreProductionRelease DexTask is null

So how can this be?

Tobias Reich
  • 4,952
  • 3
  • 47
  • 90

3 Answers3

2

I'd recommend using the Sentry Gradle integration (Gradle plugin) which is described here https://docs.sentry.io/platforms/android/#gradle-integration

The official Android Gradle plugin changed its task names over versions, Gradle version also affects those code snippets.

Google also replaced Proguard with R8 and it also affected those code snippets.

Is there a reason why not using the Sentry Gradle integration? if so, We'll be looking into updating them.

Thanks.

Manoel Neto
  • 158
  • 4
  • Actually, I did that but it doesn't seem to work. When using this, there is no mapping with my obfuscated code and I assume it is because this is not "executed" properly – Tobias Reich Feb 19 '20 at 08:39
  • 1
    do you mind raising an issue https://github.com/getsentry/sentry-android-gradle-plugin? Gradle + AGP version would be helpful, I've tested it a few days ago using AGP 3.x to 3.6.x version and Gradle till version 5.6.x and it worked just fine, maybe a misconfiguration? glad to help, thanks. – Manoel Neto Feb 19 '20 at 08:49
1

java.util.ArrayList.multiply() hints for that * in front of the [ ] list, which looks strange to me. Try removing the *[ ], only keeping List<String> (there's no ArrayList expected, to begin with):

commandLine "sentry-cli", "upload-proguard", "--write-properties", "${project.rootDir.toPath()}/app/build/intermediates/assets/${variant.dirName}/sentry-debug-meta.properties", variant.getMappingFile(), "--no-upload"

You'd have to look up how your tasks are actually being called, but it should be something alike:

def r8Task = project.tasks.findByName("transformClassesAndResourcesWithR8For${variantName}")
def d8Task = project.tasks.findByName("transformClassesWithDexBuilderFor${variantName}")

With a null check, because not every variant might have minifyEnabled true set:

if(r8Task != null) {
    d8Task.dependsOn task
    task.dependsOn r8Task
}

Maybe even a previous null check is required, because variant.getMappingFile() needs R8.

And that some flavors have no D8 task might be based upon the absence of code (nothing to do).

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • As stated in my question, when doing this, I get the error "Cannot invoke method dependsOn() on null object" so I guess the gradle task itself is broken somehow, too. – Tobias Reich Feb 18 '20 at 15:03
  • `def dexTask = project.tasks.findByName("transformClassesWithDexFor${variantName}")` is `null` ...this mean the name of the task does not match. I only have some task called `transformClassesWithDexBuilderForDebug`, which adds in the `Builder`. – Martin Zeitler Feb 18 '20 at 15:21
  • Yea thanks. As seen in my screenshot, I don't seem to have that task for Release. But how can this be? Where are these tasks generated? Debug exists but Release not. – Tobias Reich Feb 18 '20 at 15:27
  • `transformClassesAndResourcesWithR8ForPlayStoreStagingRelease` exists in the screenshot... obviously one needs the obfuscation task, so that the mapping file would even exist. Also make sure that a mapping file is even saved at the expected location. – Martin Zeitler Feb 18 '20 at 15:33
  • Okay, sorry to bother you again. I added a list to the question of the tasks that don't exist. Somehow some Release variants dont have the Dex-Task. How can this happen? – Tobias Reich Feb 18 '20 at 15:52
  • The D8 task should exist in every case, because it won't build without it... except there might be no code available in the module. – Martin Zeitler Feb 18 '20 at 16:00
  • Okay, I guess my questions are drifting here and it becomes a new question. So I will accept this as the solution to my original Gradle problem. Thanks! – Tobias Reich Feb 19 '20 at 08:46
0

Here's a summary of the steps that I followed for integrating Sentry with my Android app. These steps are to ensure the sentry gradle plugin works as expected and automatically uploads the proguard mapping files, without you having to worry about uploading using cli. I assume you would have setup the Sentry SDK as described here: https://docs.sentry.io/platforms/android/#integrating-the-sdk

  1. Ensure you have Android Studio gradle plugin 3.5.0 (Not 3.6.x, that seems to break the sentry plugin. I observed that the sentry proguard or native symbol upload tasks are not configured or executed at all). This value should be in your root project's build.gradle in dependencies block

  2. Provide a sentry.properties file the root folder of your project. The sentry.properties file should have the following values at minimum:

    defaults.project=your_sentry_project_name
    defaults.org=your_sentry_org_name
    auth.token=sentry_project_auth_token

You can get info about generating auth tokens here: https://sentry.io/settings/account/api/auth-tokens/

  1. (Optional: If you have build flavors) In my case, I have different flavors for my app. So, I had to put the sentry.properties inside my flavor specific folder in /app/src/ folder. Then, I wrote a gradle task to copy the flavor specific sentry.properties file into the project's root folder during gradle's configuration phase. Example:
    task copySentryPropertiesTask {
        if (getBuildFlavor() != null && !getBuildFlavor().isEmpty()) {
            println("Copying Sentry properties file: ${getBuildFlavor()}")
            copy {
                from "src/${getBuildFlavor()}/"
                include "sentry.properties"
                into "../"
            }
        }
    }

    def getBuildFlavor() {
        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)
        else {
            println "NO MATCH FOUND"
            return ""
        }
    }

Note 1: You can place this task in your app/build.gradle anywhere (I had placed it at the end).

Note 2: If you followed step 3 for build flavors, you can also add the root folder's sentry.properties in .gitignore. Since, it will be copied everytime you create a build.

Sentry should now be able to upload the proguard files for any release builds (or if you set minifyEnabled=true for any buildType).

PhoBi
  • 36
  • 3