mirror of
https://github.com/HighCapable/Gropify.git
synced 2025-12-16 08:01:09 +08:00
255 lines
9.9 KiB
Kotlin
255 lines
9.9 KiB
Kotlin
/*
|
|
* Gropify - A type-safe and modern properties plugin for Gradle.
|
|
* Copyright (C) 2019 HighCapable
|
|
* https://github.com/HighCapable/Gropify
|
|
*
|
|
* Apache License Version 2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* This file is created by fankes on 2025/10/12.
|
|
*/
|
|
package com.highcapable.gropify.plugin
|
|
|
|
import com.highcapable.gropify.debug.Logger
|
|
import com.highcapable.gropify.debug.require
|
|
import com.highcapable.gropify.gradle.api.entity.ProjectDescriptor
|
|
import com.highcapable.gropify.plugin.config.proxy.GropifyConfig
|
|
import com.highcapable.gropify.plugin.config.type.GropifyLocation
|
|
import com.highcapable.gropify.plugin.deployer.BuildscriptDeployer
|
|
import com.highcapable.gropify.plugin.deployer.SourceCodeDeployer
|
|
import com.highcapable.gropify.plugin.generator.extension.PropertyMap
|
|
import com.highcapable.gropify.plugin.generator.extension.PropertyTypeValue
|
|
import com.highcapable.gropify.plugin.generator.extension.createTypeValue
|
|
import com.highcapable.gropify.plugin.generator.extension.createTypeValueByType
|
|
import com.highcapable.gropify.utils.extension.hasInterpolation
|
|
import com.highcapable.gropify.utils.extension.removeSurroundingQuotes
|
|
import com.highcapable.gropify.utils.extension.replaceInterpolation
|
|
import com.highcapable.gropify.utils.extension.toStringMap
|
|
import org.gradle.api.Project
|
|
import org.gradle.api.initialization.Settings
|
|
import java.io.File
|
|
import java.io.FileReader
|
|
import java.util.*
|
|
import kotlin.collections.component1
|
|
import kotlin.collections.component2
|
|
import kotlin.collections.set
|
|
import kotlin.properties.Delegates
|
|
|
|
/**
|
|
* Default properties' key-values deployer.
|
|
*/
|
|
internal object DefaultDeployer {
|
|
|
|
private var config by Delegates.notNull<GropifyConfig>()
|
|
|
|
private var lastModifiedHashCode = 0
|
|
private var configModified = true
|
|
|
|
private val deployers by lazy {
|
|
listOf(
|
|
BuildscriptDeployer { config },
|
|
SourceCodeDeployer { config }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Initialize deployers.
|
|
* @param settings the current Gradle settings.
|
|
* @param config the gropify configuration.
|
|
*/
|
|
fun init(settings: Settings, config: GropifyConfig) {
|
|
DefaultDeployer.config = config
|
|
if (!isEnabled()) return
|
|
|
|
checkingConfigModified(settings)
|
|
|
|
Logger.debug("Initializing deployers, config modified: $configModified")
|
|
deployers.forEach { it.init(settings, configModified) }
|
|
}
|
|
|
|
/**
|
|
* Resolve deployers before project evaluation.
|
|
* @param rootProject the current root project.
|
|
*/
|
|
fun resolve(rootProject: Project) {
|
|
if (!isEnabled()) return
|
|
|
|
Logger.debug("Resolving deployers, config modified: $configModified")
|
|
deployers.forEach { it.resolve(rootProject, configModified) }
|
|
}
|
|
|
|
/**
|
|
* Deploy deployers after project evaluation.
|
|
* @param rootProject the current root project.
|
|
*/
|
|
fun deploy(rootProject: Project) {
|
|
if (!isEnabled()) return
|
|
|
|
Logger.debug("Deploying deployers, config modified: $configModified")
|
|
deployers.forEach { it.deploy(rootProject, configModified) }
|
|
}
|
|
|
|
/**
|
|
* Generate properties' key-values map.
|
|
* @param config the generate configuration.
|
|
* @param descriptor the project descriptor.
|
|
* @return [PropertyMap]
|
|
*/
|
|
fun generateMap(config: GropifyConfig.CommonGenerateConfig, descriptor: ProjectDescriptor): PropertyMap {
|
|
val properties = mutableMapOf<String, PropertyTypeValue>()
|
|
val resolveProperties = mutableMapOf<Any?, Any?>()
|
|
|
|
val locations = mutableMapOf<String, String>()
|
|
|
|
config.permanentKeyValues.forEach { (key, value) ->
|
|
properties[key] = value.createTypeValueByType(config.useTypeAutoConversion, key)
|
|
locations[key] = "Permanent Key-Value"
|
|
}
|
|
config.locations.forEach { location ->
|
|
when (location) {
|
|
GropifyLocation.CurrentProject -> createProperties(config, descriptor.currentDir).forEach {
|
|
resolveProperties.putAll(it)
|
|
|
|
it.forEach { (key, _) -> locations[key.toString()] = location.name }
|
|
}
|
|
GropifyLocation.RootProject -> createProperties(config, descriptor.rootDir).forEach {
|
|
resolveProperties.putAll(it)
|
|
|
|
it.forEach { (key, _) -> locations[key.toString()] = location.name }
|
|
}
|
|
GropifyLocation.Global -> createProperties(config, descriptor.homeDir).forEach {
|
|
resolveProperties.putAll(it)
|
|
|
|
it.forEach { (key, _) -> locations[key.toString()] = location.name }
|
|
}
|
|
GropifyLocation.System -> {
|
|
val system = System.getProperties()
|
|
resolveProperties.putAll(system)
|
|
|
|
system.forEach { (key, _) -> locations[key.toString()] = location.name }
|
|
}
|
|
GropifyLocation.SystemEnv -> {
|
|
val systemEnv = System.getenv()
|
|
resolveProperties.putAll(systemEnv)
|
|
|
|
systemEnv.forEach { (key, _) -> locations[key] = location.name }
|
|
}
|
|
}
|
|
}
|
|
|
|
resolveProperties.filter { (key, value) ->
|
|
if (config.excludeNonStringValue)
|
|
key is CharSequence && key.isNotBlank() && value is CharSequence
|
|
else key.toString().isNotBlank() && value != null
|
|
}.toStringMap().filter { (key, _) ->
|
|
config.includeKeys.ifEmpty { null }?.any { content ->
|
|
when (content) {
|
|
is Regex -> content.matches(key)
|
|
else -> content.toString() == key
|
|
}
|
|
} ?: true
|
|
}.filter { (key, _) ->
|
|
config.excludeKeys.ifEmpty { null }?.none { content ->
|
|
when (content) {
|
|
is Regex -> content.matches(key)
|
|
else -> content.toString() == key
|
|
}
|
|
} ?: true
|
|
}.toMutableMap().also { resolveKeyValues ->
|
|
resolveKeyValues.forEach { (key, value) ->
|
|
val resolveKeys = mutableListOf<String>()
|
|
|
|
fun String.resolveValue(): String = replaceInterpolation { matchKey ->
|
|
Gropify.require(resolveKeys.size <= 5) {
|
|
"Key \"$key\" has been called recursively multiple times of those $resolveKeys."
|
|
}
|
|
|
|
resolveKeys.add(matchKey)
|
|
var resolveValue = if (config.useValueInterpolation)
|
|
resolveKeyValues[matchKey] ?: ""
|
|
else matchKey
|
|
resolveValue = resolveValue.removeSurroundingQuotes()
|
|
|
|
if (resolveValue.hasInterpolation()) resolveValue.resolveValue()
|
|
else resolveValue
|
|
}
|
|
|
|
if (value.hasInterpolation()) resolveKeyValues[key] = value.resolveValue()
|
|
}
|
|
|
|
properties.putAll(resolveKeyValues.map { (key, value) ->
|
|
key to value.createTypeValue(config.useTypeAutoConversion, key)
|
|
})
|
|
}
|
|
|
|
// Replace all key-values if exists.
|
|
config.replacementKeyValues.forEach { (key, value) ->
|
|
properties[key] = value.createTypeValueByType(config.useTypeAutoConversion, key)
|
|
locations[key] = "Replacement Key-Value${locations[key]?.let { ", $it" }}"
|
|
}
|
|
|
|
// Apply key-values rules.
|
|
properties.forEach { (key, value) ->
|
|
val (mapper, type) = config.keyValuesRules[key] ?: return@forEach
|
|
|
|
val resolveValue = mapper(value.raw).createTypeValue(config.useTypeAutoConversion, key, type)
|
|
properties[key] = resolveValue
|
|
|
|
locations[key] = "Rule-based Key-Value${locations[key]?.let { ", $it" }}"
|
|
}
|
|
|
|
properties.forEach { (key, value) ->
|
|
Logger.debug(
|
|
"""
|
|
Generated property for ${config.name}
|
|
----------
|
|
[Key]: $key
|
|
[Value]: ${value.raw}
|
|
[Code Value]: ${value.codeValue}
|
|
[Type]: ${value.type.simpleName}
|
|
[Location]: ${locations[key] ?: "Unknown"}
|
|
----------
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
return properties
|
|
}
|
|
|
|
private fun createProperties(config: GropifyConfig.CommonGenerateConfig, dir: File?) = runCatching {
|
|
mutableListOf<Properties>().apply {
|
|
config.existsPropertyFiles.forEach {
|
|
val propertiesFile = dir?.resolve(it)
|
|
if (propertiesFile?.exists() == true)
|
|
add(Properties().apply { load(FileReader(propertiesFile.absolutePath)) })
|
|
}
|
|
}
|
|
}.getOrNull() ?: mutableListOf()
|
|
|
|
private fun checkingConfigModified(settings: Settings) {
|
|
settings.settingsDir.also { dir ->
|
|
val groovyHashCode = dir.resolve("settings.gradle").takeIf { it.exists() }?.readText()?.hashCode()
|
|
val kotlinHashCode = dir.resolve("settings.gradle.kts").takeIf { it.exists() }?.readText()?.hashCode()
|
|
val gradleHashCode = groovyHashCode ?: kotlinHashCode ?: -1
|
|
|
|
configModified = gradleHashCode == -1 || lastModifiedHashCode != gradleHashCode
|
|
lastModifiedHashCode = gradleHashCode
|
|
}
|
|
}
|
|
|
|
private fun isEnabled(): Boolean {
|
|
if (!config.isEnabled) Logger.debug("Gropify is disabled, skipping deployment process.")
|
|
return config.isEnabled
|
|
}
|
|
} |