From 611d3c349167fc291da13c828610e48ae0bc608e Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Thu, 9 Nov 2023 01:15:05 +0530 Subject: [PATCH] build: use Gradle Plugin and tasks to generate init script and copy tooling API to assets --- app/build.gradle.kts | 69 +------------ build-logic/ide/build.gradle.kts | 3 + .../plugins/AndroidIDEAssetsPlugin.kt | 83 ++++++++++++++++ .../plugins/tasks/AddFileToAssetsTask.kt | 98 +++++++++++++++++++ .../plugins/tasks/GenerateInitScriptTask.kt | 81 +++++++++++++++ subprojects/tooling-api-impl/build.gradle.kts | 16 +-- 6 files changed, 278 insertions(+), 72 deletions(-) create mode 100644 build-logic/ide/src/main/java/com/itsaky/androidide/plugins/AndroidIDEAssetsPlugin.kt create mode 100644 build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/AddFileToAssetsTask.kt create mode 100644 build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/GenerateInitScriptTask.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 28336c619..b20798909 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,9 +1,7 @@ @file:Suppress("UnstableApiUsage") -import androidx.navigation.safe.args.generator.ext.capitalize -import com.android.build.gradle.BaseExtension +import com.itsaky.androidide.plugins.AndroidIDEAssetsPlugin import java.util.Base64 -import java.util.Locale plugins { id("com.android.application") @@ -14,6 +12,10 @@ plugins { id("androidx.navigation.safeargs.kotlin") } +apply { + plugin(AndroidIDEAssetsPlugin::class.java) +} + android { namespace = BuildConfig.packageName @@ -209,64 +211,3 @@ fun getEnvOrProp(key: String): String? { } return value } - -tasks.create("generateInitScript") { - val out = file("src/main/assets/data/common/androidide.init.gradle") - - if (out.exists()) { - out.delete() - } - - doLast { - out.bufferedWriter().use { - - it.write( - """ - initscript { - repositories { - - // Always specify the snapshots repository first - maven { - // Add snapshots repository for AndroidIDE CI builds - url "${VersionUtils.SNAPSHOTS_REPO}" - } - - mavenCentral() - google() - } - - dependencies { - classpath '${BuildConfig.packageName}:gradle-plugin:$downloadVersion' - } - } - - apply plugin: com.itsaky.androidide.gradle.AndroidIDEInitScriptPlugin - """ - .trimIndent() - ) - } - } -} - -afterEvaluate { - extensions.getByType().apply { - val flavorNames = productFlavors.map { it.name.capitalize(Locale.getDefault()) } - val dependents = - listOf( - "merge@@DebugAssets", - "merge@@ReleaseAssets", - "lintAnalyze@@Debug", - "lintAnalyze@@Release", - "lintVitalAnalyze@@Release" - ) - - for (dependent in dependents) { - for (flavorName in flavorNames) { - tasks.getByName(dependent.replace("@@", flavorName)).apply { - dependsOn(":subprojects:tooling-api-impl:copyJarToAssets") - dependsOn("generateInitScript") - } - } - } - } -} diff --git a/build-logic/ide/build.gradle.kts b/build-logic/ide/build.gradle.kts index 5949e57ed..c0703db0b 100644 --- a/build-logic/ide/build.gradle.kts +++ b/build-logic/ide/build.gradle.kts @@ -38,6 +38,9 @@ tasks.withType { dependencies { implementation(gradleApi()) + + implementation("com.android.tools.build:gradle:${libs.versions.agp.asProvider().get()}") + implementation(libs.common.jkotlin) implementation(libs.common.antlr4) implementation(libs.google.java.format) diff --git a/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/AndroidIDEAssetsPlugin.kt b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/AndroidIDEAssetsPlugin.kt new file mode 100644 index 000000000..5a0c126f0 --- /dev/null +++ b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/AndroidIDEAssetsPlugin.kt @@ -0,0 +1,83 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.plugins + +import BuildConfig +import VersionUtils +import com.android.build.api.variant.AndroidComponentsExtension +import com.itsaky.androidide.plugins.tasks.AddFileToAssetsTask +import com.itsaky.androidide.plugins.tasks.GenerateInitScriptTask +import downloadVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.configurationcache.extensions.capitalized + +/** + * Handles asset copying and generation. + * + * @author Akash Yadav + */ +class AndroidIDEAssetsPlugin : Plugin { + + override fun apply(target: Project) { + target.run { + extensions.getByType(AndroidComponentsExtension::class.java).apply { + onVariants { variant -> + + val variantNameCapitalized = variant.name.capitalized() + + // Init script generator + val generateInitScript = tasks.register( + "generate${variantNameCapitalized}InitScript", + GenerateInitScriptTask::class.java + ) { + mavenGroupId.set(BuildConfig.packageName) + snapshotsRepository.set(VersionUtils.SNAPSHOTS_REPO) + downloadVersion.set(this@run.downloadVersion) + } + + variant.sources.assets?.addGeneratedSourceDirectory( + generateInitScript, + GenerateInitScriptTask::outputDir + ) + + // Tooling API JAR copier + val copyToolingApiJar = tasks.register( + "copy${variantNameCapitalized}ToolingApiJar", + AddFileToAssetsTask::class.java + ) { + val toolingApi = rootProject.findProject(":subprojects:tooling-api-impl")!! + dependsOn(toolingApi.tasks.getByName("copyJar")) + + val toolingApiJar = toolingApi.layout.buildDirectory.file( + "libs/tooling-api-all.jar") + + inputFile.set(toolingApiJar) + baseAssetsPath.set("data/common") + } + + variant.sources.assets?.addGeneratedSourceDirectory( + copyToolingApiJar, + AddFileToAssetsTask::outputDirectory + ) + } + } + } + } +} + diff --git a/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/AddFileToAssetsTask.kt b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/AddFileToAssetsTask.kt new file mode 100644 index 000000000..b1b5f2a97 --- /dev/null +++ b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/AddFileToAssetsTask.kt @@ -0,0 +1,98 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.plugins.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +/** + * Adds the provided file to assets. + * + * @author Akash Yadav + */ +abstract class AddFileToAssetsTask : DefaultTask() { + + /** + * The input file that should be copied to the assets directory. + */ + @get:InputFile + abstract val inputFile: RegularFileProperty + + /** + * The base assets path. The file will be saved in assets to `base-path/file-name`. + */ + @get:Input + abstract val baseAssetsPath: Property + + /** + * The file name of the file in assets. The file will be saved in assets to `base-path/file-name`. + */ + @get:Input + @get:Optional + abstract val fileName: Property + + /** + * The output assets directory. This should not be set manually, but provided to Android Gradle Plugin. + */ + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun copy() { + var basePath = baseAssetsPath.get() + if (basePath.isBlank()) { + basePath = "data" + } + + while (basePath.endsWith('/')) { + basePath = basePath.removeSuffix("/") + } + + val inFile = inputFile.get().asFile + + if (!inFile.exists()) { + throw IllegalArgumentException("File '$inFile' does not exist") + } + + if (!inFile.isFile) { + throw IllegalArgumentException("File '$inFile' is not a file") + } + + var fileName = fileName.getOrElse("") + if (fileName.isBlank()) { + fileName = inFile.name + } + + val outFile = outputDirectory.file("$basePath/$fileName").get().asFile.also { + it.parentFile.mkdirs() + } + + inFile.inputStream().buffered().use { input -> + outFile.outputStream().buffered().use { output -> + input.transferTo(output) + } + } + } +} \ No newline at end of file diff --git a/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/GenerateInitScriptTask.kt b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/GenerateInitScriptTask.kt new file mode 100644 index 000000000..ed01fe8a1 --- /dev/null +++ b/build-logic/ide/src/main/java/com/itsaky/androidide/plugins/tasks/GenerateInitScriptTask.kt @@ -0,0 +1,81 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.plugins.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +/** + * Generates the Gradle init script for AndroidIDE. + */ +abstract class GenerateInitScriptTask : DefaultTask() { + + @get:Input + abstract val downloadVersion: Property + + @get:Input + abstract val snapshotsRepository: Property + + @get:Input + abstract val mavenGroupId: Property + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @TaskAction + fun generate() { + + val outFile = this.outputDir.file("data/common/androidide.init.gradle") + .also { + it.get().asFile.parentFile.mkdirs() + } + + outFile.get().asFile.bufferedWriter().use { + + it.write( + """ + initscript { + repositories { + + // Always specify the snapshots repository first + maven { + // Add snapshots repository for AndroidIDE CI builds + url "${snapshotsRepository.get()}" + } + + mavenCentral() + google() + } + + dependencies { + classpath '${mavenGroupId.get()}:gradle-plugin:${downloadVersion.get()}' + } + } + + apply plugin: com.itsaky.androidide.gradle.AndroidIDEInitScriptPlugin + """ + .trimIndent() + ) + } + } + +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/build.gradle.kts b/subprojects/tooling-api-impl/build.gradle.kts index 6871c630c..e3e3f529c 100644 --- a/subprojects/tooling-api-impl/build.gradle.kts +++ b/subprojects/tooling-api-impl/build.gradle.kts @@ -26,17 +26,15 @@ tasks.withType { manifest { attributes("Main-Class" to "${BuildConfig.packageName}.tooling.impl.Main") } } -tasks.register("copyJarToAssets") { - from(project.file("${project.buildDir}/libs/tooling-api-all.jar")) - into(project.rootProject.file("app/src/main/assets/data/common/")) +tasks.register("deleteExistingJarFiles") { + delete { + delete(project.layout.buildDirectory.dir("libs")) + } } -tasks.register("deleteExistingJarFiles") { delete { delete(project.buildDir.resolve("libs")) } } - tasks.register("copyJar") { - finalizedBy("copyJarToAssets") doLast { - val libsDir = project.buildDir.resolve("libs") + val libsDir = project.layout.buildDirectory.dir("libs") copy { from(libsDir) @@ -52,7 +50,9 @@ project.tasks.getByName("jar") { finalizedBy("shadowJar") } -project.tasks.getByName("shadowJar") { finalizedBy("copyJar") } +project.tasks.getByName("shadowJar") { + finalizedBy("copyJar") +} dependencies { api(projects.subprojects.toolingApi)