com.flyjingfish.android_aop_plugin.plugin.CompilePlugin.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of android-aop-plugin Show documentation
Show all versions of android-aop-plugin Show documentation
Lightweight Aop for Android platform, you deserve it, action is worse than your heartbeat
The newest version!
package com.flyjingfish.android_aop_plugin.plugin
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.Variant
import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.DynamicFeaturePlugin
import com.android.build.gradle.LibraryExtension
import com.flyjingfish.android_aop_plugin.config.AndroidAopConfig
import com.flyjingfish.android_aop_plugin.scanner_visitor.WovenIntoCode
import com.flyjingfish.android_aop_plugin.tasks.CompileAndroidAopTask
import com.flyjingfish.android_aop_plugin.tasks.DebugModeFileTask
import com.flyjingfish.android_aop_plugin.tasks.SyncConfigTask
import com.flyjingfish.android_aop_plugin.utils.AndroidConfig
import com.flyjingfish.android_aop_plugin.utils.ClassFileUtils
import com.flyjingfish.android_aop_plugin.utils.InitConfig
import com.flyjingfish.android_aop_plugin.utils.Utils
import com.flyjingfish.android_aop_plugin.utils.adapterOSPath
import com.flyjingfish.android_aop_plugin.utils.getRelativePath
import org.codehaus.groovy.runtime.DefaultGroovyMethods
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.configurationcache.extensions.capitalized
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool
import java.io.File
class CompilePlugin(private val root:Boolean): BasePlugin() {
companion object{
private const val ANDROID_EXTENSION_NAME = "android"
private const val DEBUG_MODE_FILE_TASK_NAME = "debugModeFile"
}
override fun apply(project: Project) {
super.apply(project)
val isApp = project.plugins.hasPlugin(AppPlugin::class.java)
val isDynamicLibrary = project.plugins.hasPlugin(DynamicFeaturePlugin::class.java)
val androidObject: Any? = project.extensions.findByName(ANDROID_EXTENSION_NAME)
if (androidObject == null) {
if (project.rootProject == project || root){
return
}
val buildTypeName = "release"
val variantName = "release"
if (hasBuildConfig()){
try {
val javaPluginExtension = project.extensions.getByType(JavaPluginExtension::class.java)
val path = Utils.aopDebugModeJavaDir4Java()
val debugModeDir = File("${project.buildDir.absolutePath}$path")
if (!debugModeDir.exists()){
debugModeDir.mkdirs()
}
// 设置新的 Java 源代码路径
javaPluginExtension.sourceSets.getByName("main").java.srcDirs("build$path")
var packageName :String ?=null
for (srcDir in javaPluginExtension.sourceSets.getByName("main").java.srcDirs) {
if (srcDir.absolutePath != debugModeDir.absolutePath){
if (srcDir.exists() && packageName == null){
//说明这个才是真正的源码所在路径
val packageFile = getPackageNameFile(srcDir,0)
val relativePath = packageFile.getRelativePath(srcDir).replace(File.separator,".")
packageName = if (relativePath.endsWith(".")){
relativePath.substring(0,relativePath.length-1)
}else{
relativePath
}
}
}
}
if (packageName != null){
project.tasks.register(DEBUG_MODE_FILE_TASK_NAME, DebugModeFileTask::class.java){
it.debugModeDir = debugModeDir.absolutePath
it.packageName = packageName
it.variantName = variantName
it.buildTypeName = buildTypeName
it.isAndroidModule = false
}
project.afterEvaluate {
project.tasks.findByName("compileJava")?.dependsOn(DEBUG_MODE_FILE_TASK_NAME)
}
}
} catch (_: Exception) {
}
}
// java项目
project.tasks.withType(JavaCompile::class.java).configureEach { compileTask ->
compileTask.doLast{
val androidObject1: Any? = project.extensions.findByName(ANDROID_EXTENSION_NAME)
if (androidObject1 != null) {
return@doLast
}
if (compileTask !is AbstractCompile){
return@doLast
}
val javaCompile: AbstractCompile = compileTask
val compileKotlinTask = project.tasks.named("compileKotlin", KotlinCompile::class.java)
if (compileKotlinTask.get() !is KotlinCompile){
return@doLast
}
val compileKotlin = compileKotlinTask.get()
val cacheDir = try {
compileKotlin.destinationDirectory.get().asFile
} catch (e: Exception) {
null
}
val kotlinPath = cacheDir ?: File(project.buildDir.path + "/classes/kotlin/main".adapterOSPath())
doAopTask(project, isApp, variantName, buildTypeName, javaCompile, kotlinPath,false)
}
}
return
}
val hasConfig = project.extensions.findByName("androidAopConfig") != null
val syncConfig = !root && hasConfig && isApp
if (syncConfig){
val taskName = "${project.name}AndroidAopConfigSyncTask"
project.tasks.register(taskName, SyncConfigTask::class.java)
project.afterEvaluate {
project.tasks.findByName("preBuild")?.finalizedBy(taskName)
}
}
val kotlinCompileFilePathMap = mutableMapOf()
val android = androidObject as BaseExtension
val variants = if (isApp or isDynamicLibrary) {
(android as AppExtension).applicationVariants
} else {
(android as LibraryExtension).libraryVariants
}
variants.all { variant ->
if (syncConfig){
AndroidAopConfig.syncConfig(project)
}
try {
project.tasks.withType(KotlinCompile::class.java).configureEach { task ->
kotlinCompileFilePathMap[task.name] = task
}
} catch (_: Exception) {
}
val javaCompile: AbstractCompile =
if (DefaultGroovyMethods.hasProperty(variant, "javaCompileProvider") != null) {
//gradle 4.10.1 +
variant.javaCompileProvider.get()
} else if (DefaultGroovyMethods.hasProperty(variant, "javaCompiler") != null) {
variant.javaCompiler as AbstractCompile
} else {
variant.javaCompile as AbstractCompile
}
val variantName = variant.name
val buildTypeName = variant.buildType.name
if (!isIncremental() && javaCompile is JavaCompile && isDebugMode(buildTypeName,variantName)){
javaCompile.options.isIncremental = false
}
// println("CompilePlugin=variant=$variantName,output.name=${variant.buildType.name},isDebug=${isDebugMode(buildTypeName,variantName)}")
if (isApp && isIncremental()){
javaCompile.doFirst{
val enabled = try {
val firstConfig = project.extensions.getByType(AndroidAopConfig::class.java)
firstConfig.enabled
} catch (e: Exception) {
true
}
if (enabled && isDebugMode(buildTypeName,variantName)){
WovenIntoCode.deleteOtherCompileClass(project, variantName)
}
}
}
javaCompile.doLast{
val task = kotlinCompileFilePathMap["compile${variantName.capitalized()}Kotlin"]
val cacheDir = try {
task?.destinationDirectory?.get()?.asFile
} catch (e: Exception) {
null
}
val kotlinPath = cacheDir ?: File(project.buildDir.path + "/tmp/kotlin-classes/".adapterOSPath() + variantName)
doAopTask(project, isApp, variantName, buildTypeName, javaCompile, kotlinPath)
}
}
if (hasBuildConfig()){
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
val variantList: ArrayList = ArrayList()
androidComponents.onVariants { variant ->
variantList.add(variant)
val variantName = variant.name
val path = Utils.aopDebugModeJavaDir(variantName)
val debugModeDir = File("${project.buildDir.absolutePath}$path")
variant.sources.java?.let { java ->
if (!debugModeDir.exists()){
debugModeDir.mkdirs()
}
java.addStaticSourceDirectory("build$path")
}
val variantNameCapitalized = variantName.capitalized()
var packageName = if (android.namespace == null || android.namespace == "null"){
android.defaultConfig.applicationId.toString()
}else{
android.namespace.toString()
}
if (packageName == "null"){
for (sourceSet in android.sourceSets) {
// println("Source set name: ${sourceSet.name}")
// println("Java srcDirs: ${sourceSet.java.srcDirs}")
// println("Res srcDirs: ${sourceSet.res.srcDirs}")
if (sourceSet.name == "main"){
val pkName = getPackageName(sourceSet.java.srcDirs,debugModeDir)
if (pkName != null){
packageName = pkName
break
}
}
}
}
if (packageName == "null"){
for (sourceSet in android.sourceSets) {
if (sourceSet.name == "release"){
val pkName = getPackageName(sourceSet.java.srcDirs,debugModeDir)
if (pkName != null){
packageName = pkName
break
}
}
}
}
if (packageName == "null"){
for (sourceSet in android.sourceSets) {
if (sourceSet.name == "debug"){
val pkName = getPackageName(sourceSet.java.srcDirs,debugModeDir)
if (pkName != null){
packageName = pkName
break
}
}
}
}
val buildTypeName: String? = variant.buildType
project
.tasks
.register("$DEBUG_MODE_FILE_TASK_NAME$variantNameCapitalized", DebugModeFileTask::class.java){
it.debugModeDir = debugModeDir.absolutePath
it.packageName = packageName
it.variantName = variantName
it.buildTypeName = buildTypeName
it.isAndroidModule = true
}
}
project.afterEvaluate {
for (variant in variantList) {
val variantName = variant.name
val variantNameCapitalized = variantName.capitalized()
project.tasks.findByName("pre${variantNameCapitalized}Build")?.finalizedBy("$DEBUG_MODE_FILE_TASK_NAME$variantNameCapitalized")
}
}
}
}
private fun getPackageName(srcDirs :Set,debugModeDir:File):String?{
for (srcDir in srcDirs) {
if (srcDir.absolutePath != debugModeDir.absolutePath){
if (srcDir.exists()){
//说明这个才是真正的源码所在路径
val packageFile = getPackageNameFile(srcDir,0)
val relativePath = packageFile.getRelativePath(srcDir).replace("/",".")
return if (relativePath.endsWith(".")){
relativePath.substring(0,relativePath.length-1)
}else{
relativePath
}
}
}
}
return null
}
private fun doAopTask(project: Project, isApp:Boolean, variantName: String, buildTypeName: String,
javaCompile:AbstractCompile, kotlinPath: File, isAndroidModule : Boolean = true){
val logger = project.logger
val androidAopConfig : AndroidAopConfig = if (isApp){
val config = project.extensions.getByType(AndroidAopConfig::class.java)
config.initConfig()
config
}else{
var config = InitConfig.optFromJsonString(
InitConfig.readAsString(Utils.configJsonFile(project)),
AndroidAopConfig::class.java)
if (config == null){
config = AndroidAopConfig()
}
config
}
if (androidAopConfig.cutInfoJson){
InitConfig.initCutInfo(project,false)
}
val debugMode = if (isAndroidModule){
isDebugMode(buildTypeName,variantName)
}else{
isDebugMode()
}
if (androidAopConfig.enabled && debugMode){
ClassFileUtils.debugMode = true
val hint = "AndroidAOP提示:打正式包时请注意通过设置 androidAop.debugMode 或 androidAop.debugMode.variantOnlyDebug 关闭debug模式"
if (buildTypeName == "release"){
logger.error(hint)
}else{
logger.warn(hint)
}
val localInput = mutableListOf()
val javaPath = File(javaCompile.destinationDirectory.asFile.orNull.toString())
if (javaPath.exists()){
localInput.add(javaPath)
}
if (kotlinPath.exists()){
localInput.add(kotlinPath)
}
val jarInput = mutableListOf()
val bootJarPath = mutableSetOf()
if (isAndroidModule){
val androidConfig = AndroidConfig(project)
val list: List = androidConfig.getBootClasspath()
for (file in list) {
bootJarPath.add(file.absolutePath)
}
}
for (file in localInput) {
bootJarPath.add(file.absolutePath)
}
for (file in javaCompile.classpath) {
if (file.absolutePath !in bootJarPath && file.exists()){
if (file.isDirectory){
localInput.add(file)
}else{
jarInput.add(file)
}
}
}
if (localInput.isNotEmpty()){
val reflectInvokeMethod = if (isAndroidModule){
isReflectInvokeMethod(buildTypeName,variantName)
}else{
isReflectInvokeMethod()
}
ClassFileUtils.reflectInvokeMethod = reflectInvokeMethod
ClassFileUtils.reflectInvokeMethodStatic = isReflectInvokeMethodStatic()
val output = File(javaCompile.destinationDirectory.asFile.orNull.toString())
val task = CompileAndroidAopTask(jarInput,localInput,output,project,isApp,
File(Utils.aopCompileTempDir(project,variantName)),
File(Utils.invokeJsonFile(project,variantName)),
variantName,isAndroidModule
)
task.taskAction()
}
}
}
private fun getPackageNameFile(file: File,deep:Int): File {
if (!file.isDirectory){
return file.parentFile
}
val files = file.listFiles()
if (files != null){
return if (files.size == 1){
if (files[0].isDirectory){
getPackageNameFile(files[0],deep+1)
}else{
files[0].parentFile
}
}else{
if (files.isNotEmpty()){
getPackageNameFile(files[0],deep+1)
}else{
file
}
}
}else{
return file
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy