Skip to content

Commit

Permalink
RUM-592: Support new Variant API
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnm committed May 29, 2024
1 parent 7cd2c7d commit 70ba81c
Show file tree
Hide file tree
Showing 37 changed files with 1,593 additions and 545 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ object MavenConfig {
const val PUBLICATION = "pluginMaven"
}

@Suppress("UnstableApiUsage")
fun Project.publishingConfig(projectDescription: String) {
val projectName = name
val signingExtension = extensions.findByType(SigningExtension::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.util.Queue
/**
* A Gradle task to check the Datadog SDK throughout the variant dependencies.
*/
abstract class DdCheckSdkDepsTask : DefaultTask() {
abstract class CheckSdkDepsTask : DefaultTask() {

/**
* The sdkCheckLevel: NONE, WARN, FAIL.
Expand All @@ -47,7 +47,7 @@ abstract class DdCheckSdkDepsTask : DefaultTask() {
init {
group = DdAndroidGradlePlugin.DATADOG_TASK_GROUP
description = "Checks for the Datadog SDK into your variant dependencies."
outputs.upToDateWhen { it is DdCheckSdkDepsTask && it.isLastRunSuccessful }
outputs.upToDateWhen { it is CheckSdkDepsTask && it.isLastRunSuccessful }
}

/**
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2020-Present Datadog, Inc.
*/

package com.datadog.gradle.plugin

import com.android.build.gradle.api.ApplicationVariant
import com.datadog.gradle.plugin.internal.ApiKey
import com.datadog.gradle.plugin.internal.ApiKeySource
import com.datadog.gradle.plugin.internal.DdAppIdentifier
import com.datadog.gradle.plugin.internal.OkHttpUploader
import com.datadog.gradle.plugin.internal.Uploader
import com.datadog.gradle.plugin.internal.variant.AppVariant
import org.gradle.api.DefaultTask
import org.gradle.api.logging.Logging
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.Input
Expand All @@ -27,10 +35,11 @@ import javax.inject.Inject
* A Gradle task to upload symbolication files to Datadog servers (NDK symbol files,
* Proguard/R8 files, etc.)..
*/
abstract class DdFileUploadTask @Inject constructor(
private val providerFactory: ProviderFactory,
abstract class FileUploadTask @Inject constructor(
providerFactory: ProviderFactory,
@get:Internal internal val repositoryDetector: RepositoryDetector
) : DefaultTask() {

@get:Internal
internal var uploader: Uploader = OkHttpUploader()

Expand Down Expand Up @@ -63,20 +72,20 @@ abstract class DdFileUploadTask @Inject constructor(
* The version name of the application.
*/
@get:Input
var versionName: String = ""
abstract val versionName: Property<String>

/**
* The version code of the application. Need to be a provider, because resolution during
* configuration phase may cause incompatibility with other plugins if legacy Variant API is used.
*/
@get:Input
var versionCode: Provider<Int> = providerFactory.provider { 0 }
abstract val versionCode: Property<Int>

/**
* The service name of the application (by default, it is your app's package name).
*/
@get:Input
var serviceName: String = ""
abstract val serviceName: Property<String>

/**
* The Datadog site to upload to (one of "US1", "EU1", "US1_FED").
Expand All @@ -94,7 +103,7 @@ abstract class DdFileUploadTask @Inject constructor(
* Build ID which will be used for mapping file matching.
*/
@get:Input
var buildId: Provider<String> = providerFactory.provider { "" }
abstract val buildId: Property<String>

/**
* datadog-ci.json file, if found or applicable for the particular task.
Expand All @@ -107,7 +116,7 @@ abstract class DdFileUploadTask @Inject constructor(
* The sourceSet root folders.
*/
@get:InputFiles
var sourceSetRoots: List<File> = emptyList()
abstract val sourceSetRoots: ListProperty<File>

/**
* The file containing the repository description.
Expand All @@ -121,14 +130,11 @@ abstract class DdFileUploadTask @Inject constructor(
outputs.upToDateWhen { false }
}

@Internal
internal abstract fun getFilesList(): List<Uploader.UploadFileInfo>

/**
* Uploads the files retrieved from `getFilesList` to Datadog.
*/
@TaskAction
@Suppress("TooGenericExceptionCaught")
@Suppress("TooGenericExceptionCaught", "LongMethod")
fun applyTask() {
datadogCiFile?.let {
applyDatadogCiConfig(it)
Expand All @@ -147,8 +153,15 @@ abstract class DdFileUploadTask @Inject constructor(
val mappingFiles = getFilesList()
if (mappingFiles.isEmpty()) return

// it can be an overlap between java and kotlin directories and since File doesn't override
// equals for set comparison, we will remove duplicates manually
val uniqueSourceSetRoots = sourceSetRoots.get()
.map { it.absolutePath }
.distinct()
.map { File(it) }

val repositories = repositoryDetector.detectRepositories(
sourceSetRoots,
uniqueSourceSetRoots,
remoteRepositoryUrl
)

Expand All @@ -167,8 +180,8 @@ abstract class DdFileUploadTask @Inject constructor(
if (repositories.isEmpty()) null else repositoryFile,
apiKey,
DdAppIdentifier(
serviceName = serviceName,
version = versionName,
serviceName = serviceName.get(),
version = versionName.get(),
versionCode = versionCode.get(),
variant = variantName,
buildId = buildId.get()
Expand All @@ -195,22 +208,37 @@ abstract class DdFileUploadTask @Inject constructor(
}
}

// region Internal

@Internal
internal abstract fun getFilesList(): List<Uploader.UploadFileInfo>

internal fun configureWith(
apiKey: ApiKey,
extensionConfiguration: DdExtensionConfiguration,
variant: ApplicationVariant
variant: AppVariant
) {
this.apiKey = apiKey.value
apiKeySource = apiKey.source
site = extensionConfiguration.site ?: ""

versionName = variant.versionName ?: ""
versionCode = providerFactory.provider { variant.versionCode }
serviceName = extensionConfiguration.serviceName ?: variant.applicationId
versionName.set(variant.versionName)
versionCode.set(variant.versionCode)

if (extensionConfiguration.serviceName != null) {
serviceName.set(extensionConfiguration.serviceName)
} else {
serviceName.set(variant.applicationId)
}

variantName = variant.flavorName
remoteRepositoryUrl = extensionConfiguration.remoteRepositoryUrl ?: ""
}

// endregion

// region Private

private fun applySiteFromEnvironment() {
val environmentSite = System.getenv(DATADOG_SITE)
if (!environmentSite.isNullOrEmpty()) {
Expand Down Expand Up @@ -310,6 +338,8 @@ abstract class DdFileUploadTask @Inject constructor(
repositoryFile.writeText(jsonObject.toString(0))
}

// endregion

internal companion object {
private const val REPOSITORY_FILE_VERSION = 1
private const val INDENT = 4
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2020-Present Datadog, Inc.
*/

package com.datadog.gradle.plugin

import com.android.build.gradle.api.ApplicationVariant
import com.datadog.gradle.plugin.internal.variant.AppVariant
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.Directory
Expand Down Expand Up @@ -57,20 +63,20 @@ abstract class GenerateBuildIdTask : DefaultTask() {
.writeText(buildId)
}

companion object {
internal const val TASK_NAME = "generateBuildId"
internal companion object {
private const val TASK_NAME = "generateBuildId"

/**
* Name of the file containing build ID information.
*/
const val BUILD_ID_FILE_NAME = "datadog.buildId"

/**
* Registers a new instance of [GenerateBuildIdTask] specific for the given [ApplicationVariant].
* Registers a new instance of [GenerateBuildIdTask] specific for the given [AppVariant].
*/
fun register(
target: Project,
variant: ApplicationVariant,
variant: AppVariant,
buildIdDirectory: Provider<Directory>
): TaskProvider<GenerateBuildIdTask> {
val generateBuildIdTask = target.tasks.register(
Expand All @@ -80,6 +86,7 @@ abstract class GenerateBuildIdTask : DefaultTask() {
it.buildIdDirectory.set(buildIdDirectory)
it.variantName.set(variant.name)
}
variant.bindWith(generateBuildIdTask, buildIdDirectory)

return generateBuildIdTask
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,31 @@

package com.datadog.gradle.plugin

import com.datadog.gradle.plugin.DdAndroidGradlePlugin.Companion.LOGGER
import com.datadog.gradle.plugin.internal.Uploader
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import java.io.File
import javax.inject.Inject

/**
* A Gradle task to upload a Proguard/R8 mapping file to Datadog servers.
*/
open class DdMappingFileUploadTask
abstract class MappingFileUploadTask
@Inject constructor(
providerFactory: ProviderFactory,
repositoryDetector: RepositoryDetector
) : DdFileUploadTask(providerFactory, repositoryDetector) {
) : FileUploadTask(providerFactory, repositoryDetector) {

/**
* The path to the mapping file to upload.
*/
@get:Input
var mappingFilePath: String = ""
// don't use InputFile here, because it will check if file exists before executing task,
// we want to do it manually and show our own error
@get:InputFiles
abstract val mappingFile: RegularFileProperty

/**
* Replacements for the source prefixes in the mapping file.
Expand All @@ -40,6 +44,12 @@ open class DdMappingFileUploadTask
@get:Input
var mappingFileTrimIndents: Boolean = false

/**
* Application ID (usually a package prefix).
*/
@get:Input
abstract val applicationId: Property<String>

init {
group = DdAndroidGradlePlugin.DATADOG_TASK_GROUP
description = "Uploads the Proguard/R8 mapping file to Datadog"
Expand All @@ -48,7 +58,10 @@ open class DdMappingFileUploadTask
}

override fun getFilesList(): List<Uploader.UploadFileInfo> {
var mappingFile = File(mappingFilePath)
check(mappingFile.isPresent) {
"No mapping file value in the input property."
}
var mappingFile = mappingFile.get().asFile
if (!validateMappingFile(mappingFile)) return emptyList()

mappingFile = shrinkMappingFile(mappingFile)
Expand All @@ -67,13 +80,13 @@ open class DdMappingFileUploadTask
@Suppress("CheckInternal")
private fun validateMappingFile(mappingFile: File): Boolean {
if (!mappingFile.exists()) {
println("There's no mapping file $mappingFilePath, nothing to upload")
println("There's no mapping file ${mappingFile.absolutePath}, nothing to upload")
return false
}

check(mappingFile.isFile) { "Expected $mappingFilePath to be a file" }
check(mappingFile.isFile) { "Expected ${mappingFile.absolutePath} to be a file" }

check(mappingFile.canRead()) { "Cannot read file $mappingFilePath" }
check(mappingFile.canRead()) { "Cannot read file ${mappingFile.absolutePath}" }

return true
}
Expand All @@ -92,12 +105,28 @@ open class DdMappingFileUploadTask
if (shrinkedFile.exists()) {
shrinkedFile.delete()
}
// sort is needed to have predictable replacement in the following case:
// imagine there are 2 keys - "androidx.work" and "androidx.work.Job", and the latter
// occurs much more often than the rest under "androidx.work.*". So for the more efficient
// compression we need first to process the replacement of "androidx.work.Job" and only
// after that any possible prefix (which has a smaller length).
val replacements = mappingFilePackagesAliases.entries

val replacements = mappingFilePackagesAliases
.filter {
// not necessarily applicationId == package attribute from the Manifest, but it is
// best and cheapest effort to avoid wrong renaming (otherwise we may loose Git
// integration feature).
if (applicationId.get().startsWith(it.key)) {
DdAndroidGradlePlugin.LOGGER.warn(
"Renaming of package prefix=${it.key} will be skipped, because" +
" it belongs to the application package."
)
false
} else {
true
}
}
.entries
// sort is needed to have predictable replacement in the following case:
// imagine there are 2 keys - "androidx.work" and "androidx.work.Job", and the latter
// occurs much more often than the rest under "androidx.work.*". So for the more efficient
// compression we need first to process the replacement of "androidx.work.Job" and only
// after that any possible prefix (which has a smaller length).
.sortedByDescending { it.key.length }
.map {
Regex("(?<=^|\\W)${it.key}(?=\\W)") to it.value
Expand Down
Loading

0 comments on commit 70ba81c

Please sign in to comment.