Skip to content

Commit

Permalink
Merge pull request #7 from UbiqueInnovation/feature/task-io-dependency
Browse files Browse the repository at this point in the history
explicit robust task i/o dependencies
  • Loading branch information
fbzli authored Oct 7, 2024
2 parents 4096824 + 75fa4ac commit 66102f6
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 94 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The plugin contains the following functionality:
* Writes build information as fields to the `BuildConfig` class (see section [Build information](#build-information))
* Registers a task `injectMetadataIntoManifest<VARIANT>` for each variant that injects the build information into the Android
manifest
* Registers a task `generateAppIcon<VARIANT>` for each variant that generates an app icon with the flavor name as overlay
* Registers a task `labelAppIcon<VARIANT>` for each variant that generates an app icon with the flavor name as overlay
* Registers a task `uploadToAlpaka<VARIANT>` for each release variant that uploads the APK to the Alpaka backend

## Configuration
Expand Down
68 changes: 41 additions & 27 deletions alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/AlpakaPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ch.ubique.gradle.alpaka.config.AlpakaPluginConfig
import ch.ubique.gradle.alpaka.extensions.applicationvariant.launcherIconLabel
import ch.ubique.gradle.alpaka.extensions.capitalize
import ch.ubique.gradle.alpaka.extensions.getMergedManifestFile
import ch.ubique.gradle.alpaka.extensions.productflavor.launcherIconLabel as flavorLauncherIconLabel
import ch.ubique.gradle.alpaka.extensions.listFilesOrEmpty
import ch.ubique.gradle.alpaka.extensions.productflavor.alpakaUploadKey
import ch.ubique.gradle.alpaka.model.UploadRequest
import ch.ubique.gradle.alpaka.task.IconTask
Expand All @@ -25,6 +25,7 @@ import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.plugin.extraProperties
import java.io.File
import ch.ubique.gradle.alpaka.extensions.productflavor.launcherIconLabel as flavorLauncherIconLabel

abstract class AlpakaPlugin : Plugin<Project> {

Expand Down Expand Up @@ -81,7 +82,8 @@ abstract class AlpakaPlugin : Plugin<Project> {
"injectMetadataIntoManifest${variantName.capitalize()}",
InjectMetadataIntoManifestTask::class.java
) { manifestTask ->
manifestTask.outputs.file(project.getMergedManifestFile(variantName))
val mergedManifestFile = project.getMergedManifestFile(variantName)
manifestTask.mergedManifestFile = mergedManifestFile
manifestTask.variantName = variantName
manifestTask.flavor = flavor
manifestTask.buildType = buildType
Expand All @@ -90,10 +92,11 @@ abstract class AlpakaPlugin : Plugin<Project> {
manifestTask.buildBatch = buildBatch
manifestTask.buildTimestamp = buildTimestamp
manifestTask.buildBranch = buildBranch
manifestTask.outputs.file(mergedManifestFile)
}

variant.outputs.forEach { output ->
output.processManifestProvider.get().finalizedBy(injectManifestTask)
output.processManifestProvider.configure { it.finalizedBy(injectManifestTask) }
}
project.tasks.named("process${variantName.capitalize()}ManifestForPackage") {
it.dependsOn(injectManifestTask)
Expand Down Expand Up @@ -128,16 +131,20 @@ abstract class AlpakaPlugin : Plugin<Project> {
}

val iconTask = project.tasks.register(
"generateAppIcon${variantName.capitalize()}",
"labelAppIcon${variantName.capitalize()}",
IconTask::class.java
) { iconTask ->
iconTask.variantName = variantName
iconTask.flavor = flavor
iconTask.buildType = buildType
iconTask.labelValue = if (labelAppIcons) labelValue else null
iconTask.targetWebIconFile = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType)
iconTask.sourceWebIconFile = project.provider { findWebIcon(project.projectDir, flavor) }
iconTask.mergedManifestFile = project.getMergedManifestFile(variantName)
iconTask.generatedWebIcon = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType)
iconTask.generatedIconDir = getGeneratedIconDir(project.layout.buildDirectory, flavor, buildType)
iconTask.outputs.upToDateWhen { false } // always run the task

iconTask.mustRunAfter(project.tasks.named("injectMetadataIntoManifest${variantName.capitalize()}"))
}

project.tasks.named("map${variantName.capitalize()}SourceSetPaths") { it.dependsOn(iconTask) }
Expand Down Expand Up @@ -166,33 +173,32 @@ abstract class AlpakaPlugin : Plugin<Project> {
"uploadToAlpaka${variantName.capitalize()}",
UploadToAlpakaBackendTask::class.java
) { uploadTask ->
val uploadRequest = variant.outputs.first().let {
UploadRequest(
apk = it.outputFile,
appIcon = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType),
appName = "", // Will be set from manifest inside the task
packageName = packageName,
flavor = flavor,
branch = buildBranch,
minSdk = minSdk,
targetSdk = targetSdk,
usesFeature = emptyList(), // Will be set from manifest inside the task
buildId = buildId,
buildNumber = buildNumber,
buildTime = buildTimestamp,
buildBatch = buildBatch,
changelog = "", // Will be set inside the task
signature = "", // Will be set inside the task
version = versionName,
)
}

uploadTask.variant = variant
uploadTask.flavor = flavor
uploadTask.buildType = buildType
uploadTask.uploadKey = uploadKey ?: throw GradleException("No alpakaUploadKey specified")
uploadTask.mergedManifestFile = project.getMergedManifestFile(variantName)
uploadTask.apk = project.provider { variant.outputs.first().outputFile }
uploadTask.webIcon = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType)
uploadTask.proxy = pluginExtension.proxy.orNull
uploadTask.commitCount = pluginExtension.changelogCommitCount.orNull

val uploadRequest = UploadRequest(
appName = "", // Will be set from manifest inside the task
packageName = packageName,
flavor = flavor,
branch = buildBranch,
minSdk = minSdk,
targetSdk = targetSdk,
usesFeature = emptyList(), // Will be set from manifest inside the task
buildId = buildId,
buildNumber = buildNumber,
buildTime = buildTimestamp,
buildBatch = buildBatch,
changelog = "", // Will be set inside the task
signature = "", // Will be set inside the task
version = versionName,
)
uploadTask.uploadRequest = uploadRequest

uploadTask.dependsOn("assemble${variantName.capitalize()}")
Expand All @@ -207,8 +213,16 @@ abstract class AlpakaPlugin : Plugin<Project> {
return ext
}

private fun findWebIcon(moduleDir: File, flavor: String): File {
val dirs = sequenceOf(File(moduleDir, "src/$flavor"), File(moduleDir, "src/main"), moduleDir)
return dirs
.flatMap { it.listFilesOrEmpty() }
.find { it.name.matches(Regex(".*(web|playstore|512)\\.(png|webp)")) }
?: throw GradleException("Must provide web icon matching (web|playstore|512).(png|webp) in one of the following locations:\n ${dirs.joinToString()}")
}

private fun getGeneratedWebIconFile(buildDir: DirectoryProperty, flavor: String, buildType: String): Provider<File> {
return getGeneratedIconDir(buildDir, flavor, buildType).map { it.file("web-icon.png").asFile }
return buildDir.file("outputs/launcher-icon/$flavor/$buildType/web-icon.png").map { it.asFile }
}

private fun getGeneratedIconDir(buildDir: DirectoryProperty, flavor: String, buildType: String): Provider<Directory> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ fun File?.olderThan(other: File, orLastModified: Long): Boolean {
return this?.lastModified()?.let {
other.lastModified() > it || orLastModified > it
} ?: true
}
}

fun File.listFilesOrEmpty(): List<File> {
return listFiles()?.asList() ?: emptyList()
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package ch.ubique.gradle.alpaka.extensions


import com.android.build.gradle.BaseExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider
import java.io.File

/**
* returns File with merged manifest
*/
fun Project.getMergedManifestFile(variantName: String): File {
return File(
layout.buildDirectory.asFile.get(),
"intermediates/merged_manifests/${variantName}/process${variantName.capitalize()}Manifest/AndroidManifest.xml"
)
fun Project.getMergedManifestFile(variantName: String): Provider<File> {
return layout.buildDirectory
.file("intermediates/merged_manifests/${variantName}/process${variantName.capitalize()}Manifest/AndroidManifest.xml")
.map { it.asFile }
}


fun Project.getResDirs(flavor: String): List<File> {
val androidModules: List<BaseExtension> = configurations
.asSequence()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package ch.ubique.gradle.alpaka.model

import ch.ubique.gradle.alpaka.network.dto.UploadDataDto
import org.gradle.api.provider.Provider
import java.io.File

data class UploadRequest(
val apk: File,
val appIcon: Provider<File>,
val appName: String,
val packageName: String,
val flavor: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ class BackendRepository {
resetService()
}

fun appsUpload(uploadRequest: UploadRequest, uploadKey: String) {
fun appsUpload(uploadRequest: UploadRequest, apk: File, appIcon: File, uploadKey: String) {
val data = MoshiBuilder.createMoshi()
.toJson(uploadRequest.toUploadDataJson(uploadKey = uploadKey))
.toByteArray()
.toRequestBody("application/json".toMediaType())

val response = service.appsUpload(
apk = uploadRequest.apk.toPartMap("apk", "application/octet-stream"),
icon = uploadRequest.appIcon.get().toPartMap("icon", "image/png"),
data = data
apk = apk.toPartMap("apk", "application/octet-stream"),
icon = appIcon.toPartMap("icon", "image/png"),
data = data,
).execute()

if (response.isSuccessful.not()) throw HttpException(response)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package ch.ubique.gradle.alpaka.task

import ch.ubique.gradle.alpaka.extensions.getMergedManifestFile
import ch.ubique.gradle.alpaka.extensions.getResDirs
import ch.ubique.gradle.alpaka.extensions.olderThan
import ch.ubique.gradle.alpaka.utils.IconUtils
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
Expand All @@ -32,59 +30,64 @@ abstract class IconTask : DefaultTask() {
@get:Optional
abstract var labelValue: String?

@get:Input
abstract var targetWebIconFile: Provider<File>
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract var mergedManifestFile: Provider<File>

@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract var sourceWebIconFile: Provider<File>

@get:OutputDirectory
abstract var generatedIconDir: Provider<Directory>

@get:OutputFile
abstract var generatedWebIcon: Provider<File>

@TaskAction
fun iconAction() {
val moduleDir = File(project.rootDir, project.name)
val targetWebIcon = targetWebIconFile.get().also {
val webIconTarget = generatedWebIcon.get().also {
it.parentFile.mkdirs()
it.createNewFile()
}
val generatedIconDir = generatedIconDir.get().asFile

val gradleLastModified = listOf(
File(moduleDir, "build.gradle").lastModified(),
File(moduleDir, "build.gradle.kts").lastModified(),
File(project.projectDir, "build.gradle").lastModified(),
File(project.projectDir, "build.gradle.kts").lastModified(),
File(project.rootDir, "build.gradle").lastModified(),
File(project.rootDir, "build.gradle.kts").lastModified(),
File(project.rootDir, "settings.gradle").lastModified(),
File(project.rootDir, "settings.gradle.kts").lastModified(),
File(project.rootDir, "gradle/libs.versions.toml").lastModified(),
).max()

val manifestFile = project.getMergedManifestFile(variantName)
val resDirs = project.getResDirs(flavor)

val allIcons = IconUtils.findIcons(resDirs, manifestFile)
val allIcons = IconUtils.findIcons(resDirs, mergedManifestFile.get())

val webIconSource = (
(File(moduleDir, "src/$flavor").listFiles() ?: arrayOf()) +
(File(moduleDir, "src/main").listFiles() ?: arrayOf()) +
(moduleDir.listFiles() ?: arrayOf())
).find { it.name.matches(Regex(".*(web|playstore|512)\\.(png|webp)")) }
?: throw GradleException("Web icon source not found")
val webIconSource = sourceWebIconFile.get()

val bannerLabel = labelValue

if (bannerLabel.isNullOrEmpty()) {
if (webIconSource.olderThan(targetWebIcon, gradleLastModified)) {
// delete any unwanted files
generatedIconDir.deleteRecursively()
// copy web icon as-is
if (webIconSource.olderThan(webIconTarget, gradleLastModified)) {
logger.info("No banner label, copy source icon")
webIconSource.copyTo(targetWebIcon, overwrite = true)
webIconSource.copyTo(webIconTarget, overwrite = true)
}
} else {
if (webIconSource.olderThan(targetWebIcon, gradleLastModified)) {
if (webIconSource.olderThan(webIconTarget, gradleLastModified)) {
logger.info("Apply banner label to web icon: ${webIconSource.absolutePath}")
IconUtils.drawLabel(webIconSource, targetWebIcon, bannerLabel, adaptive = false)
IconUtils.drawLabel(webIconSource, webIconTarget, bannerLabel, adaptive = false)
}

allIcons.forEach iconsForEach@{ original ->
val resTypeName = original.parentFile.name
val originalBaseName = original.name.substringBefore(".")
val targetDir = File(generatedIconDir.get().asFile, resTypeName)
val targetDir = File(generatedIconDir, resTypeName)

val modified = targetDir.listFiles { file ->
file.name.matches(Regex("$originalBaseName\\.[^.]+"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package ch.ubique.gradle.alpaka.task

import ch.ubique.gradle.alpaka.extensions.getMergedManifestFile
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.work.DisableCachingByDefault
import java.io.File
Expand Down Expand Up @@ -49,9 +50,12 @@ abstract class InjectMetadataIntoManifestTask : DefaultTask() {
@get:Input
abstract var buildBranch: String

@get:InputFile
abstract var mergedManifestFile: Provider<File>

@TaskAction
fun injectMetadataIntoManifest() {
val manifestFile = project.getMergedManifestFile(variantName)
val manifestFile = mergedManifestFile.get()
if (manifestFile.exists()) {
manipulateManifestFile(manifestFile)
} else {
Expand Down
Loading

0 comments on commit 66102f6

Please sign in to comment.