Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This version introduces sentry-cli 3.0.0. Sentry-cli 3.0.0 and above only offici
- Fix `Modifier.sentryTag()` not found warning ([#997](https://github.com/getsentry/sentry-android-gradle-plugin/pull/997))
- Fix reproducible builds by writing `sentry-debug-meta.properties` without timestamps ([#876](https://github.com/getsentry/sentry-android-gradle-plugin/pull/876))
- Skip generating `sentry-debug-meta.properties` when `includeProguardMapping` and `includeSourceContext` are disabled ([#1047](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1047))
- Fix proguard mapping tasks compatibility with Kotlin/Compose 2.3.0 ([#1054](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1054))
- Include root project check in preMerge task ([#1006](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1006))
- Set SENTRY_PIPELINE environment variable for all sentry-cli invocations ([#1036](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1036))
- Stop passing deprecated parameters to sentry-cli ([#1015](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1015))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import com.android.build.api.variant.CanMinifyCode
import com.android.build.api.variant.Variant
import com.android.build.api.variant.impl.ApplicationVariantImpl
import com.android.build.api.variant.impl.VariantImpl
import io.sentry.android.gradle.SentryTasksProvider.getComposeMappingMergeTask
import io.sentry.android.gradle.SentryTasksProvider.getMinifyTask
import io.sentry.android.gradle.tasks.SentryGenerateProguardUuidTask
import io.sentry.gradle.common.SentryVariant
import io.sentry.gradle.common.filterBuildConfig
import org.gradle.api.Project
Expand Down Expand Up @@ -48,9 +51,7 @@ data class AndroidVariant74(private val variant: Variant) : SentryVariant {
val artifacts = variant.artifacts

override fun mappingFileProvider(project: Project): Provider<FileCollection> =
project.provider {
project.files(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
}
variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE).map { project.files(it) }

override fun sources(
project: Project,
Expand All @@ -77,6 +78,30 @@ data class AndroidVariant74(private val variant: Variant) : SentryVariant {
}
}

override fun wireMappingFileToUuidTask(
project: Project,
task: TaskProvider<out SentryGenerateProguardUuidTask>,
variantName: String,
dexguardEnabled: Boolean,
) {
// we need to wait for project evaluation to have all tasks available, otherwise the new
// AndroidComponentsExtension is configured too early to look up for the tasks
project.afterEvaluate {
val minifyTask = getMinifyTask(project, variantName, dexguardEnabled)

// we just hack ourselves into the Proguard/R8/DexGuard task's doLast.
minifyTask?.configure { it.finalizedBy(task) }

// When Kotlin Compose compiler is enabled, the final mapping file is written by
// mergeStagingReleaseComposeMapping task, not by R8 directly. We need to hook into that task
// as well to ensure the mapping file exists when our UUID task runs.
val composeMappingMergeTask = getComposeMappingMergeTask(project, variantName)
composeMappingMergeTask?.let { composeTask ->
task.configure { generateUuidTask -> generateUuidTask.mustRunAfter(composeTask) }
}
}
}

fun <T : Task> assetsWiredWithDirectories(
task: TaskProvider<T>,
inputDir: (T) -> DirectoryProperty,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@file:Suppress("UnstableApiUsage")

package io.sentry.android.gradle

import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.variant.Variant
import io.sentry.android.gradle.tasks.SentryGenerateProguardUuidTask
import io.sentry.gradle.common.SentryVariant
import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider

/**
* Compatibility layer for AGP 8.3+.
*
* This class provides access to the `toListenTo` API, which was introduced in AGP 8.3. It allows
* proper task wiring for artifacts without forcing their production.
*/
data class AndroidVariant83(
private val variant: Variant,
private val delegate: SentryVariant = AndroidVariant74(variant),
) : SentryVariant by delegate {

override val isDebuggable: Boolean = variant.debuggable

/**
* Wires the [SentryGenerateProguardUuidTask] to listen to the obfuscation mapping file artifact.
*
* When minification runs, this ensures the UUID task executes after the mapping file is produced,
* and receives the mapping file location via [SentryGenerateProguardUuidTask.mappingFile].
*
* When minification doesn't run, the UUID task won't be triggered via this wiring (it would only
* run if explicitly depended upon by another task).
*/
override fun wireMappingFileToUuidTask(
project: Project,
task: TaskProvider<out SentryGenerateProguardUuidTask>,
variantName: String,
dexguardEnabled: Boolean,
) {
if (!dexguardEnabled) {
variant.artifacts
.use(task)
.wiredWith(SentryGenerateProguardUuidTask::mappingFile)
.toListenTo(SingleArtifact.OBFUSCATION_MAPPING_FILE)
} else {
// when dexguard is enabled we still want to go the old way, because AGP API does not apply
delegate.wireMappingFileToUuidTask(project, task, variantName, true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ import io.sentry.android.gradle.tasks.SentryUploadProguardMappingsTask
import io.sentry.android.gradle.tasks.configureNativeSymbolsTask
import io.sentry.android.gradle.tasks.dependencies.SentryExternalDependenciesReportTaskV2
import io.sentry.android.gradle.telemetry.SentryTelemetryService
import io.sentry.android.gradle.util.AgpVersions
import io.sentry.android.gradle.util.GroovyCompat
import io.sentry.android.gradle.util.SentryModules
import io.sentry.android.gradle.util.SentryPluginUtils.isMinificationEnabled
import io.sentry.android.gradle.util.SentryPluginUtils.isVariantAllowed
import io.sentry.android.gradle.util.collectModules
import io.sentry.android.gradle.util.hookWithAssembleTasks
import io.sentry.android.gradle.util.hookWithMinifyTasks
import java.io.File
import org.gradle.api.Project
import org.gradle.api.file.Directory
Expand Down Expand Up @@ -353,18 +353,24 @@ private fun ApplicationVariant.configureProguardMappingsTasks(
sentryOrg: String?,
sentryProject: String?,
): TaskProvider<SentryGenerateProguardUuidTask>? {
val variant = AndroidVariant74(this)
val variant =
if (AgpVersions.isAGP83(AgpVersions.CURRENT)) {
AndroidVariant83(this)
} else {
AndroidVariant74(this)
}
val sentryProps = getPropertiesFilePath(project, variant)
val dexguardEnabled = extension.dexguardEnabled.get()
val isMinifyEnabled = isMinificationEnabled(project, variant, dexguardEnabled)

if (isMinifyEnabled && extension.includeProguardMapping.get()) {
val mappings = getMappingFileProvider(project, variant, dexguardEnabled)
val generateUuidTask =
SentryGenerateProguardUuidTask.register(
project = project,
extension,
sentryTelemetryProvider,
proguardMappingFile = getMappingFileProvider(project, variant, dexguardEnabled),
proguardMappingFile = mappings,
taskSuffix = name.capitalized,
output = paths.proguardUuidDir,
)
Expand All @@ -378,7 +384,7 @@ private fun ApplicationVariant.configureProguardMappingsTasks(
cliExecutable = cliExecutable,
generateUuidTask = generateUuidTask,
sentryProperties = sentryProps,
mappingFiles = getMappingFileProvider(project, variant, dexguardEnabled),
mappingFiles = mappings,
autoUploadProguardMapping = extension.autoUploadProguardMapping,
sentryOrg = sentryOrg?.let { project.provider { it } } ?: extension.org,
sentryProject = sentryProject?.let { project.provider { it } } ?: extension.projectName,
Expand All @@ -387,8 +393,9 @@ private fun ApplicationVariant.configureProguardMappingsTasks(
taskSuffix = name.capitalized,
)

generateUuidTask.hookWithMinifyTasks(
variant.wireMappingFileToUuidTask(
project,
generateUuidTask,
name,
dexguardEnabled && GroovyCompat.isDexguardEnabledForVariant(project, name),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ internal object SentryTasksProvider {
return project.findTask(tasks)
}

/**
* Returns the Compose mapping merge task for the given project and variant. This task is
* responsible for merging Compose mapping data with the R8 mapping file. The final mapping file
* at build/outputs/mapping/<variant>/mapping.txt is written by this task, not by R8 directly.
*
* https://github.com/JetBrains/kotlin/blob/b73fc4e8afb382976646fac728e717fd0b1d1c9c/libraries/tools/kotlin-compose-compiler/src/common/kotlin/org/jetbrains/kotlin/compose/compiler/gradle/internal/ComposeAgpMappingFile.kt#L84-L87
*
* @return the task or null if the Kotlin Compose plugin is not applied or the task doesn't exist
*/
@JvmStatic
fun getComposeMappingMergeTask(project: Project, variantName: String): TaskProvider<Task>? =
project.findTask(listOf("merge${variantName.capitalized}ComposeMapping"))

/**
* Returns the pre bundle task for the given project and variant.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
Expand All @@ -29,16 +30,29 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() {
override val outputFile: Provider<RegularFile>
get() = output.file(SENTRY_UUID_OUTPUT)

@get:Internal abstract val proguardMappingFiles: ConfigurableFileCollection
// Used by AGP < 8.3 with conventional file paths
@get:Internal abstract val fallbackMappingFiles: ConfigurableFileCollection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it still correct that these are internal? i guess better to do this in a separate PR if we need to change this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, still needs to be internal for this test to pass, otherwise it'll trigger minify task in robolectric tests unfortunately

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange! is that true for 8.3+ as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually haven't tried, but will give it a shot!


// Used by AGP 8.3+ with toListenTo API - this property is wired to the mapping artifact
@get:Internal abstract val mappingFile: RegularFileProperty

@TaskAction
fun generateProperties() {
val outputDir = output.get().asFile
outputDir.mkdirs()

val proguardMappingFileHash =
proguardMappingFiles.files.joinToString { if (it.isFile) it.contentHash() else STATIC_HASH }
val uuid = UUID.nameUUIDFromBytes(proguardMappingFileHash.toByteArray())
// Prefer mappingFile (set via toListenTo on AGP 8.3+) over fallbackMappingFiles
val mappingFile =
if (mappingFile.isPresent) {
mappingFile.get().asFile.takeIf { it.exists() }
} else {
// Fallback for AGP < 8.3: use conventional file paths
fallbackMappingFiles.files.firstOrNull { it.exists() }
}

val uuid =
mappingFile?.let { UUID.nameUUIDFromBytes(it.contentHash().toByteArray()) }
?: UUID.randomUUID()
outputFile.get().asFile.writer().use { writer ->
writer.appendLine("$SENTRY_PROGUARD_MAPPING_UUID_PROPERTY=$uuid")
}
Expand All @@ -47,7 +61,6 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() {
}

companion object {
internal const val STATIC_HASH = "<hash>"
internal const val SENTRY_UUID_OUTPUT = "sentry-proguard-uuid.properties"
const val SENTRY_PROGUARD_MAPPING_UUID_PROPERTY = "io.sentry.ProguardUuids"

Expand All @@ -67,7 +80,7 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() {
output?.let { task.output.set(it) }
task.withSentryTelemetry(extension, sentryTelemetryProvider)
if (proguardMappingFile != null) {
task.proguardMappingFiles.from(proguardMappingFile)
task.fallbackMappingFiles.from(proguardMappingFile)
}
task.outputs.upToDateWhen { false }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import org.gradle.util.GradleVersion
internal object AgpVersions {
val CURRENT: SemVer = SemVer.parse(Version.ANDROID_GRADLE_PLUGIN_VERSION)
val VERSION_7_4_0: SemVer = SemVer.parse("7.4.0-rc01")
val VERSION_8_3_0: SemVer = SemVer.parse("8.3.0")

fun isAGP74(current: SemVer) = current >= VERSION_7_4_0

fun isAGP83(current: SemVer) = current >= VERSION_8_3_0
}

internal object GradleVersions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,12 @@ package io.sentry.android.gradle.util
import io.sentry.android.gradle.SentryTasksProvider.getAssembleTaskProvider
import io.sentry.android.gradle.SentryTasksProvider.getBundleTask
import io.sentry.android.gradle.SentryTasksProvider.getInstallTaskProvider
import io.sentry.android.gradle.SentryTasksProvider.getMinifyTask
import io.sentry.android.gradle.SentryTasksProvider.getPackageBundleTask
import io.sentry.android.gradle.SentryTasksProvider.getPackageProvider
import io.sentry.android.gradle.SentryTasksProvider.getPreBundleTask
import io.sentry.android.gradle.util.SentryPluginUtils.withLogging
import io.sentry.gradle.common.SentryVariant
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider

fun TaskProvider<out Task>.hookWithMinifyTasks(
project: Project,
variantName: String,
dexguardEnabled: Boolean,
) {
// we need to wait for project evaluation to have all tasks available, otherwise the new
// AndroidComponentsExtension is configured too early to look up for the tasks
project.afterEvaluate {
val minifyTask = getMinifyTask(project, variantName, dexguardEnabled)

// we just hack ourselves into the Proguard/R8/DexGuard task's doLast.
minifyTask?.configure { it.finalizedBy(this) }
}
}

fun TaskProvider<out Task>.hookWithPackageTasks(project: Project, variant: SentryVariant) {
val variantName = variant.name
val preBundleTaskProvider =
withLogging(project.logger, "preBundleTask") { getPreBundleTask(project, variantName) }
val packageBundleTaskProvider =
withLogging(project.logger, "packageBundleTask") { getPackageBundleTask(project, variantName) }

// To include proguard uuid file into aab, run before bundle task.
preBundleTaskProvider?.configure { task -> task.dependsOn(this) }
// The package task will only be executed if the generateUuidTask has already been executed.
getPackageProvider(variant)?.configure { task -> task.dependsOn(this) }

// App bundle has different package task
packageBundleTaskProvider?.configure { task -> task.dependsOn(this) }
}

fun TaskProvider<out Task>.hookWithAssembleTasks(project: Project, variant: SentryVariant) {
// we need to wait for project evaluation to have all tasks available, otherwise the new
// AndroidComponentsExtension is configured too early to look up for the tasks
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.gradle.common

import io.sentry.android.gradle.tasks.SentryGenerateProguardUuidTask
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
Expand Down Expand Up @@ -38,4 +39,11 @@ data class JavaVariant(val project: Project, val javaExtension: JavaPluginExtens
(javaDirs + additionalSources.get()).filterBuildConfig().toSet()
}
}

override fun wireMappingFileToUuidTask(
project: Project,
task: TaskProvider<out SentryGenerateProguardUuidTask>,
variantName: String,
dexguardEnabled: Boolean,
) = Unit
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.gradle.common

import io.sentry.android.gradle.tasks.SentryGenerateProguardUuidTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
Expand Down Expand Up @@ -33,6 +34,13 @@ interface SentryVariant {
project: Project,
additionalSources: Provider<out Collection<Directory>>,
): Provider<out Collection<Directory>>

fun wireMappingFileToUuidTask(
project: Project,
task: TaskProvider<out SentryGenerateProguardUuidTask>,
variantName: String,
dexguardEnabled: Boolean,
)
}

fun Collection<Directory>.filterBuildConfig(): Collection<Directory> = filterNot {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,25 @@ internal fun verifyProguardUuid(
val apk = rootFile.resolve("app/build/outputs/apk/$variant/app-$variant$signedStr.apk")
val sentryProperties =
if (inGeneratedFolder) {
val propsFile =
// For AGP 7.4.0+, the transform API puts the file in intermediates
val intermediatesFile =
rootFile.resolve(
"app/build/intermediates/assets" +
"/$variant/injectSentryDebugMetaPropertiesIntoAssets${variant.capitalized}" +
"/sentry-debug-meta.properties"
)
// For AGP < 7.4.0, check the old generated folder location
val generatedFile =
rootFile.resolve(
"app/build/generated/assets" +
"/generateSentryDebugMetaProperties${variant.capitalized}" +
"/sentry-debug-meta.properties"
)
if (propsFile.exists()) propsFile.readText() else ""
when {
intermediatesFile.exists() -> intermediatesFile.readText()
generatedFile.exists() -> generatedFile.readText()
else -> ""
}
} else {
extractZip(apk, "assets/sentry-debug-meta.properties")
}
Expand Down
Loading
Loading