353 lines
12 KiB
Groovy
353 lines
12 KiB
Groovy
import java.util.zip.CRC32
|
|
|
|
/**
|
|
* A custom Gradle plugin for AutoJs6 projects.
|
|
* Created by SuperMonster003 on May 18, 2022.
|
|
*/
|
|
|
|
apply plugin: ConfigPlugin
|
|
|
|
@SuppressWarnings(['GroovyImplicitNullArgumentCall', 'GrMethodMayBeStatic'])
|
|
class ConfigPlugin implements Plugin<Project> {
|
|
|
|
private isApp, isInrt, isDeployable
|
|
|
|
private sign = new Sign('sign.properties')
|
|
private versions = new Versions('version.properties')
|
|
|
|
@Override
|
|
void apply(Project project) {
|
|
isApp = project.name == 'app'
|
|
isInrt = project.name == 'inrt'
|
|
isDeployable = isApp || isInrt
|
|
|
|
applyPlugins(project)
|
|
applyExtensions(project)
|
|
applyDependencies(project)
|
|
applyAndroid(project)
|
|
applyCompileOptions(project)
|
|
|
|
handleVersionsIfNeeded(project)
|
|
}
|
|
|
|
private applyPlugins = {
|
|
[
|
|
'kotlin-android',
|
|
isDeployable ? 'com.android.application' : 'com.android.library',
|
|
].forEach (s) -> it.pluginManager.apply(s)
|
|
}
|
|
|
|
private applyExtensions = {
|
|
// @Hint by SuperMonster003 on May 20, 2022.
|
|
// ! Uncomment if you want to share with other projects
|
|
// it.ext { sign = sign; versions = versions }
|
|
}
|
|
|
|
private applyDependencies = {
|
|
it.dependencies {
|
|
testImplementation "junit:junit:4.13.2"
|
|
}
|
|
}
|
|
|
|
private applyAndroid = { project ->
|
|
project.android {
|
|
namespace project.namespace
|
|
|
|
compileSdkVersion versions.sdkVersionCompile
|
|
|
|
defaultConfig {
|
|
minSdkVersion versions.sdkVersionMin
|
|
targetSdkVersion versions.sdkVersionTarget
|
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility versions.javaVersion
|
|
targetCompatibility versions.javaVersion
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = versions.javaVersion
|
|
}
|
|
|
|
lintOptions {
|
|
abortOnError false
|
|
}
|
|
|
|
signingConfigs {
|
|
isApp && sign.isValid && release {
|
|
storeFile project.file(sign.properties['storeFile'])
|
|
keyPassword sign.properties['keyPassword']
|
|
keyAlias sign.properties['keyAlias']
|
|
storePassword sign.properties['storePassword']
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
minifyEnabled false
|
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
// @Hint by SuperMonster003 on May 19, 2022.
|
|
// ! Notice the difference between release and 'release'.
|
|
// !
|
|
// ! Alternative grammars:
|
|
// ! a. release in signingConfigs
|
|
// ! b. signingConfigs.contains(release)
|
|
// ! c. signingConfigs.hasProperty('release')
|
|
// !
|
|
// ! Other way with the same purpose:
|
|
// ! a. signingConfigs.properties.names.contains('release')
|
|
// ! b. 'release' in signingConfigs.properties.names
|
|
signingConfig = release in signingConfigs ? signingConfigs.release : null
|
|
}
|
|
isDeployable && debug {
|
|
minifyEnabled = release.minifyEnabled
|
|
proguardFiles = release.proguardFiles
|
|
signingConfig = release.signingConfig
|
|
}
|
|
}
|
|
|
|
isDeployable && defaultConfig {
|
|
versionCode versions.appVersionCode
|
|
versionName versions.appVersionName
|
|
multiDexEnabled true
|
|
javaCompileOptions {
|
|
annotationProcessorOptions {
|
|
arguments = [
|
|
resourcePackageName: project.applicationId,
|
|
androidManifestFile: "$project.projectDir/src/main/AndroidManifest.xml".toString()
|
|
]
|
|
}
|
|
}
|
|
buildConfigField 'String', 'VERSION_DATE', "\"${Utils.getDateString("MMM dd, yyyy", "GMT+08:00")}\""
|
|
}
|
|
|
|
isDeployable && applicationVariants.all { variant ->
|
|
variant.outputs.all { output ->
|
|
outputFileName = Utils.getOutputFileName(variant, output)
|
|
}
|
|
|
|
variant.mergeAssetsProvider.configure {
|
|
doLast {
|
|
project.delete(project.fileTree(dir: outputDir, includes: ['declarations/**']))
|
|
}
|
|
}
|
|
}
|
|
|
|
isDeployable && splits {
|
|
// Configures multiple APKs based on ABI.
|
|
abi {
|
|
// Enables building multiple APKs per ABI.
|
|
enable true
|
|
// By default all ABIs are included, so use reset() and include to specify that we only
|
|
// want APKs for x86 and x86_64.
|
|
// Resets the list of ABIs that Gradle should create APKs for to none.
|
|
reset()
|
|
// Specifies a list of ABIs that Gradle should create APKs for.
|
|
include 'x86', 'armeabi-v7a', 'arm64-v8a', 'x86_64'
|
|
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
|
|
universalApk true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private applyCompileOptions = {
|
|
it.tasks.withType(JavaCompile) {
|
|
options.encoding 'UTF-8'
|
|
|
|
// @Hint by SuperMonster003 on May 18, 2022.
|
|
// ! Comment or remove this option if you're tired of plenty of warnings. :)
|
|
// options.compilerArgs << '-Xlint:deprecation' << '-Xlint:unchecked'
|
|
}
|
|
}
|
|
|
|
private handleVersionsIfNeeded = { versions.handleIfNeeded(it) }
|
|
|
|
}
|
|
|
|
class Versions {
|
|
|
|
def sdkVersionMin, sdkVersionTarget, sdkVersionCompile, appVersionName, appVersionCode, javaVersion
|
|
def properties, file
|
|
|
|
private isBuildNumberAutoIncremented
|
|
private minBuildTimeGap = Utils.hours2Millis(0.5)
|
|
/**
|
|
* <p>@Type
|
|
* <code>
|
|
* {@link LinkedHashMap}<{@link String} projectName, {@link String} | {@link ArrayList}<{@link String}> buildType>
|
|
* </code>
|
|
* </p>
|
|
* <p>@Example
|
|
* <code><pre>
|
|
* def assembleTargets = [
|
|
* [autojs: 'release'],
|
|
* [app: ['debug', 'release']],
|
|
* [others: 'debug'],
|
|
* ]
|
|
* </pre></code>
|
|
* </p>
|
|
*/
|
|
private assembleTargets = [app: ['debug', 'release']]
|
|
|
|
Versions(filePath) {
|
|
properties = new Properties()
|
|
file = new File(filePath)
|
|
loadFile()
|
|
setVersions()
|
|
}
|
|
|
|
private loadFile = {
|
|
if (!file.canRead()) {
|
|
throw new FileNotFoundException("Can't read file version.properties")
|
|
}
|
|
properties.load(new FileInputStream(file))
|
|
}
|
|
|
|
private setVersions = {
|
|
sdkVersionMin = properties['MIN_SDK_VERSION'] as int
|
|
sdkVersionTarget = properties['TARGET_SDK_VERSION'] as int
|
|
sdkVersionCompile = properties['COMPILE_SDK_VERSION'] as int
|
|
appVersionName = properties['VERSION_NAME']
|
|
appVersionCode = properties['VERSION_BUILD'] as int
|
|
javaVersion = JavaVersion["VERSION_${properties['JAVA_VERSION']}"]
|
|
}
|
|
|
|
private isBuildGapEnough = {
|
|
if (properties['BUILD_TIME'] != null) {
|
|
new Date().time - (properties['BUILD_TIME'] as long) > minBuildTimeGap
|
|
}
|
|
}
|
|
|
|
public updateProperties = {
|
|
if (isBuildGapEnough()) {
|
|
properties['VERSION_BUILD'] = (appVersionCode + 1).toString()
|
|
isBuildNumberAutoIncremented = true
|
|
}
|
|
properties['BUILD_TIME'] = new Date().time.toString()
|
|
properties.store(file.newWriter(), null)
|
|
}
|
|
|
|
public showInfo = {
|
|
println "Version name: $appVersionName"
|
|
println "Version code: $appVersionCode${isBuildNumberAutoIncremented ? " [auto-incremented]" : ""}"
|
|
println "SDK versions: min [$sdkVersionMin] / target [$sdkVersionTarget] / compile [$sdkVersionCompile]"
|
|
println "Java version: $javaVersion"
|
|
}
|
|
|
|
public handleIfNeeded = { project ->
|
|
def hasTask = { taskGraph, name, buildType ->
|
|
taskGraph.hasTask(Utils.getAssembleFullTaskName(name, buildType))
|
|
}
|
|
def append = { buildType ->
|
|
project.tasks.named(Utils.getAssembleTaskName(buildType)) {
|
|
doLast { updateProperties(); println(); showInfo() }
|
|
}
|
|
}
|
|
|
|
assembleTargets.each { Map.Entry<String, ArrayList<String>> entry ->
|
|
def target = [name: entry.key, buildType: entry.value]
|
|
if (target.name != project.name) {
|
|
return
|
|
}
|
|
project.gradle.taskGraph.whenReady { taskGraph ->
|
|
if (target.buildType instanceof ArrayList) {
|
|
for (buildType in target.buildType) {
|
|
if (hasTask(taskGraph, target.name, buildType)) {
|
|
return append(buildType)
|
|
}
|
|
}
|
|
} else {
|
|
if (hasTask(taskGraph, target.name, target.buildType)) {
|
|
return append(target.buildType)
|
|
}
|
|
}
|
|
return showInfo()
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class Sign {
|
|
|
|
def properties, file, isValid
|
|
|
|
Sign(filePath) {
|
|
properties = new Properties()
|
|
file = new File(filePath)
|
|
if (file.exists()) {
|
|
properties.load(new FileInputStream(file))
|
|
isValid = !properties.isEmpty()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class Utils {
|
|
|
|
static FILE_EXTENSION_APK = 'apk'
|
|
|
|
static hours2Millis = { it * 3.6e6 }
|
|
|
|
static capitalize = { "${it[0].toUpperCase()}${it.substring(1)}" }
|
|
|
|
static getDateString = { format, timeZone ->
|
|
def zone = timeZone ? TimeZone.getTimeZone(timeZone) : TimeZone.getDefault()
|
|
|
|
new Date().format(format, zone) // e.g. May 23, 2011
|
|
}
|
|
|
|
static getOutputFileName = { variant, output ->
|
|
def autojs = variant.applicationId.replaceAll(/^.+\.(.+)$/, '$1') // e.g. autojs6
|
|
def version = variant.versionName.replaceAll(/\s/, '-') // e.g. 6.1.0
|
|
def architecture = output.getFilter("ABI") ?: 'universal'
|
|
def extension = FILE_EXTENSION_APK
|
|
|
|
"$autojs-v$version-$architecture.$extension".toLowerCase()
|
|
}
|
|
|
|
static getAssembleTaskName = { buildType ->
|
|
"assemble${Utils.capitalize(buildType)}"
|
|
}
|
|
|
|
static getAssembleFullTaskName = { name, buildType ->
|
|
":$name:${Utils.getAssembleTaskName(buildType)}"
|
|
}
|
|
|
|
static digestCRC32 = { File file ->
|
|
def instance = new CRC32()
|
|
|
|
def fis = new FileInputStream(file)
|
|
def buffer = new byte[4096]
|
|
def read
|
|
while ((read = fis.read(buffer)) > 0) {
|
|
instance.update(buffer, 0, read)
|
|
}
|
|
|
|
String.format("%08x", instance.getValue())
|
|
}
|
|
|
|
}
|
|
|
|
task appendDigestToReleasedFiles(type: Copy) {
|
|
def src = "release"
|
|
def dst = "${src}s"
|
|
def ext = Utils.FILE_EXTENSION_APK
|
|
|
|
from src
|
|
into dst
|
|
include "*.$ext"
|
|
rename { String name ->
|
|
String digest = Utils.digestCRC32(file("${src}/$name"))
|
|
if (digest.matches(/^0{8}$/)) {
|
|
project.logger.error "Invalid digest"
|
|
} else {
|
|
name.replaceAll "^(.+?)(\\.$ext)\$", "\$1-$digest\$2"
|
|
}
|
|
}
|
|
doLast {
|
|
println "Destination: ${file(dst)}"
|
|
}
|
|
} |