* Add new gradle build logic

* rework ko.wolfdotcom

* rework all other extensions

(cherry picked from commit 153fbece55832e8a76f32e26199bb6f8f4252fcb)

---------

Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
(cherry picked from commit b030eeff0bc2e68fff2f070a86c851d950f82145)
(cherry picked from commit d7ba3e81a5a0af9c91d96bee014592e991c4efd9)
This commit is contained in:
FourTOne5
2026-05-16 02:48:02 +06:00
committed by Cuong-Tran
parent f3738c7486
commit ce1d6466bf
371 changed files with 1099 additions and 757 deletions

View File

@@ -17,7 +17,7 @@ MULTISRC_LIB_REGEX = re.compile(r"^lib-multisrc/(?P<multisrc>\w+)")
LIB_REGEX = re.compile(r"^lib/(?P<lib>\w+)")
MODULE_REGEX = re.compile(r"^:src:(?P<lang>\w+):(?P<extension>\w+)$")
CORE_FILES_REGEX = re.compile(
r"^(buildSrc/|core/|gradle/|build\.gradle\.kts|common\.gradle|gradle\.properties|settings\.gradle\.kts|.github/scripts)"
r"^(common/|core/|gradle/|build\.gradle\.kts|common\.gradle|gradle\.properties|settings\.gradle\.kts|.github/scripts)"
)
def run_command(command: str) -> str:

View File

@@ -1,18 +1,22 @@
allprojects {
repositories {
mavenCentral()
google()
maven(url = "https://jitpack.io")
buildscript {
dependencies {
classpath(libs.kotlin.gradle)
}
}
buildscript {
repositories {
mavenCentral()
google()
maven(url = "https://jitpack.io")
}
dependencies {
classpath(libs.gradle.kotlin)
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(kei.plugins.spotless)
}
val buildLogic: IncludedBuild = gradle.includedBuild("build-logic")
tasks {
listOf("clean", "spotlessApply", "spotlessCheck").forEach { task ->
named(task) {
dependsOn(buildLogic.task(":$task"))
}
}
}

View File

@@ -1,16 +0,0 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
google()
}
dependencies {
implementation(libs.gradle.agp)
implementation(libs.gradle.kotlin)
implementation(libs.gradle.serialization)
implementation(libs.spotless.gradle)
}

View File

@@ -1,7 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,7 +0,0 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

View File

@@ -1,5 +0,0 @@
object AndroidConfig {
const val compileSdk = 34
const val minSdk = 21
const val targetSdk = 34
}

View File

@@ -1,25 +0,0 @@
import com.diffplug.spotless.FormatterFunc
import com.diffplug.spotless.FormatterStep
import java.io.Serializable
object RandomUaCheck {
fun create(): FormatterStep = FormatterStep.create(
"randomua-requires-getMangaUrl",
State(),
State::toFormatter,
)
private class State : Serializable {
fun toFormatter() = FormatterFunc { content ->
if ("package keiyoushi.lib.randomua" !in content &&
"keiyoushi.lib.randomua" in content &&
"override fun getMangaUrl(" !in content
) {
throw AssertionError(
"usage of :lib:randomua requires override of getMangaUrl()",
)
}
content
}
}
}

View File

@@ -1,46 +0,0 @@
plugins {
id("com.diffplug.spotless")
}
spotless {
kotlin {
target("**/*.kt", "**/*.kts")
targetExclude("**/build/**/*.kt")
ktlint()
.editorConfigOverride(mapOf(
"max_line_length" to 2147483647,
))
trimTrailingWhitespace()
endWithNewline()
addStep(RandomUaCheck.create())
}
java {
target("**/*.java")
targetExclude("**/build/**/*.java")
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
format("gradle") {
target("**/*.gradle")
trimTrailingWhitespace()
endWithNewline()
}
format("xml") {
target("**/*.xml")
targetExclude("**/build/**/*.xml")
trimTrailingWhitespace()
endWithNewline()
}
}
tasks {
val spotlessTask = if (providers.environmentVariable("CI").orNull != "true") "spotlessApply" else "spotlessCheck"
named("preBuild") {
dependsOn(tasks.getByName(spotlessTask))
}
}

View File

@@ -1,45 +0,0 @@
plugins {
id("com.android.library")
id("kotlinx-serialization")
id("keiyoushi.lint")
}
android {
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
val proguardFile = file("proguard-rules.pro")
if (proguardFile.exists()) {
consumerProguardFiles(proguardFile)
}
}
namespace = "aniyomi.lib.${project.name}"
sourceSets {
named("main") {
java.directories.clear()
java.directories.add("src")
kotlin.directories.clear()
kotlin.directories.add("src")
assets.directories.clear()
assets.directories.add("assets")
}
}
androidResources.enable = false
}
kotlin {
compilerOptions {
freeCompilerArgs.add("-opt-in=kotlinx.serialization.ExperimentalSerializationApi")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
dependencies {
compileOnly(versionCatalogs.named("libs").findBundle("common").get())
implementation(project(":core"))
}

View File

@@ -1,46 +0,0 @@
plugins {
id("com.android.library")
id("kotlinx-serialization")
id("keiyoushi.lint")
}
android {
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
val proguardFile = file("proguard-rules.pro")
if (proguardFile.exists()) {
consumerProguardFiles(proguardFile)
}
}
namespace = "eu.kanade.tachiyomi.multisrc.${project.name}"
sourceSets {
named("main") {
manifest.srcFile("AndroidManifest.xml")
java.directories.clear()
java.directories.add("src")
kotlin.directories.clear()
kotlin.directories.add("src")
res.directories.clear()
res.directories.add("res")
assets.directories.clear()
assets.directories.add("assets")
}
}
}
kotlin {
compilerOptions {
freeCompilerArgs.add("-opt-in=kotlinx.serialization.ExperimentalSerializationApi")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
dependencies {
compileOnly(versionCatalogs.named("libs").findBundle("common").get())
implementation(project(":core"))
}

View File

@@ -1,171 +0,0 @@
import com.android.build.gradle.tasks.PackageAndroidArtifact
apply plugin: 'com.android.application'
apply plugin: 'kotlinx-serialization'
apply plugin: 'keiyoushi.lint'
assert !ext.has("pkgNameSuffix")
assert !ext.has("libVersion")
assert extName.chars().max().asInt < 0x180 : "Extension name should be romanized"
// Load local.properties for local development
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withInputStream { localProperties.load(it) }
}
Project theme = ext.has("themePkg") ? project(":lib-multisrc:$themePkg") : null
if (theme != null) evaluationDependsOn(theme.path)
android {
compileSdk = AndroidConfig.compileSdk
namespace = "eu.kanade.tachiyomi.animeextension"
sourceSets {
main {
manifest.srcFile layout.buildDirectory.file('tempAndroidManifest.xml')
java.directories.clear()
java.directories.add("src")
kotlin.directories.clear()
kotlin.directories.add("src")
res.directories.clear()
res.directories.add("res")
assets.directories.clear()
assets.directories.add("assets")
}
}
defaultConfig {
minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk
applicationIdSuffix = project.parent.name + "." + project.name
versionCode = theme == null ? extVersionCode : theme.baseVersionCode + overrideVersionCode
versionName = "14.$versionCode"
base {
archivesName = "aniyomi-$applicationIdSuffix-v$versionName"
}
assert extClass.startsWith(".")
manifestPlaceholders = [
appName : "Aniyomi: $extName",
extClass: extClass,
nsfw : project.ext.find("isNsfw") ? 1 : 0,
]
String baseUrl = project.ext.find("baseUrl") ?: ""
if (theme != null && !baseUrl.isEmpty()) {
def split = baseUrl.split("://")
assert split.length == 2
def path = split[1].split("/")
manifestPlaceholders += [
SOURCEHOST : path[0],
SOURCESCHEME: split[0],
]
}
}
lintOptions {
checkReleaseBuilds false
}
signingConfigs {
release {
storeFile = rootProject.file("signingkey.jks")
storePassword = providers.environmentVariable("KEY_STORE_PASSWORD").orNull
keyAlias = providers.environmentVariable("ALIAS").orNull
keyPassword = providers.environmentVariable("KEY_PASSWORD").orNull
}
}
buildTypes {
release {
signingConfig = rootProject.file("signingkey.jks").exists()
? signingConfigs.release
: signingConfigs.debug
minifyEnabled = true
proguardFiles(
rootProject.file("proguard-rules.pro"),
layout.buildDirectory.file("generated-proguard-rules.pro").get().asFile
)
File proguardFile = file("proguard-rules.pro")
if (proguardFile.exists()) {
proguardFiles(proguardFile)
}
vcsInfo.include = false
}
}
dependenciesInfo {
includeInApk = false
}
buildFeatures {
buildConfig = true
}
buildTypes {
configureEach {
buildConfigField "String", "MEGACLOUD_API", "\"${System.getenv("MEGACLOUD_API") ?: localProperties.getProperty("MEGACLOUD_API", "")}\""
buildConfigField "String", "KISSKH_API", "\"${System.getenv("KISSKH_API") ?: localProperties.getProperty("KISSKH_API", "")}\""
buildConfigField "String", "KISSKH_SUB_API", "\"${System.getenv("KISSKH_SUB_API") ?: localProperties.getProperty("KISSKH_SUB_API", "")}\""
buildConfigField "String", "KAISVA", "\"${System.getenv("KAISVA") ?: localProperties.getProperty("KAISVA", "")}\""
buildConfigField "String", "TMDB_API", "\"${System.getenv("TMDB_API") ?: localProperties.getProperty("TMDB_API", "")}\""
}
}
packaging {
resources.excludes.add("kotlin-tooling-metadata.json")
}
}
kotlin {
compilerOptions {
freeCompilerArgs.add("-opt-in=kotlinx.serialization.ExperimentalSerializationApi")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
dependencies {
if (theme != null) implementation(theme) // Overrides core launcher icons
implementation(project(":core"))
compileOnly(libs.bundles.common)
}
tasks.register("copyManifestFile", Copy) {
from 'AndroidManifest.xml'
rename { 'tempAndroidManifest.xml' }
into layout.buildDirectory
}
tasks.register("writeManifestFile") {
dependsOn(copyManifestFile)
File tempFile = android.sourceSets.getByName('main').manifest.srcFile
doLast {
if (!tempFile.exists()) {
tempFile.parentFile.mkdirs()
tempFile.write('<?xml version="1.0" encoding="utf-8"?>\n<manifest />\n')
}
}
}
tasks.register("writeProguardFile") {
File proguardFile = layout.buildDirectory.file("generated-proguard-rules.pro").get().asFile
String extClassPath = "${android.namespace}.${android.defaultConfig.applicationIdSuffix}${extClass}"
doLast {
proguardFile.parentFile.mkdirs()
proguardFile.withWriter {
it.writeLine("-keep class $extClassPath { <init>(); }")
}
}
}
afterEvaluate {
tasks.withType(PackageAndroidArtifact).configureEach {
// need to be in afterEvaluate to overwrite default value
createdBy = ""
// https://stackoverflow.com/a/77745844
doFirst { appMetadata.asFile.getOrNull()?.write('') }
}
}
preBuild.dependsOn(writeManifestFile, writeProguardFile)

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@@ -1,21 +1,15 @@
plugins {
id("com.android.library")
id("kotlinx-serialization")
id("keiyoushi.lint")
alias(libs.plugins.android.library)
alias(kei.plugins.android.base)
alias(kei.plugins.spotless)
}
android {
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
}
namespace = "keiyoushi.core"
buildFeatures {
resValues = false
shaders = false
}
testOptions {
@@ -23,16 +17,9 @@ android {
}
}
kotlin {
compilerOptions {
freeCompilerArgs.add("-opt-in=kotlinx.serialization.ExperimentalSerializationApi")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
dependencies {
compileOnly(versionCatalogs.named("libs").findBundle("common").get())
compileOnly(libs.bundles.common)
testImplementation(versionCatalogs.named("libs").findBundle("common").get())
testImplementation("junit:junit:4.13.2")
testImplementation(libs.bundles.common)
testImplementation(libs.junit)
}

View File

@@ -1,6 +1,7 @@
kotlin.stdlib.default.dependency=false
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx6144m
org.gradle.parallel=true
kotlin.stdlib.default.dependency=false

View File

@@ -0,0 +1,65 @@
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.samWithReceiver)
alias(libs.plugins.spotless)
`java-gradle-plugin`
}
// Configuration should be synced with [/gradle/build-logic/src/main/kotlin/PluginSpotless.kt]
val ktlintVersion = libs.ktlint.bom.get().version
val editorConfigFile = rootProject.file("../../.editorconfig")
spotless {
kotlin {
target("src/**/*.kt", "*.kts")
ktlint(ktlintVersion)
.setEditorConfigPath(editorConfigFile)
.editorConfigOverride(
mapOf(
"max_line_length" to 2147483647,
),
)
trimTrailingWhitespace()
endWithNewline()
}
}
dependencies {
compileOnly(gradleKotlinDsl())
compileOnly(libs.android.gradle)
compileOnly(libs.kotlin.gradle)
implementation(libs.spotless.gradle)
implementation(libs.tapmoc.gradle)
// These allow us to reference the dependency catalog inside our compiled plugins
compileOnly(files(libs::class.java.superclass.protectionDomain.codeSource.location))
compileOnly(files(kei::class.java.superclass.protectionDomain.codeSource.location))
}
samWithReceiver {
annotation("org.gradle.api.HasImplicitReceiver")
}
gradlePlugin {
plugins {
register("android-base") {
id = kei.plugins.android.base.get().pluginId
implementationClass = "PluginAndroidBase"
}
register("extension") {
id = kei.plugins.extension.legacy.get().pluginId
implementationClass = "PluginExtensionLegacy"
}
register("library") {
id = kei.plugins.library.get().pluginId
implementationClass = "PluginLibrary"
}
register("multisrc") {
id = kei.plugins.multisrc.get().pluginId
implementationClass = "PluginMultiSrc"
}
register("spotless") {
id = kei.plugins.spotless.get().pluginId
implementationClass = "PluginSpotless"
}
}
}

View File

@@ -0,0 +1,9 @@
# Configuration should be synced with [/gradle.properties]
#
# Gradle properties are not passed to included builds
# https://github.com/gradle/gradle/issues/2534
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx6144m
org.gradle.parallel=true

View File

@@ -0,0 +1,18 @@
dependencyResolutionManagement {
@Suppress("UnstableApiUsage")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
from(files("../libs.versions.toml"))
}
create("kei") {
from(files("../kei.versions.toml"))
}
}
}
rootProject.name = "build-logic"

View File

@@ -0,0 +1,46 @@
import com.android.build.api.dsl.ApplicationDefaultConfig
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.DefaultConfig
import com.android.build.api.dsl.LibraryDefaultConfig
import keiyoushi.gradle.configurations.configureKotlin
import keiyoushi.gradle.extensions.kei
import keiyoushi.gradle.extensions.spotlessTaskName
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
@Suppress("UNUSED")
class PluginAndroidBase : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
configureKotlin()
android {
compileSdk = kei.versions.android.sdk.compile.get().toInt()
defaultConfig {
minSdk = kei.versions.android.sdk.min.get().toInt()
if (this is ApplicationDefaultConfig) {
targetSdk = kei.versions.android.sdk.target.get().toInt()
}
val proguardFile = file("proguard-rules.pro")
if (proguardFile.exists()) {
when (this) {
is ApplicationDefaultConfig -> proguardFile(proguardFile)
is LibraryDefaultConfig -> consumerProguardFiles(proguardFile)
}
}
}
}
tasks.getByName("preBuild").dependsOn(spotlessTaskName())
}
}
private fun Project.android(block: CommonExtension.() -> Unit) {
extensions.configure(block)
}
private fun CommonExtension.defaultConfig(block: DefaultConfig.() -> Unit) {
defaultConfig.apply(block)
}

View File

@@ -0,0 +1,190 @@
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import keiyoushi.gradle.extensions.alias
import keiyoushi.gradle.extensions.baseVersionCode
import keiyoushi.gradle.extensions.compileOnly
import keiyoushi.gradle.extensions.implementation
import keiyoushi.gradle.extensions.kei
import keiyoushi.gradle.extensions.libs
import keiyoushi.gradle.extensions.plugins
import keiyoushi.gradle.tasks.GenerateKeepRulesTask
import keiyoushi.gradle.utils.assertWithoutFlag
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePluginExtension
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.register
@Suppress("UNUSED")
class PluginExtensionLegacy : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.serialization)
alias(kei.plugins.android.base)
alias(kei.plugins.spotless)
}
assertWithoutFlag(!extra.has("pkgNameSuffix")) { "Gradle configuration cannot contain 'pkgNameSuffix'" }
assertWithoutFlag(!extra.has("libVersion")) { "Gradle configuration cannot contain 'libVersion'" }
assertWithoutFlag(extName.max().code < 0x180) { "Extension name should be romanized" }
val theme: Project? = if (extra.has("themePkg")) project(":lib-multisrc:$themePkg") else null
if (theme != null) evaluationDependsOn(theme.path)
android {
namespace = "eu.kanade.tachiyomi.animeextension"
sourceSets {
named("main") {
manifest.srcFile(rootProject.file("common/AndroidManifest.xml"))
java.directories.clear()
java.directories.add("src")
kotlin.directories.clear()
kotlin.directories.add("src")
res.directories.clear()
res.directories.add("res")
assets.directories.clear()
assets.directories.add("assets")
}
}
defaultConfig {
applicationIdSuffix = project.parent?.name + "." + project.name
versionCode = if (theme == null) extVersionCode else theme.baseVersionCode + overrideVersionCode
versionName = "14.$versionCode"
base {
archivesName.set("aniyomi-$applicationIdSuffix-v$versionName")
}
assertWithoutFlag(extClass.startsWith(".")) { "'extClass' must start with '.'" }
manifestPlaceholders += mapOf(
"appName" to "Aniyomi: $extName",
"extClass" to extClass,
"nsfw" to if (isNsfw) 1 else 0,
)
if (theme != null && baseUrl.isNotEmpty()) {
val split = baseUrl.split("://")
assertWithoutFlag(split.size == 2) { "'baseUrl' must be in the format of 'https://example.com'" }
val path = split[1].split("/")
manifestPlaceholders += mapOf(
"SOURCEHOST" to path[0],
"SOURCESCHEME" to split[0],
)
}
}
lint {
checkReleaseBuilds = false
}
signingConfigs {
create("release") {
storeFile = rootProject.file("signingkey.jks")
storePassword = providers.environmentVariable("KEY_STORE_PASSWORD").orNull
keyAlias = providers.environmentVariable("ALIAS").orNull
keyPassword = providers.environmentVariable("KEY_PASSWORD").orNull
}
}
buildTypes {
named("release") {
signingConfig = if (rootProject.file("signingkey.jks").exists()) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
isMinifyEnabled = true
proguardFiles(rootProject.file("common/proguard-rules.pro"))
@Suppress("UnstableApiUsage")
vcsInfo.include = false
}
}
dependenciesInfo {
includeInApk = false
}
buildFeatures {
buildConfig = true
}
buildTypes {
configureEach {
buildConfigField("String", "MEGACLOUD_API", "\"https://script.google.com/macros/s/AKfycbxHbYHbrGMXYD2-bC-C43D3njIbU-wGiYQuJL61H4vyy6YVXkybMNNEPJNPPuZrD1gRVA/exec\"")
buildConfigField("String", "KISSKH_API", "\"https://script.google.com/macros/s/AKfycbzn8B31PuDxzaMa9_CQ0VGEDasFqfzI5bXvjaIZH4DM8DNq9q6xj1ALvZNz_JT3jF0suA/exec?id=\"")
buildConfigField("String", "KISSKH_SUB_API", "\"https://script.google.com/macros/s/AKfycbyq6hTj0ZhlinYC6xbggtgo166tp6XaDKBCGtnYk8uOfYBUFwwxBui0sGXiu_zIFmA/exec?id=\"")
buildConfigField("String", "KAISVA", "\"https://c-kai-8090.amarullz.com\"")
buildConfigField("String", "TMDB_API", "\"${System.getenv("TMDB_API")}\"")
}
}
packaging {
resources.excludes.add("kotlin-tooling-metadata.json")
}
}
androidComponents {
onVariants { variant ->
val variantName = variant.name.replaceFirstChar { it.uppercase() }
@Suppress("UnstableApiUsage")
val keepRules = variant.sources.keepRules
if (keepRules != null) {
val task = tasks.register<GenerateKeepRulesTask>("generate${variantName}KeepRules") {
this.applicationId.set(variant.applicationId)
this.extClass.set(this@with.extClass)
}
keepRules.addGeneratedSourceDirectory(task) { it.outputDir }
}
variant.sources.manifests.addStaticManifestFile("AndroidManifest.xml")
}
}
dependencies {
if (theme != null) implementation(theme) // Overrides core launcher icons
implementation(project(":core"))
compileOnly(libs.bundles.common)
}
}
}
private fun Project.android(block: ApplicationExtension.() -> Unit) {
extensions.configure(block)
}
private fun Project.androidComponents(block: ApplicationAndroidComponentsExtension.() -> Unit) {
extensions.configure(block)
}
private fun Project.base(block: BasePluginExtension.() -> Unit) {
extensions.configure(block)
}
private val Project.extName: String
get() = extra.get("extName") as String
private val Project.extVersionCode: Int
get() = extra.get("extVersionCode") as Int
private val Project.extClass: String
get() = extra.get("extClass") as String
private val Project.isNsfw: Boolean
get() = extra.getOrNull("isNsfw") == true
private val Project.baseUrl: String
get() = (extra.getOrNull("baseUrl") as String?).orEmpty()
private val Project.overrideVersionCode: Int
get() = extra.get("overrideVersionCode") as Int
private val Project.themePkg: String
get() = extra.get("themePkg") as String
private fun ExtraPropertiesExtension.getOrNull(name: String) = if (has(name)) get(name) else null

View File

@@ -0,0 +1,50 @@
import com.android.build.api.dsl.LibraryExtension
import keiyoushi.gradle.extensions.alias
import keiyoushi.gradle.extensions.compileOnly
import keiyoushi.gradle.extensions.implementation
import keiyoushi.gradle.extensions.kei
import keiyoushi.gradle.extensions.libs
import keiyoushi.gradle.extensions.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
@Suppress("UNUSED")
class PluginLibrary : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.serialization)
alias(kei.plugins.android.base)
alias(kei.plugins.spotless)
}
android {
namespace = "aniyomi.lib.${project.name}"
sourceSets {
named("main") {
java.directories.clear()
java.directories.add("src")
kotlin.directories.clear()
kotlin.directories.add("src")
assets.directories.clear()
assets.directories.add("assets")
}
}
androidResources.enable = false
}
dependencies {
compileOnly(libs.bundles.common)
implementation(project(":core"))
}
}
}
private fun Project.android(block: LibraryExtension.() -> Unit) {
extensions.configure(block)
}

View File

@@ -0,0 +1,49 @@
import com.android.build.api.dsl.LibraryExtension
import keiyoushi.gradle.extensions.alias
import keiyoushi.gradle.extensions.compileOnly
import keiyoushi.gradle.extensions.implementation
import keiyoushi.gradle.extensions.kei
import keiyoushi.gradle.extensions.libs
import keiyoushi.gradle.extensions.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
@Suppress("UNUSED")
class PluginMultiSrc : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.serialization)
alias(kei.plugins.android.base)
alias(kei.plugins.spotless)
}
android {
namespace = "eu.kanade.tachiyomi.multisrc.${project.name}"
sourceSets {
named("main") {
manifest.srcFile("AndroidManifest.xml")
kotlin.directories.clear()
kotlin.directories.add("src")
res.directories.clear()
res.directories.add("res")
assets.directories.clear()
assets.directories.add("assets")
}
}
}
dependencies {
compileOnly(libs.bundles.common)
implementation(project(":core"))
}
}
}
private fun Project.android(block: LibraryExtension.() -> Unit) {
extensions.configure(block)
}

View File

@@ -0,0 +1,83 @@
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.spotless.FormatterFunc
import com.diffplug.spotless.FormatterStep
import keiyoushi.gradle.extensions.alias
import keiyoushi.gradle.extensions.libs
import keiyoushi.gradle.extensions.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.invoke
import java.io.Serializable
@Suppress("UNUSED")
class PluginSpotless : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
plugins {
alias(libs.plugins.spotless)
}
// Configuration should be synced with [/gradle/build-logic/build.gradle.kts]
val ktlintVersion = libs.ktlint.bom.get().version
spotless {
kotlin {
target("src/**/*.kt", "*.kts")
ktlint(ktlintVersion)
.editorConfigOverride(
mapOf(
"max_line_length" to 2147483647,
),
)
trimTrailingWhitespace()
endWithNewline()
addStep(RandomUACheck.create())
}
java {
target("src/**/*.java")
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
format("gradle") {
target("*.gradle")
trimTrailingWhitespace()
endWithNewline()
}
format("xml") {
target("src/**/*.xml")
trimTrailingWhitespace()
endWithNewline()
}
}
}
}
private object RandomUACheck {
fun create(): FormatterStep = FormatterStep.create(
"randomua-requires-getMangaUrl",
State(),
State::toFormatter,
)
private class State : Serializable {
fun toFormatter() = FormatterFunc { content ->
if ("package keiyoushi.lib.randomua" !in content &&
"keiyoushi.lib.randomua" in content &&
"override fun getMangaUrl(" !in content
) {
throw AssertionError(
"usage of :lib:randomua requires override of getMangaUrl()",
)
}
content
}
}
}
private fun Project.spotless(block: SpotlessExtension.() -> Unit) {
extensions.configure(block)
}

View File

@@ -0,0 +1,28 @@
package keiyoushi.gradle.configurations
import keiyoushi.gradle.extensions.kei
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.dsl.HasConfigurableKotlinCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
import tapmoc.configureJavaCompatibility
fun Project.configureKotlin() {
configureJavaCompatibility(kei.versions.java.get().toInt())
kotlin {
compilerOptions {
optIn.add("kotlinx.serialization.ExperimentalSerializationApi")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
}
private fun Project.kotlin(block: KotlinBaseExtension.() -> Unit) {
extensions.configure(block)
}
private fun KotlinBaseExtension.compilerOptions(block: KotlinCommonCompilerOptions.() -> Unit) {
if (this is HasConfigurableKotlinCompilerOptions<*>) compilerOptions(block)
}

View File

@@ -0,0 +1,33 @@
package keiyoushi.gradle.extensions
import org.gradle.api.Project
import org.gradle.api.artifacts.ExternalModuleDependencyBundle
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.DependencyHandlerScope
fun DependencyHandlerScope.api(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
add("api", dependencyNotation)
}
fun DependencyHandlerScope.compileOnly(dependencyNotation: Provider<ExternalModuleDependencyBundle>) {
add("compileOnly", dependencyNotation)
}
@JvmName("implementationBundle")
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<ExternalModuleDependencyBundle>) {
add("implementation", dependencyNotation)
}
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
add("implementation", dependencyNotation)
}
fun DependencyHandlerScope.implementation(dependencyNotation: Project) {
add("implementation", dependencyNotation)
}
fun DependencyHandlerScope.implementation(dependencyNotation: ProjectDependency) {
add("implementation", dependencyNotation)
}

View File

@@ -1,6 +1,8 @@
package keiyoushi.gradle.extensions
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.extra
var ExtensionAware.baseVersionCode: Int
get() = extra.get("baseVersionCode") as Int
get() = (extra.get("baseVersionCode") as Int)
set(value) = extra.set("baseVersionCode", value)

View File

@@ -0,0 +1,9 @@
package keiyoushi.gradle.extensions
import org.gradle.api.plugins.PluginManager
import org.gradle.api.provider.Provider
import org.gradle.plugin.use.PluginDependency
fun PluginManager.alias(notation: Provider<PluginDependency>) {
apply(notation.get().pluginId)
}

View File

@@ -0,0 +1,16 @@
package keiyoushi.gradle.extensions
import org.gradle.accessors.dm.LibrariesForKei
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.api.plugins.PluginManager
import org.gradle.kotlin.dsl.the
internal val Project.libs get() = the<LibrariesForLibs>()
internal val Project.kei get() = the<LibrariesForKei>()
internal fun Project.plugins(block: PluginManager.() -> Unit) {
pluginManager.apply(block)
}
fun Project.spotlessTaskName() = if (providers.environmentVariable("CI").orNull != "true") "spotlessApply" else "spotlessCheck"

View File

@@ -0,0 +1,28 @@
package keiyoushi.gradle.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
abstract class GenerateKeepRulesTask : DefaultTask() {
@get:Input
abstract val applicationId: Property<String>
@get:Input
abstract val extClass: Property<String>
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun action() {
outputDir.get().file("extClass.keep").asFile.apply {
parentFile.mkdirs()
writeText("-keep class ${applicationId.get()}${extClass.get()} { <init>(); }\n")
}
}
}

View File

@@ -0,0 +1,11 @@
package keiyoushi.gradle.utils
/**
* Throws an [AssertionError] calculated by [lazyMessage] if the [value] is false.
*/
inline fun assertWithoutFlag(value: Boolean, lazyMessage: () -> Any) {
if (!value) {
val message = lazyMessage()
throw AssertionError(message)
}
}

13
gradle/kei.versions.toml Normal file
View File

@@ -0,0 +1,13 @@
[versions]
# Used in [/gradle/build-config/src/main/kotlin/PluginKotlinMultiplatform.kt]
android-sdk-min = "21"
android-sdk-compile = "34"
android-sdk-target = "34"
java = "11"
[plugins]
android-base = { id = "kei.plugins.android.base" }
extension-legacy = { id = "kei.plugins.extension.legacy" }
library = { id = "kei.plugins.library" }
multisrc = { id = "kei.plugins.multisrc" }
spotless = { id = "kei.plugins.spotless" }

View File

@@ -1,33 +1,47 @@
[versions]
kotlin = "2.3.0"
android-gradle = "9.2.1"
coroutines = "1.10.2"
junit = "4.13.2"
kotlin-gradle = "2.3.21"
ktlint = "1.8.0"
spotless = "8.5.0"
tapmoc = "0.4.2"
# 1.8 and beyond switched to `all-compatibility` mode
# https://github.com/Kotlin/kotlinx.serialization/blob/master/CHANGELOG.md#generate-java-8s-default-method-implementations-in-interfaces
# this causes issues for extensions as some required generated classes are not in the final apk due to the library being compile only
# more info: https://github.com/keiyoushi/extensions-source/pull/12962#issuecomment-3828458599
# noinspection NewerVersionAvailable
serialization = "1.7.3"
[libraries]
gradle-agp = { module = "com.android.tools.build:gradle", version = "9.2.1" }
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
gradle-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-gradle" }
ktlint-bom = { module = "com.pinterest.ktlint:ktlint-bom", version.ref = "ktlint" }
spotless-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
tapmoc-gradle = { module = "com.gradleup.tapmoc:tapmoc-gradle-plugin", version.ref = "tapmoc" }
spotless-gradle = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version = "8.4.0" }
aniyomi-lib = { module = "com.github.komikku-app:aniyomi-extensions-lib", version = "bdc8184127" }
kotlin-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "serialization" }
kotlin-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
kotlin-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "serialization" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
injekt-core = { module = "com.github.null2264.injekt:injekt-core", version = "4135455a2a" }
jsoup = { module = "org.jsoup:jsoup", version = "1.22.1" }
kotlin-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
kotlin-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "serialization" }
kotlin-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "serialization" }
#kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin-gradle" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.3.2" }
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
aniyomi-lib = { module = "com.github.komikku-app:aniyomi-extensions-lib", version = "bdc8184127" }
[plugins]
android-application = { id = "com.android.application", version.ref = "android-gradle" }
android-library = { id = "com.android.library", version.ref = "android-gradle" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-gradle" }
kotlin-samWithReceiver = { id = "org.jetbrains.kotlin.plugin.sam.with.receiver", version.ref = "kotlin-gradle" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-gradle" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
[bundles]
common = ["coroutines-core", "coroutines-android", "injekt-core", "kotlin-protobuf", "kotlin-json", "kotlin-json-okio", "jsoup", "okhttp", "aniyomi-lib", "quickjs"]
common = ["coroutines-core", "coroutines-android", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "kotlin-json-okio", "jsoup", "okhttp", "aniyomi-lib", "quickjs"]

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 4

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 3

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 3

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 1

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 3

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 22

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 5

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
dependencies {

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 1

View File

@@ -1,5 +1,7 @@
import keiyoushi.gradle.extensions.baseVersionCode
plugins {
id("lib-multisrc")
alias(kei.plugins.multisrc)
}
baseVersionCode = 8

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
android {

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,3 +1,3 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}

View File

@@ -1,5 +1,5 @@
plugins {
id("lib-android")
alias(kei.plugins.library)
}
dependencies {

Some files were not shown because too many files have changed in this diff Show More