Skip to content

Commit

Permalink
feat(plugin): support dependency packaging thru AGP application plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
rhenwinch committed Sep 23, 2024
1 parent 3fb5291 commit 9ec3aee
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 56 deletions.
12 changes: 8 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ dependencies {

gradlePlugin {
plugins {
create("com.flixclusive.gradle") {
id = "com.flixclusive.gradle"
implementationClass = "com.flixclusive.gradle.FlixclusiveProvider"
register("flixclusiveProvider") {
id = "flixclusive.provider"
implementationClass = "FlixclusiveProviderLibraryPlugin"
}
register("flixclusiveProviderApp") {
id = "flixclusive.provider.app"
implementationClass = "FlixclusiveProviderAppPlugin"
}
}
}
Expand All @@ -55,7 +59,7 @@ val sourcesJar = tasks.register<Jar>("sourcesJar") {
}

group = "com.github.flixclusive"
version = "1.1.4"
version = "1.2.0"

publishing {
repositories {
Expand Down
44 changes: 44 additions & 0 deletions src/main/kotlin/FlixclusiveProviderAppPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import com.android.build.api.dsl.ApplicationExtension
import com.flixclusive.gradle.FLX_PROVIDER_EXTENSION_NAME
import com.flixclusive.gradle.FlixclusiveProviderExtension
import com.flixclusive.gradle.configuration.registerConfigurations
import com.flixclusive.gradle.task.registerTasks
import com.flixclusive.gradle.util.configureAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

@Suppress("unused")
class FlixclusiveProviderAppPlugin : Plugin<Project> {
override fun apply(project: Project) {
with(project) {
with(pluginManager) {
apply("com.android.application")
}

extensions.create(FLX_PROVIDER_EXTENSION_NAME, FlixclusiveProviderExtension::class.java, project)

extensions.configure<ApplicationExtension> {
configureAndroid(commonExtension = this@configure)
}
}

registerTasks(project)
registerConfigurations(project)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,31 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.flixclusive.gradle

import com.android.build.api.dsl.LibraryExtension
import com.flixclusive.gradle.FLX_PROVIDER_EXTENSION_NAME
import com.flixclusive.gradle.FlixclusiveProviderExtension
import com.flixclusive.gradle.configuration.registerConfigurations
import com.flixclusive.gradle.task.registerTasks
import com.flixclusive.gradle.util.configureAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure

@Suppress("unused")
abstract class FlixclusiveProvider : Plugin<Project> {
class FlixclusiveProviderLibraryPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.extensions.create("flxProvider", FlixclusiveProviderExtension::class.java, project)
with(project) {
with(pluginManager) {
apply("com.android.library")
}

extensions.create(FLX_PROVIDER_EXTENSION_NAME, FlixclusiveProviderExtension::class.java, project)


extensions.configure<LibraryExtension> {
configureAndroid(commonExtension = this@configure)
}
}

registerTasks(project)
registerConfigurations(project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import javax.inject.Inject

internal const val APK_STUBS_DEPRECATED_MESSAGE = "This class is deprecated. See https://github.com/flixclusiveorg/core-stubs for more details."

const val FLX_PROVIDER_EXTENSION_NAME = "flxProvider"

@Suppress("unused", "MemberVisibilityCanBePrivate")
abstract class FlixclusiveProviderExtension @Inject constructor(val project: Project) {
/**
Expand Down Expand Up @@ -215,9 +217,9 @@ class Stubs(
}

fun ExtensionContainer.getFlixclusive(): FlixclusiveProviderExtension {
return getByName("flxProvider") as FlixclusiveProviderExtension
return getByName(FLX_PROVIDER_EXTENSION_NAME) as FlixclusiveProviderExtension
}

fun ExtensionContainer.findFlixclusive(): FlixclusiveProviderExtension? {
return findByName("flxProvider") as FlixclusiveProviderExtension?
return findByName(FLX_PROVIDER_EXTENSION_NAME) as FlixclusiveProviderExtension?
}
39 changes: 11 additions & 28 deletions src/main/kotlin/com/flixclusive/gradle/task/CompileDexTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@ import com.android.builder.dexing.DexArchiveBuilder
import com.android.builder.dexing.DexParameters
import com.android.builder.dexing.r8.ClassFileProviderFactory
import com.flixclusive.gradle.getFlixclusive
import com.flixclusive.gradle.util.findProviderClassName
import com.google.common.io.Closer
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.ClassNode
import org.gradle.api.tasks.IgnoreEmptyDirectories
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Path
import java.util.*
import java.util.Arrays
import java.util.stream.Collectors

abstract class CompileDexTask : DefaultTask() {
Expand Down Expand Up @@ -90,30 +93,10 @@ abstract class CompileDexTask : DefaultTask() {
dexOutput = dexOutputDir.toPath()
)

for (file in files) {
val reader = ClassReader(file.readAllBytes())

val classNode = ClassNode()
reader.accept(classNode, 0)

for (annotation in classNode.visibleAnnotations.orEmpty() + classNode.invisibleAnnotations.orEmpty()) {
if (annotation.desc == "Lcom/flixclusive/provider/FlixclusiveProvider;") {
val flixclusive = project.extensions.getFlixclusive()

require(flixclusive.providerClassName == null) {
"Only 1 active provider class per project is supported"
}

for (method in classNode.methods) {
if (method.name == "getManifest" && method.desc == "()Lcom/flixclusive/provider/ProviderManifest;") {
throw IllegalArgumentException("Provider class cannot override getManifest, use manifest.json system!")
}
}

flixclusive.providerClassName = classNode.name.replace('/', '.')
.also { providerClassFile.asFile.orNull?.writeText(it) }
}
}
val className = project.findProviderClassName(files = files)
if (className != null) {
extensions.getFlixclusive().providerClassName = className
providerClassFile.asFile.orNull?.writeText(className)
}
}
}
Expand Down
102 changes: 84 additions & 18 deletions src/main/kotlin/com/flixclusive/gradle/task/Tasks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,25 @@ package com.flixclusive.gradle.task

import com.android.build.gradle.BaseExtension
import com.android.build.gradle.tasks.ProcessLibraryManifest
import com.android.builder.dexing.ClassFileInputs
import com.flixclusive.gradle.FLX_PROVIDER_EXTENSION_NAME
import com.flixclusive.gradle.FlixclusiveProviderExtension
import com.flixclusive.gradle.getFlixclusive
import com.flixclusive.gradle.util.AndroidProjectType
import com.flixclusive.gradle.util.createProviderManifest
import com.flixclusive.gradle.util.findProviderClassName
import com.flixclusive.gradle.util.getAndroidProjectType
import com.flixclusive.gradle.util.isValidFilename
import groovy.json.JsonBuilder
import groovy.json.JsonGenerator
import org.gradle.api.Project
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.AbstractCopyTask
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.util.Arrays
import java.util.stream.Collectors

const val TASK_GROUP = "flixclusive"

Expand Down Expand Up @@ -93,27 +101,87 @@ fun registerTasks(project: Project) {
}
}

val androidManifestPath = project.file("src/main/AndroidManifest.xml")
val generateAndroidManifest = project.tasks.register("generateAndroidManifest") {
androidManifestPath.writeText("""
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:label="Flixclusive Provider"
android:supportsRtl="true">
<!-- This is a fake/mimic AndroidManifest.xml needed for compiling this provider - do NOT delete! -->
</application>
</manifest>
""".trimIndent())
}

project.afterEvaluate {
val make = project.tasks.register("make", Zip::class.java) {
group = TASK_GROUP
val compileDexTask = compileDex.get()
dependsOn(compileDexTask)

val manifestFile = intermediates.resolve("manifest.json")
val generateAndroidManifestTask = generateAndroidManifest.get()

val compileKotlinTask = project.tasks.findByName("compileDebugKotlin") as KotlinCompile?

when (project.getAndroidProjectType()) {
AndroidProjectType.APPLICATION -> {
dependsOn(generateAndroidManifestTask)

val assembleTask = project.tasks.getByName("assembleDebug")
dependsOn(assembleTask)

val apkFile = project.file("build/outputs/apk/debug/${project.name}-debug.apk")
from(project.zipTree(apkFile)) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
AndroidProjectType.LIBRARY -> {
val compileDexTask = compileDex.get()
dependsOn(compileDexTask)

from(compileDexTask.outputFile)

if (extension.requiresResources.get()) {
dependsOn(compileResources.get())
}
}
AndroidProjectType.UNKNOWN -> throw IllegalStateException("Non-android providers are not supported YET!")
}

from(manifestFile)
doFirst {
val manifestFile = intermediates.resolve("manifest.json")
from(manifestFile)

val (versionCode, _) = extension.getVersionDetails()
require(versionCode > 0L) {
"No version is set"
}

if (extension.providerClassName == null) {
if (providerClassFile.exists()) {
extension.providerClassName = providerClassFile.readText()
}
if (project.getAndroidProjectType() == AndroidProjectType.APPLICATION && compileKotlinTask != null) {
val fileStreams = compileKotlinTask.destinationDirectory.asFile.get().listFiles()
?.map { input ->
ClassFileInputs.fromPath(input.toPath())
.use { it.entries { _, _ -> true } }
}?.toTypedArray()

logger.lifecycle("Finding annotated provider class...")
Arrays.stream(fileStreams).flatMap { it }
.use { classesInput ->
val files = classesInput.collect(Collectors.toList())
val className = findProviderClassName(files)
if (className != null) {
logger.lifecycle("Annotated provider class: $className")
extension.providerClassName = className
}
}
} else if (project.getAndroidProjectType() == AndroidProjectType.LIBRARY && extension.providerClassName == null && providerClassFile.exists()) {
extension.providerClassName = providerClassFile.readText()
}


require(extension.providerClassName != null) {
"No provider class found, make sure your provider class is annotated with @FlixclusiveProvider"
}
Expand All @@ -128,14 +196,7 @@ fun registerTasks(project: Project) {
)
}

from(compileDexTask.outputFile)

if (extension.requiresResources.get()) {
dependsOn(compileResources.get())
}


val flxProvider = project.extensions.getByName("flxProvider") as FlixclusiveProviderExtension
val flxProvider = project.extensions.getByName(FLX_PROVIDER_EXTENSION_NAME) as FlixclusiveProviderExtension
val projectName = flxProvider.providerName.get()

if (!isValidFilename(projectName)) {
Expand All @@ -149,14 +210,19 @@ fun registerTasks(project: Project) {
destinationDirectory.set(project.buildDir)

doLast {
if (androidManifestPath.exists()) {
androidManifestPath.delete()
}

logger.lifecycle("Provider package ${projectName}.flx created at ${outputs.files.singleFile}")
}
}

project.rootProject.tasks.getByName("generateUpdaterJson").dependsOn(make)
project.rootProject.tasks.getByName("generateUpdaterJson")
.dependsOn(make.get())
project.tasks.register("deployWithAdb", DeployWithAdbTask::class.java) {
group = TASK_GROUP
dependsOn("make")
dependsOn(make.get())
dependsOn(":generateUpdaterJson")
}

Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/com/flixclusive/gradle/util/AndroidProjectType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.flixclusive.gradle.util

import org.gradle.api.Project

enum class AndroidProjectType {
APPLICATION,
LIBRARY,
UNKNOWN
}

fun Project.getAndroidProjectType(): AndroidProjectType {
return when {
plugins.hasPlugin("com.android.application") -> AndroidProjectType.APPLICATION
plugins.hasPlugin("com.android.library") -> AndroidProjectType.LIBRARY
else -> AndroidProjectType.UNKNOWN
}
}
Loading

0 comments on commit 9ec3aee

Please sign in to comment.