Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configuration caching #257

Merged
merged 37 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a3a0147
Make BugsnagInstallJniLibsTask support configuration caching
ZacSweers Aug 8, 2020
d9746ed
Use providerfactory
ZacSweers Aug 8, 2020
7873dec
Use injected gradle version
ZacSweers Aug 8, 2020
8a0c008
Fix doc
ZacSweers Aug 8, 2020
a0af86a
Make BugsnagReleasesTask compatible with configuration caching
ZacSweers Aug 8, 2020
7fc125d
Use register pattern
ZacSweers Aug 8, 2020
a85f901
Use ProjectLayout
ZacSweers Aug 9, 2020
61e14df
Make classes sealed to prevent extra source extension
ZacSweers Aug 9, 2020
cf428fc
Make exec internal
ZacSweers Aug 9, 2020
e0b9033
Fully support configuration caching in BugsnagInstallJniLibsTask
ZacSweers Aug 9, 2020
757f36a
Use more flexible versioning checks
ZacSweers Aug 9, 2020
98d8c7a
Make BugsnagUploadNdkTask compatible with configuration caching
ZacSweers Aug 9, 2020
65d4f1a
Make BugsnagUploadProguardTask compatible with configuration caching
ZacSweers Aug 9, 2020
a9f26ce
Match naming pattern
ZacSweers Aug 9, 2020
1c212fd
Add gradle version input
ZacSweers Aug 9, 2020
a59825a
Fix fileCollection use on older versions
ZacSweers Aug 9, 2020
023aafd
Borrow some kotlin gradle util
ZacSweers Aug 9, 2020
1c0f048
Use direct task logger in BugsnagManifestUuidTask
ZacSweers Aug 9, 2020
26e3cdd
Fix nullability check
ZacSweers Aug 9, 2020
bbc4d8d
Use kotlin extensions!
ZacSweers Aug 9, 2020
9e606ef
Backport configurablefilecollection use in BugsnagUploadNdkTask
ZacSweers Aug 9, 2020
ba7eed8
Fix missing inputs
ZacSweers Aug 9, 2020
a42ea8d
Backport ConfigurableFileCollection in BugsnagReleasesTask
ZacSweers Aug 9, 2020
714a6e9
Detekt fixes
ZacSweers Aug 9, 2020
5046dd7
Use a BuildService for OkHttpClient reuse
ZacSweers Aug 9, 2020
4eec71c
Make UploadRequestClient use a build service on gradle 6.1+
ZacSweers Aug 9, 2020
68f3bc1
Detekt issues
ZacSweers Aug 9, 2020
a91949b
Remove import
ZacSweers Aug 9, 2020
f1c443b
Use fast file hashing + de-dupe buildUuid from hashcode generation
ZacSweers Aug 9, 2020
2f136e3
Update test
ZacSweers Aug 9, 2020
55eb3f1
Use single provider instance
ZacSweers Aug 9, 2020
8a6e5db
Use single provider instance
ZacSweers Aug 9, 2020
dcf8007
Revert custom hashcode
ZacSweers Aug 10, 2020
d8ed6ff
Remove content
ZacSweers Aug 10, 2020
d1921a6
Merge branch 'v5' into z/configurationCaching
fractalwrench Aug 10, 2020
d7bada8
address review feedback
fractalwrench Aug 10, 2020
11f3e2b
docs: add changelog entry
fractalwrench Aug 10, 2020
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
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 2 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#Mon Jul 06 17:13:48 BST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-rc-6-all.zip
fractalwrench marked this conversation as resolved.
Show resolved Hide resolved
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
21 changes: 3 additions & 18 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Expand All @@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
Expand All @@ -64,29 +64,14 @@ echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import java.io.File

@JsonClass(generateAdapter = true)
data class AndroidManifestInfo(
var apiKey: String,
var versionCode: String,
var buildUUID: String?,
var versionName: String,
var applicationId: String
val apiKey: String,
val versionCode: String,
val buildUUID: String?,
val versionName: String,
val applicationId: String
fractalwrench marked this conversation as resolved.
Show resolved Hide resolved
) {
internal fun write(file: File) {
file.sink().buffer().use {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ import java.io.IOException
import java.io.PrintWriter
import javax.xml.parsers.ParserConfigurationException

class AndroidManifestParser {
internal class AndroidManifestParser {

private val namespace = Namespace("http://schemas.android.com/apk/res/android", "android")

@Throws(ParserConfigurationException::class, SAXException::class, IOException::class)
fractalwrench marked this conversation as resolved.
Show resolved Hide resolved
fun readManifest(manifestPath: File, logger: Logger): AndroidManifestInfo {
logger.debug("Bugsnag: Reading manifest at: $manifestPath")
val root = XmlParser().parse(manifestPath)
Expand Down Expand Up @@ -63,6 +62,7 @@ class AndroidManifestParser {
|versionCode=$versionCode
|versionName=$versionName
|applicationId=$applicationId
|Manifest file = $manifestPath
""".trimMargin())
}
return AndroidManifestInfo(apiKey, versionCode, buildUUID, versionName, applicationId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bugsnag.android.gradle

import com.bugsnag.android.gradle.internal.BugsnagHttpClientHelper
import org.gradle.api.provider.Property

interface BugsnagFileUploadTask {
Expand All @@ -8,6 +9,7 @@ interface BugsnagFileUploadTask {
val endpoint: Property<String>
val retryCount: Property<Int>
val timeoutMillis: Property<Long>
val httpClientHelper: Property<BugsnagHttpClientHelper>
fractalwrench marked this conversation as resolved.
Show resolved Hide resolved

fun configureWith(bugsnag: BugsnagPluginExtension) {
failOnUploadError.set(bugsnag.failOnUploadError)
Expand Down
126 changes: 114 additions & 12 deletions src/main/kotlin/com.bugsnag.android.gradle/BugsnagInstallJniLibsTask.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,56 @@
package com.bugsnag.android.gradle

import com.bugsnag.android.gradle.internal.GradleVersions
import com.bugsnag.android.gradle.internal.register
import com.bugsnag.android.gradle.internal.versionNumber
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.ArchiveOperations
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.FileTree
import org.gradle.api.file.ProjectLayout
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.WorkResult
import java.io.File
import javax.inject.Inject

open class BugsnagInstallJniLibsTask @Inject constructor(
objects: ObjectFactory
sealed class BugsnagInstallJniLibsTask(
objects: ObjectFactory,
projectLayout: ProjectLayout
) : DefaultTask() {

init {
description = "Copies shared object files from the bugsnag-android AAR to the required build directory"
group = BugsnagPlugin.GROUP_NAME
}

@get:OutputDirectory
val buildDirDestination: RegularFileProperty = objects.fileProperty()
val buildDirDestination: DirectoryProperty = objects.directoryProperty()
.convention(projectLayout.buildDirectory.dir("intermediates/bugsnag-libs"))

@get:InputFiles
val bugsnagArtefacts: Property<FileCollection> = objects.property(FileCollection::class.java)
abstract val bugsnagArtifacts: ConfigurableFileCollection

internal abstract fun copy(action: (CopySpec) -> Unit): WorkResult
internal abstract fun zipTree(file: File): FileTree

/**
* Looks at all the dependencies and their dependencies and finds the `com.bugsnag` artifacts with SO files.
*/
@TaskAction
fun setupNdkProject() {
bugsnagArtefacts.get().forEach { file: File ->
project.copy {
it.from(project.zipTree(file))
it.into(project.file(buildDirDestination))
val destination = buildDirDestination.asFile.get()
bugsnagArtifacts.forEach { file: File ->
copy {
it.from(zipTree(file))
it.into(destination)
}
}
}
Expand All @@ -45,7 +59,7 @@ open class BugsnagInstallJniLibsTask @Inject constructor(
private val sharedObjectAarIds = listOf("bugsnag-android", "bugsnag-android-ndk",
"bugsnag-plugin-android-anr", "bugsnag-plugin-android-ndk")

internal fun resolveBugsnagArtefacts(project: Project): FileCollection {
internal fun resolveBugsnagArtifacts(project: Project): FileCollection {
val files = project.configurations
.filter { it.toString().contains("CompileClasspath") }
.map { it.resolvedConfiguration }
Expand All @@ -60,5 +74,93 @@ open class BugsnagInstallJniLibsTask @Inject constructor(
.toSet()
return project.files(files)
}

/**
* Registers the appropriate subtype to this [project] with the given [name] and
* [configurationAction]
*/
internal fun register(
project: Project,
name: String,
configurationAction: BugsnagInstallJniLibsTask.() -> Unit
): TaskProvider<out BugsnagInstallJniLibsTask> {
val gradleVersion = project.gradle.versionNumber()
return when {
gradleVersion >= GradleVersions.VERSION_6 -> {
when {
gradleVersion >= GradleVersions.VERSION_6_6 -> {
Copy link
Contributor

Choose a reason for hiding this comment

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

style nit: the indentation is off by two spaces for this when block

project.tasks.register<BugsnagInstallJniLibsTaskGradle66Plus>(name, configurationAction)
}
else -> {
project.tasks.register<BugsnagInstallJniLibsTaskGradle6Plus>(name, configurationAction)
}
}
}
gradleVersion >= GradleVersions.VERSION_5_3 -> {
project.tasks.register<BugsnagInstallJniLibsTask53Plus>(name, configurationAction)
}
else -> {
project.tasks.register<BugsnagInstallJniLibsTaskLegacy>(name, configurationAction)
}
}
}
}
}

fractalwrench marked this conversation as resolved.
Show resolved Hide resolved
/**
* Legacy [BugsnagInstallJniLibsTask] task that requires using [getProject] and
* [ProjectLayout.configurableFiles].
*/
internal open class BugsnagInstallJniLibsTaskLegacy @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout
) : BugsnagInstallJniLibsTask(objects, projectLayout) {
@Suppress("DEPRECATION") // Here for backward compatibility
@get:InputFiles
override val bugsnagArtifacts: ConfigurableFileCollection = projectLayout.configurableFiles()

override fun copy(action: (CopySpec) -> Unit): WorkResult = project.copy(action)
override fun zipTree(file: File): FileTree = project.zipTree(file)
}

/** Legacy [BugsnagInstallJniLibsTask] task that requires using [getProject]. */
internal open class BugsnagInstallJniLibsTask53Plus @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout
) : BugsnagInstallJniLibsTask(objects, projectLayout) {
@get:InputFiles
override val bugsnagArtifacts: ConfigurableFileCollection = objects.fileCollection()

override fun copy(action: (CopySpec) -> Unit): WorkResult = project.copy(action)
override fun zipTree(file: File): FileTree = project.zipTree(file)
}

/** A Gradle 6+ compatible [BugsnagInstallJniLibsTask], which uses [FileSystemOperations]. */
internal open class BugsnagInstallJniLibsTaskGradle6Plus @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout,
private val fsOperations: FileSystemOperations
) : BugsnagInstallJniLibsTask(objects, projectLayout) {
@get:InputFiles
override val bugsnagArtifacts: ConfigurableFileCollection = objects.fileCollection()

override fun copy(action: (CopySpec) -> Unit): WorkResult = fsOperations.copy(action)
override fun zipTree(file: File): FileTree = project.zipTree(file)
}

/**
* A Gradle 6.6+ compatible [BugsnagInstallJniLibsTask], which uses [FileSystemOperations]
* and [ArchiveOperations] to support configuration caching.
*/
internal open class BugsnagInstallJniLibsTaskGradle66Plus @Inject constructor(
objects: ObjectFactory,
projectLayout: ProjectLayout,
private val fsOperations: FileSystemOperations,
private val archiveOperations: ArchiveOperations
fractalwrench marked this conversation as resolved.
Show resolved Hide resolved
) : BugsnagInstallJniLibsTask(objects, projectLayout) {
@get:InputFiles
override val bugsnagArtifacts: ConfigurableFileCollection = objects.fileCollection()

override fun copy(action: (CopySpec) -> Unit): WorkResult = fsOperations.copy(action)
override fun zipTree(file: File): FileTree = archiveOperations.zipTree(file)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.bugsnag.android.gradle
import com.android.build.gradle.api.ApkVariant
import com.android.build.gradle.api.ApkVariantOutput
import com.android.build.gradle.tasks.ManifestProcessorTask
import com.bugsnag.android.gradle.internal.property
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.Directory
Expand All @@ -27,7 +28,7 @@ abstract class BaseBugsnagManifestUuidTask(objects: ObjectFactory) : DefaultTask
}

@get:Input
val buildUuid: Property<String> = objects.property(String::class.java)
val buildUuid: Property<String> = objects.property()

@get:OutputFile
val manifestInfoProvider: RegularFileProperty = objects.fileProperty()
Expand Down Expand Up @@ -59,10 +60,10 @@ open class BugsnagManifestUuidTask @Inject constructor(objects: ObjectFactory) :
fun updateManifest() {
val manifestPath = getManifestPaths(project, variant, variantOutput)
if (manifestPath == null) {
project.logger.warn("Bugsnag: Failed to find manifest at $manifestPath for $variantOutput")
logger.warn("Bugsnag: Failed to find manifest at $manifestPath for $variantOutput")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

While this task isn't compatible with configuration caching (not an issue since it wouldn't be used on versions that support it anyway), I did clean this up just to make it more consistent with other tasks

}

project.logger.info("Bugsnag: Updating manifest with build UUID: $manifestPath")
logger.info("Bugsnag: Updating manifest with build UUID: $manifestPath")

// read the manifest information and store it for subsequent tasks
val manifestParser = AndroidManifestParser()
Expand Down Expand Up @@ -98,16 +99,16 @@ open class BugsnagManifestUuidTask @Inject constructor(objects: ObjectFactory) :
}
val processManifest = variantOutput.processManifestProvider.get()
if (getMergedManifest) {
directoryMerged = getManifestOutputDir(processManifest, project)
directoryMerged = getManifestOutputDir(processManifest)
if (directoryMerged != null) {
addManifestPath(manifestPaths, directoryMerged, project.logger, variantOutput)
addManifestPath(manifestPaths, directoryMerged, logger, variantOutput)
}
}

// Attempt to get the bundle manifest directory if required
if (getBundleManifest) {
directoryBundle = resolveBundleManifestOutputDirectory(processManifest)
addManifestPath(manifestPaths, directoryBundle, project.logger, variantOutput)
addManifestPath(manifestPaths, directoryBundle, logger, variantOutput)
}
require(manifestPaths.size == 1) { "Unexpected number of manifest paths.$manifestPaths" }
return manifestPaths[0]
Expand All @@ -116,31 +117,29 @@ open class BugsnagManifestUuidTask @Inject constructor(objects: ObjectFactory) :
private fun addManifestPath(manifestPaths: MutableList<File?>, directory: File, logger: Logger, variantOutput: ApkVariantOutput) {
val manifestFile = Paths.get(directory.toString(), variantOutput.dirName, "AndroidManifest.xml").toFile()
if (manifestFile.exists()) {
logger.info("Bugsnag: Found manifest at ${manifestFile}")
logger.info("Bugsnag: Found manifest at $manifestFile")
manifestPaths.add(manifestFile)
} else {
logger.info("Bugsnag: Failed to find manifest at ${manifestFile}")
logger.info("Bugsnag: Failed to find manifest at $manifestFile")
}
}

private fun getManifestOutputDir(processManifest: ManifestProcessorTask, project: Project): File? {
try {
private fun getManifestOutputDir(processManifest: ManifestProcessorTask): File? {
return try {
val outputDir = processManifest.javaClass.getMethod("getManifestOutputDirectory").invoke(processManifest)
if (outputDir is File) {
return outputDir
outputDir
} else {
// gradle 4.7 introduced a provider API for lazy evaluation of properties,
// AGP subsequently changed the API from File to Provider<File>
// see https://docs.gradle.org/4.7/userguide/lazy_configuration.html
@Suppress("UNCHECKED_CAST") val dir = (outputDir as Provider<Directory?>).orNull
if (dir != null) {
return dir.asFile
}
dir?.asFile
}
} catch (exc: Throwable) {
project.logger.warn("Bugsnag: failed to find manifestOutputDir", exc)
logger.warn("Bugsnag: failed to find manifestOutputDir", exc)
null
}
return null
}

private fun resolveBundleManifestOutputDirectory(processManifest: ManifestProcessorTask): File {
Expand Down
Loading