diff --git a/src/main/kotlin/com/lagradost/gradle/CloudstreamExtension.kt b/src/main/kotlin/com/lagradost/gradle/CloudstreamExtension.kt index 80ef0b6..347ad45 100644 --- a/src/main/kotlin/com/lagradost/gradle/CloudstreamExtension.kt +++ b/src/main/kotlin/com/lagradost/gradle/CloudstreamExtension.kt @@ -8,7 +8,11 @@ import javax.inject.Inject abstract class CloudstreamExtension @Inject constructor(project: Project) { val userCache = project.gradle.gradleUserHomeDir.resolve("caches").resolve("cloudstream") - var discord: ApkInfo? = null + + var apkinfo: ApkInfo? = null + internal set + + internal var pluginClassName: String? = null } class ApkInfo(extension: CloudstreamExtension, val version: Int) { @@ -20,4 +24,8 @@ class ApkInfo(extension: CloudstreamExtension, val version: Int) { fun ExtensionContainer.getCloudstream(): CloudstreamExtension { return getByName("cloudstream") as CloudstreamExtension +} + +fun ExtensionContainer.findCloudstream(): CloudstreamExtension? { + return findByName("cloudstream") as CloudstreamExtension? } \ No newline at end of file diff --git a/src/main/kotlin/com/lagradost/gradle/CloudstreamPlugin.kt b/src/main/kotlin/com/lagradost/gradle/CloudstreamPlugin.kt index c5bf601..4540e25 100644 --- a/src/main/kotlin/com/lagradost/gradle/CloudstreamPlugin.kt +++ b/src/main/kotlin/com/lagradost/gradle/CloudstreamPlugin.kt @@ -2,9 +2,12 @@ package com.lagradost.gradle import org.gradle.api.Plugin import org.gradle.api.Project +import com.lagradost.gradle.tasks.registerTasks abstract class CloudstreamPlugin : Plugin { override fun apply(project: Project) { project.extensions.create("cloudstream", CloudstreamExtension::class.java, project) + + registerTasks(project) } } \ No newline at end of file diff --git a/src/main/kotlin/com/lagradost/gradle/tasks/CompileDexTask.kt b/src/main/kotlin/com/lagradost/gradle/tasks/CompileDexTask.kt new file mode 100644 index 0000000..2c2937c --- /dev/null +++ b/src/main/kotlin/com/lagradost/gradle/tasks/CompileDexTask.kt @@ -0,0 +1,99 @@ +package com.lagradost.gradle.tasks + +import com.cloudstream.gradle.getCloudstream +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.internal.errors.MessageReceiverImpl +import com.android.build.gradle.options.SyncOptions.ErrorFormatMode +import com.android.builder.dexing.ClassFileInputs +import com.android.builder.dexing.DexArchiveBuilder +import com.android.builder.dexing.DexParameters +import com.android.builder.dexing.r8.ClassFileProviderFactory +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.slf4j.LoggerFactory +import java.io.File +import java.nio.file.Path +import java.util.* +import java.util.stream.Collectors + +abstract class CompileDexTask : DefaultTask() { + @InputFiles + @SkipWhenEmpty + @IgnoreEmptyDirectories + val input: ConfigurableFileCollection = project.objects.fileCollection() + + @get:OutputFile + abstract val outputFile: RegularFileProperty + + @get:OutputFile + abstract val pluginClassFile: RegularFileProperty + + @Suppress("UnstableApiUsage") + @TaskAction + fun compileDex() { + val android = project.extensions.getByName("android") as BaseExtension + + val dexOutputDir = outputFile.get().asFile.parentFile + + Closer.create().use { closer -> + val dexBuilder = DexArchiveBuilder.createD8DexBuilder( + DexParameters( + minSdkVersion = android.defaultConfig.maxSdkVersion ?: 24, + debuggable = true, + dexPerClass = false, + withDesugaring = true, + desugarBootclasspath = ClassFileProviderFactory(android.bootClasspath.map(File::toPath)) + .also { closer.register(it) }, + desugarClasspath = ClassFileProviderFactory(listOf()).also { closer.register(it) }, + coreLibDesugarConfig = null, + coreLibDesugarOutputKeepRuleFile = null, + messageReceiver = MessageReceiverImpl( + ErrorFormatMode.HUMAN_READABLE, + LoggerFactory.getLogger(CompileDexTask::class.java) + ) + ) + ) + + val fileStreams = + input.map { input -> ClassFileInputs.fromPath(input.toPath()).use { it.entries { _, _ -> true } } } + .toTypedArray() + + Arrays.stream(fileStreams).flatMap { it } + .use { classesInput -> + val files = classesInput.collect(Collectors.toList()) + + dexBuilder.convert( + files.stream(), + 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/lagradost/annotations/CloudstreamPlugin;") { + val cloudstream = project.extensions.getCloudstream() + + require(cloudstream.pluginClassName == null) { + "Only 1 active plugin class per project is supported" + } + + cloudstream.pluginClassName = classNode.name.replace('/', '.') + .also { pluginClassFile.asFile.orNull?.writeText(it) } + } + } + } + } + } + + logger.lifecycle("Compiled dex to ${outputFile.get()}") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lagradost/gradle/tasks/GenSourcesTask.kt b/src/main/kotlin/com/lagradost/gradle/tasks/GenSourcesTask.kt new file mode 100644 index 0000000..fc55d29 --- /dev/null +++ b/src/main/kotlin/com/lagradost/gradle/tasks/GenSourcesTask.kt @@ -0,0 +1,43 @@ +package com.lagradost.gradle.tasks + +import com.cloudstream.gradle.getCloudstream +import jadx.api.JadxArgs +import jadx.api.JadxDecompiler +import jadx.api.impl.NoOpCodeCache +import jadx.api.impl.SimpleCodeWriter +import jadx.plugins.input.dex.DexInputPlugin +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import java.util.function.Function + +abstract class GenSourcesTask : DefaultTask() { + @TaskAction + fun genSources() { + val extension = project.extensions.getAliucord() + val apkinfo = extension.apkinfo!! + + val sourcesJarFile = apkinfo.cache.resolve("cloudstream-${apkinfo.version}-sources.jar") + + val args = JadxArgs() + args.setInputFile(apkinfo.apkFile) + args.outDirSrc = sourcesJarFile + args.isSkipResources = true + args.isShowInconsistentCode = true + args.isRespectBytecodeAccModifiers = true + args.isFsCaseSensitive = true + args.isGenerateKotlinMetadata = false + args.isDebugInfo = false + args.isInlineAnonymousClasses = false + args.isInlineMethods = false + args.isReplaceConsts = false + + args.codeCache = NoOpCodeCache() + args.codeWriterProvider = Function { SimpleCodeWriter(it) } + + JadxDecompiler(args).use { decompiler -> + decompiler.registerPlugin(DexInputPlugin()) + decompiler.load() + decompiler.save() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lagradost/gradle/tasks/Tasks.kt b/src/main/kotlin/com/lagradost/gradle/tasks/Tasks.kt new file mode 100644 index 0000000..5e6260d --- /dev/null +++ b/src/main/kotlin/com/lagradost/gradle/tasks/Tasks.kt @@ -0,0 +1,67 @@ +package com.lagradost.gradle.tasks + +import com.cloudstream.gradle.getCloudstream +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.tasks.ProcessLibraryManifest +import groovy.json.JsonBuilder +import org.gradle.api.Project +import org.gradle.api.tasks.AbstractCopyTask +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.compile.AbstractCompile + +const val TASK_GROUP = "cloudstream" + +fun registerTasks(project: Project) { + val extension = project.extensions.getCloudstream() + val intermediates = project.buildDir.resolve("intermediates") + + project.tasks.register("genSources", GenSourcesTask::class.java) { + it.group = TASK_GROUP + } + + val pluginClassFile = intermediates.resolve("pluginClass") + + val compileDex = project.tasks.register("compileDex", CompileDexTask::class.java) { + it.group = TASK_GROUP + + it.pluginClassFile.set(pluginClassFile) + + for (name in arrayOf("compileDebugJavaWithJavac", "compileDebugKotlin")) { + val task = project.tasks.findByName(name) as AbstractCompile? + if (task != null) { + it.dependsOn(task) + it.input.from(task.destinationDirectory) + } + } + + it.outputFile.set(intermediates.resolve("classes.dex")) + } + + project.afterEvaluate { + project.tasks.register("make", Copy::class.java) + { + val compileDexTask = compileDex.get() + it.dependsOn(compileDexTask) + + it.doFirst { + require(project.version != "unspecified") { + "No version is set" + } + + if (extension.pluginClassName == null) { + if (pluginClassFile.exists()) { + extension.pluginClassName = pluginClassFile.readText() + } + } + + require(extension.pluginClassName != null) { + "No plugin class found, make sure your plugin class is annotated with @CloudstreamPlugin" + } + } + + it.from(compileDexTask.outputFile) + it.into(project.buildDir) + it.rename { return@rename project.name } + } + } +} \ No newline at end of file