mirror of
https://github.com/HighCapable/SweetProperty.git
synced 2025-09-05 18:25:35 +08:00
Initial commit
This commit is contained in:
2
sweetproperty-gradle-plugin/.gitignore
vendored
Normal file
2
sweetproperty-gradle-plugin/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.gradle
|
||||
build/
|
65
sweetproperty-gradle-plugin/build.gradle.kts
Normal file
65
sweetproperty-gradle-plugin/build.gradle.kts
Normal file
@@ -0,0 +1,65 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
autowire(libs.plugins.kotlin.jvm)
|
||||
autowire(libs.plugins.maven.publish)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = property.project.groupName
|
||||
version = property.project.version
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
sourceSets.all { languageSettings { languageVersion = "2.0" } }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(com.squareup.kotlinpoet)
|
||||
implementation(com.squareup.javapoet)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create(property.project.moduleName) {
|
||||
id = property.project.groupName
|
||||
implementationClass = property.gradle.plugin.implementationClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
coordinates(property.project.groupName, property.project.moduleName, property.project.version)
|
||||
pom {
|
||||
name = property.project.name
|
||||
description = property.project.description
|
||||
url = property.project.url
|
||||
licenses {
|
||||
license {
|
||||
name = property.project.licence.name
|
||||
url = property.project.licence.url
|
||||
distribution = property.project.licence.url
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = property.project.developer.id
|
||||
name = property.project.developer.name
|
||||
email = property.project.developer.email
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url = property.maven.publish.scm.url
|
||||
connection = property.maven.publish.scm.connection
|
||||
developerConnection = property.maven.publish.scm.developerConnection
|
||||
}
|
||||
}
|
||||
publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.S01)
|
||||
signAllPublications()
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/25.
|
||||
*/
|
||||
package com.highcapable.sweetproperty
|
||||
|
||||
import com.highcapable.sweetproperty.generated.SweetPropertyProperties
|
||||
|
||||
/**
|
||||
* [SweetProperty] 的装载调用类
|
||||
*/
|
||||
object SweetProperty {
|
||||
|
||||
/** 标签名称 */
|
||||
const val TAG = SweetPropertyProperties.PROJECT_NAME
|
||||
|
||||
/** 版本 */
|
||||
const val VERSION = SweetPropertyProperties.PROJECT_VERSION
|
||||
|
||||
/** 项目地址 */
|
||||
const val PROJECT_URL = SweetPropertyProperties.PROJECT_URL
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/28.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.gradle.entity
|
||||
|
||||
import com.highcapable.sweetproperty.gradle.factory.fullName
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.noBlank
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.initialization.Settings
|
||||
import java.io.File
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* 项目描述实现类
|
||||
*/
|
||||
internal class ProjectDescriptor private constructor() {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/**
|
||||
* 创建 [ProjectDescriptor]
|
||||
* @param settings 当前设置
|
||||
* @param name 当前名称 (项目) - 默认空
|
||||
* @return [ProjectDescriptor]
|
||||
*/
|
||||
internal fun create(settings: Settings, name: String = "") = ProjectDescriptor().also {
|
||||
val isRootProject = name.isBlank() || name == settings.rootProject.name
|
||||
it.type = Type.SETTINGS
|
||||
it.name = name.noBlank() ?: settings.rootProject.name
|
||||
it.currentDir = (if (isRootProject) settings.rootProject else settings.findProject(":$name"))?.projectDir
|
||||
?: SError.make("Project \"$name\" not found")
|
||||
it.rootDir = settings.rootDir
|
||||
it.homeDir = settings.gradle.gradleUserHomeDir
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 [ProjectDescriptor]
|
||||
* @param project 当前项目
|
||||
* @return [ProjectDescriptor]
|
||||
*/
|
||||
internal fun create(project: Project) = ProjectDescriptor().also {
|
||||
it.type = Type.PROJECT
|
||||
it.name = project.fullName
|
||||
it.currentDir = project.projectDir
|
||||
it.rootDir = project.rootDir
|
||||
it.homeDir = project.gradle.gradleUserHomeDir
|
||||
}
|
||||
}
|
||||
|
||||
/** 当前项目类型 */
|
||||
internal var type by Delegates.notNull<Type>()
|
||||
|
||||
/** 当前项目名称 */
|
||||
internal var name = ""
|
||||
|
||||
/** 当前项目目录 */
|
||||
internal var currentDir by Delegates.notNull<File>()
|
||||
|
||||
/** 根项目目录 */
|
||||
internal var rootDir by Delegates.notNull<File>()
|
||||
|
||||
/** 用户目录 */
|
||||
internal var homeDir by Delegates.notNull<File>()
|
||||
|
||||
/**
|
||||
* 项目类型定义类
|
||||
*/
|
||||
internal enum class Type {
|
||||
/** 设置 */
|
||||
SETTINGS,
|
||||
|
||||
/** 项目 */
|
||||
PROJECT
|
||||
}
|
||||
|
||||
override fun toString() = "ProjectDescriptor(type: $type, name: $name)"
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
@file:Suppress("unused", "USELESS_CAST", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package com.highcapable.sweetproperty.gradle.factory
|
||||
|
||||
import com.highcapable.sweetproperty.utils.camelcase
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
|
||||
/**
|
||||
* 创建、获取扩展方法
|
||||
* @param name 方法名称 - 自动调用 [toSafeExtName]
|
||||
* @param clazz 目标对象 [Class]
|
||||
* @param args 方法参数
|
||||
* @return [ExtensionAware]
|
||||
*/
|
||||
internal fun ExtensionAware.getOrCreate(name: String, clazz: Class<*>, vararg args: Any?) = name.toSafeExtName().let { sName ->
|
||||
runCatching { extensions.create(sName, clazz, *args).asExtension() }.getOrElse {
|
||||
if ((it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true).not()) throw it
|
||||
runCatching { extensions.getByName(sName).asExtension() }.getOrNull() ?: SError.make("Create or get extension failed with name \"$sName\"")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建、获取扩展方法 - 目标对象 [T]
|
||||
* @param name 方法名称 - 自动调用 [toSafeExtName]
|
||||
* @param args 方法参数
|
||||
* @return [T]
|
||||
*/
|
||||
internal inline fun <reified T> ExtensionAware.getOrCreate(name: String, vararg args: Any?) = name.toSafeExtName().let { sName ->
|
||||
runCatching { extensions.create(sName, T::class.java, *args) as T }.getOrElse {
|
||||
if ((it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true).not()) throw it
|
||||
runCatching { extensions.getByName(sName) as? T? }.getOrNull() ?: SError.make("Create or get extension failed with name \"$sName\"")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展方法
|
||||
* @param name 方法名称
|
||||
* @return [ExtensionAware]
|
||||
*/
|
||||
internal fun ExtensionAware.get(name: String) =
|
||||
runCatching { extensions.getByName(name).asExtension() }.getOrNull() ?: SError.make("Could not get extension with name \"$name\"")
|
||||
|
||||
/**
|
||||
* 获取扩展方法 - 目标对象 [T]
|
||||
* @param name 方法名称
|
||||
* @return [T]
|
||||
*/
|
||||
internal inline fun <reified T> ExtensionAware.get(name: String) =
|
||||
runCatching { extensions.getByName(name) as T }.getOrNull() ?: SError.make("Could not get extension with name \"$name\"")
|
||||
|
||||
/**
|
||||
* 获取扩展方法 - 目标对象 [T]
|
||||
* @return [T]
|
||||
*/
|
||||
internal inline fun <reified T> ExtensionAware.get() =
|
||||
runCatching { extensions.getByType(T::class.java) as T }.getOrNull() ?: SError.make("Could not get extension with type ${T::class.java}")
|
||||
|
||||
/**
|
||||
* 配置扩展方法 - 目标对象 [T]
|
||||
* @param name 方法名称
|
||||
* @param configure 配置方法体
|
||||
*/
|
||||
internal inline fun <reified T> ExtensionAware.configure(name: String, configure: Action<T>) = extensions.configure(name, configure)
|
||||
|
||||
/**
|
||||
* 是否存在扩展方法
|
||||
* @param name 方法名称
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal fun ExtensionAware.hasExtension(name: String) = runCatching { extensions.getByName(name); true }.getOrNull() ?: false
|
||||
|
||||
/**
|
||||
* 转换到扩展方法类型 [ExtensionAware]
|
||||
* @return [ExtensionAware]
|
||||
* @throws IllegalStateException 如果类型不是 [ExtensionAware]
|
||||
*/
|
||||
internal fun Any.asExtension() = this as? ExtensionAware? ?: SError.make("This instance \"$this\" is not a valid Extension")
|
||||
|
||||
/**
|
||||
* 由于 Gradle 存在一个 [ExtensionAware] 的扩展
|
||||
*
|
||||
* 此功能用于检测当前字符串是否为 Gradle 使用的关键字名称
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal fun String.isUnSafeExtName() = camelcase().let { it == "ext" || it == "extra" || it == "extraProperties" || it == "extensions" }
|
||||
|
||||
/**
|
||||
* 由于 Gradle 存在一个 [ExtensionAware] 的扩展
|
||||
*
|
||||
* 此功能用于转换不符合规定的字符串到 "{字符串}s"
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.toSafeExtName() = if (isUnSafeExtName()) "${this}s" else this
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.gradle.factory
|
||||
|
||||
import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
|
||||
import com.highcapable.sweetproperty.utils.toFile
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.buildscript
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
|
||||
/**
|
||||
* 获取指定项目的完整名称
|
||||
* @return [String]
|
||||
*/
|
||||
internal val Project.fullName
|
||||
get(): String {
|
||||
val baseNames = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* 递归子项目
|
||||
* @param project 当前项目
|
||||
*/
|
||||
fun fetchChild(project: Project) {
|
||||
project.parent?.also { if (it != it.rootProject) fetchChild(it) }
|
||||
baseNames.add(project.name)
|
||||
}
|
||||
fetchChild(project = this)
|
||||
return buildString { baseNames.onEach { append(":$it") }.clear() }.drop(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 向构建脚本添加自定义依赖
|
||||
* @param repositoryPath 存储库路径
|
||||
* @param pomData Maven POM 实体
|
||||
*/
|
||||
internal fun Project.addDependencyToBuildScript(repositoryPath: String, pomData: MavenPomData) =
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url = repositoryPath.toFile().toURI()
|
||||
mavenContent { includeGroup(pomData.groupId) }
|
||||
}
|
||||
}; dependencies { classpath("${pomData.groupId}:${pomData.artifactId}:${pomData.version}") }
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载构建脚本的 [Class]
|
||||
* @param name [Class] 完整名称
|
||||
* @return [Class]
|
||||
*/
|
||||
internal fun Project.loadBuildScriptClass(name: String) = buildscript.classLoader.loadClass(name)
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.gradle.proxy
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
/**
|
||||
* Gradle 生命周期接口
|
||||
*/
|
||||
internal interface IGradleLifecycle {
|
||||
|
||||
/**
|
||||
* 当 Gradle 开始装载时回调
|
||||
* @param settings 当前设置
|
||||
*/
|
||||
fun onSettingsLoaded(settings: Settings)
|
||||
|
||||
/**
|
||||
* 当 Gradle 装载完成时回调
|
||||
* @param settings 当前设置
|
||||
*/
|
||||
fun onSettingsEvaluate(settings: Settings)
|
||||
|
||||
/**
|
||||
* 当 Gradle 开始装载项目时回调
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
fun onProjectLoaded(rootProject: Project)
|
||||
|
||||
/**
|
||||
* 当 Gradle 项目装载完成时回调
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
fun onProjectEvaluate(rootProject: Project)
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.gradle.factory.getOrCreate
|
||||
import com.highcapable.sweetproperty.gradle.proxy.IGradleLifecycle
|
||||
import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
|
||||
import com.highcapable.sweetproperty.plugin.helper.PropertiesDeployHelper
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
/**
|
||||
* [SweetProperty] 插件扩展类
|
||||
*/
|
||||
internal class SweetPropertyExtension internal constructor() : IGradleLifecycle {
|
||||
|
||||
/** 当前配置方法体实例 */
|
||||
private var configure: SweetPropertyConfigureExtension? = null
|
||||
|
||||
override fun onSettingsLoaded(settings: Settings) {
|
||||
configure = settings.getOrCreate<SweetPropertyConfigureExtension>(SweetPropertyConfigureExtension.NAME)
|
||||
}
|
||||
|
||||
override fun onSettingsEvaluate(settings: Settings) {
|
||||
val configs = configure?.build(settings) ?: SError.make("Extension \"${SweetPropertyConfigureExtension.NAME}\" create failed")
|
||||
PropertiesDeployHelper.initialize(settings, configs)
|
||||
}
|
||||
|
||||
override fun onProjectLoaded(rootProject: Project) {
|
||||
PropertiesDeployHelper.resolve(rootProject)
|
||||
}
|
||||
|
||||
override fun onProjectEvaluate(rootProject: Project) {
|
||||
PropertiesDeployHelper.deploy(rootProject)
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/25.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.sweetproperty.plugin
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.initialization.Settings
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
|
||||
/**
|
||||
* [SweetProperty] 插件定义类
|
||||
*/
|
||||
class SweetPropertyPlugin<T : ExtensionAware> internal constructor() : Plugin<T> {
|
||||
|
||||
/** 当前扩展实例 */
|
||||
private val extension = SweetPropertyExtension()
|
||||
|
||||
override fun apply(target: T) = when (target) {
|
||||
is Settings -> {
|
||||
extension.onSettingsLoaded(target)
|
||||
target.gradle.settingsEvaluated { extension.onSettingsEvaluate(target) }
|
||||
target.gradle.projectsLoaded {
|
||||
extension.onProjectLoaded(rootProject)
|
||||
rootProject.afterEvaluate { extension.onProjectEvaluate(rootProject) }
|
||||
}
|
||||
}
|
||||
else -> SError.make("${SweetProperty.TAG} can only applied in settings.gradle/settings.gradle.kts, but current is $target")
|
||||
}
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/28.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.config.default
|
||||
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
|
||||
import com.highcapable.sweetproperty.utils.noBlank
|
||||
|
||||
/**
|
||||
* 默认配置类实现类
|
||||
*/
|
||||
internal object DefaultConfigs {
|
||||
|
||||
/**
|
||||
* 获取默认子配置类
|
||||
* @param name 名称
|
||||
* @param base 继承配置 - 默认 null
|
||||
* @return [ISweetPropertyConfigs.ISubConfigs]
|
||||
*/
|
||||
internal fun subConfigs(name: String, base: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null) =
|
||||
object : ISweetPropertyConfigs.ISubConfigs {
|
||||
override val sourcesCode get() = sourcesCodeGenerateConfigs(name, base)
|
||||
override val buildScript get() = buildScriptGenerateConfigs(name, base)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认项目生成代码配置类
|
||||
* @param name 名称
|
||||
* @param selfBase 自身继承配置 - 默认 null
|
||||
* @param globalBase 全局继承配置 - 默认 null
|
||||
* @return [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs]
|
||||
*/
|
||||
internal fun sourcesCodeGenerateConfigs(
|
||||
name: String,
|
||||
selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
|
||||
globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
|
||||
) = object : ISweetPropertyConfigs.ISourcesCodeGenerateConfigs {
|
||||
override val name get() = name
|
||||
override val generateDirPath get() = ISweetPropertyConfigs.DEFAULT_GENERATE_DIR_PATH
|
||||
override val packageName get() = ""
|
||||
override val className get() = ""
|
||||
override val isEnableRestrictedAccess get() = false
|
||||
override val isEnable
|
||||
get() = selfBase?.isEnable
|
||||
?: globalBase?.isEnable
|
||||
?: baseGenerateConfigs(name).isEnable
|
||||
override val propertiesFileName
|
||||
get() = selfBase?.propertiesFileName?.noBlank()
|
||||
?: globalBase?.propertiesFileName?.noBlank()
|
||||
?: baseGenerateConfigs(name).propertiesFileName
|
||||
override val permanentKeyValues
|
||||
get() = selfBase?.permanentKeyValues
|
||||
?: globalBase?.permanentKeyValues
|
||||
?: baseGenerateConfigs(name).permanentKeyValues
|
||||
override val excludeKeys
|
||||
get() = selfBase?.excludeKeys
|
||||
?: globalBase?.excludeKeys
|
||||
?: baseGenerateConfigs(name).excludeKeys
|
||||
override val isEnableExcludeNonStringValue
|
||||
get() = selfBase?.isEnableExcludeNonStringValue
|
||||
?: globalBase?.isEnableExcludeNonStringValue
|
||||
?: baseGenerateConfigs(name).isEnableExcludeNonStringValue
|
||||
override val isEnableTypeAutoConversion
|
||||
get() = selfBase?.isEnableTypeAutoConversion
|
||||
?: globalBase?.isEnableTypeAutoConversion
|
||||
?: baseGenerateConfigs(name).isEnableTypeAutoConversion
|
||||
override val isEnableValueInterpolation
|
||||
get() = selfBase?.isEnableValueInterpolation
|
||||
?: globalBase?.isEnableValueInterpolation
|
||||
?: baseGenerateConfigs(name).isEnableValueInterpolation
|
||||
override val generateLocationTypes
|
||||
get() = selfBase?.generateLocationTypes
|
||||
?: globalBase?.generateLocationTypes
|
||||
?: baseGenerateConfigs(name).generateLocationTypes
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认构建脚本生成代码配置类
|
||||
* @param name 名称
|
||||
* @param selfBase 自身继承配置 - 默认 null
|
||||
* @param globalBase 全局继承配置 - 默认 null
|
||||
* @return [ISweetPropertyConfigs.IBuildScriptGenerateConfigs]
|
||||
*/
|
||||
internal fun buildScriptGenerateConfigs(
|
||||
name: String,
|
||||
selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
|
||||
globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
|
||||
) = object : ISweetPropertyConfigs.IBuildScriptGenerateConfigs {
|
||||
override val name get() = name
|
||||
override val extensionName get() = ISweetPropertyConfigs.DEFAULT_EXTENSION_NAME
|
||||
override val isEnable
|
||||
get() = selfBase?.isEnable
|
||||
?: globalBase?.isEnable
|
||||
?: baseGenerateConfigs(name).isEnable
|
||||
override val propertiesFileName
|
||||
get() = selfBase?.propertiesFileName?.noBlank()
|
||||
?: globalBase?.propertiesFileName?.noBlank()
|
||||
?: baseGenerateConfigs(name).propertiesFileName
|
||||
override val permanentKeyValues
|
||||
get() = selfBase?.permanentKeyValues
|
||||
?: globalBase?.permanentKeyValues
|
||||
?: baseGenerateConfigs(name).permanentKeyValues
|
||||
override val excludeKeys
|
||||
get() = selfBase?.excludeKeys
|
||||
?: globalBase?.excludeKeys
|
||||
?: baseGenerateConfigs(name).excludeKeys
|
||||
override val isEnableExcludeNonStringValue
|
||||
get() = selfBase?.isEnableExcludeNonStringValue
|
||||
?: globalBase?.isEnableExcludeNonStringValue
|
||||
?: baseGenerateConfigs(name).isEnableExcludeNonStringValue
|
||||
override val isEnableTypeAutoConversion
|
||||
get() = selfBase?.isEnableTypeAutoConversion
|
||||
?: globalBase?.isEnableTypeAutoConversion
|
||||
?: baseGenerateConfigs(name).isEnableTypeAutoConversion
|
||||
override val isEnableValueInterpolation
|
||||
get() = selfBase?.isEnableValueInterpolation
|
||||
?: globalBase?.isEnableValueInterpolation
|
||||
?: baseGenerateConfigs(name).isEnableValueInterpolation
|
||||
override val generateLocationTypes
|
||||
get() = selfBase?.generateLocationTypes
|
||||
?: globalBase?.generateLocationTypes
|
||||
?: baseGenerateConfigs(name).generateLocationTypes
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认通用生成代码配置类
|
||||
* @param name 名称
|
||||
* @return [ISweetPropertyConfigs.IBaseGenerateConfigs]
|
||||
*/
|
||||
private fun baseGenerateConfigs(name: String) = object : ISweetPropertyConfigs.IBaseGenerateConfigs {
|
||||
override val name get() = name
|
||||
override val isEnable get() = true
|
||||
override val propertiesFileName get() = ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME
|
||||
override val permanentKeyValues get() = mutableMapOf<String, Any>()
|
||||
override val excludeKeys get() = mutableListOf<Any>()
|
||||
override val isEnableExcludeNonStringValue get() = true
|
||||
override val isEnableTypeAutoConversion get() = true
|
||||
override val isEnableValueInterpolation get() = true
|
||||
override val generateLocationTypes get() = ISweetPropertyConfigs.defaultGenerateLocationTypes
|
||||
}
|
||||
}
|
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/28.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.config.factory
|
||||
|
||||
import com.highcapable.sweetproperty.gradle.factory.fullName
|
||||
import com.highcapable.sweetproperty.plugin.config.default.DefaultConfigs
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
|
||||
import com.highcapable.sweetproperty.utils.noBlank
|
||||
import org.gradle.api.Project
|
||||
|
||||
/**
|
||||
* 获取当前配置
|
||||
* @param project 当前项目
|
||||
* @return [ISweetPropertyConfigs.ISubConfigs]
|
||||
*/
|
||||
internal fun ISweetPropertyConfigs.with(project: Project) = projects[project.fullName] ?: global
|
||||
|
||||
/**
|
||||
* 创建 [ISweetPropertyConfigs.ISubConfigs] 实例
|
||||
* @param name 名称 - 默认 "Global"
|
||||
* @param global 全局配置 - 默认为自身
|
||||
* @return [ISweetPropertyConfigs.ISubConfigs]
|
||||
*/
|
||||
internal fun SweetPropertyConfigureExtension.SubConfigureExtension.create(
|
||||
name: String = "Global",
|
||||
global: SweetPropertyConfigureExtension.SubConfigureExtension = this
|
||||
) = object : ISweetPropertyConfigs.ISubConfigs {
|
||||
override val sourcesCode
|
||||
get() = this@create.sourcesCodeConfigure?.create(name, global.sourcesCodeConfigure, this@create.allConfigure, global.allConfigure)
|
||||
?: global.sourcesCodeConfigure?.create(name, globalBase = global.allConfigure)
|
||||
?: DefaultConfigs.subConfigs(name, global.allConfigure).sourcesCode
|
||||
override val buildScript
|
||||
get() = this@create.buildScriptConfigure?.create(name, global.buildScriptConfigure, this@create.allConfigure, global.allConfigure)
|
||||
?: global.buildScriptConfigure?.create(name, globalBase = global.allConfigure)
|
||||
?: DefaultConfigs.subConfigs(name, global.allConfigure).buildScript
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs] 实例
|
||||
* @param name 名称
|
||||
* @param global 全局配置 - 默认 null
|
||||
* @param selfBase 自身继承配置 - 默认 null
|
||||
* @param globalBase 全局继承配置 - 默认 null
|
||||
* @return [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs]
|
||||
*/
|
||||
private fun SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtension.create(
|
||||
name: String,
|
||||
global: SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtension? = null,
|
||||
selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
|
||||
globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
|
||||
) = object : ISweetPropertyConfigs.ISourcesCodeGenerateConfigs {
|
||||
override val name get() = name
|
||||
override val generateDirPath
|
||||
get() = this@create.generateDirPath.noBlank()
|
||||
?: global?.generateDirPath?.noBlank()
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).generateDirPath
|
||||
override val packageName
|
||||
get() = this@create.packageName.noBlank()
|
||||
?: global?.packageName?.noBlank()
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).packageName
|
||||
override val className
|
||||
get() = this@create.className.noBlank()
|
||||
?: global?.className?.noBlank()
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).className
|
||||
override val isEnableRestrictedAccess
|
||||
get() = this@create.isEnableRestrictedAccess
|
||||
?: global?.isEnableRestrictedAccess
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableRestrictedAccess
|
||||
override val isEnable
|
||||
get() = this@create.isEnable
|
||||
?: selfBase?.isEnable
|
||||
?: global?.isEnable
|
||||
?: globalBase?.isEnable
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnable
|
||||
override val propertiesFileName
|
||||
get() = this@create.propertiesFileName.noBlank()
|
||||
?: global?.propertiesFileName?.noBlank()
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).propertiesFileName
|
||||
override val permanentKeyValues
|
||||
get() = this@create.permanentKeyValues
|
||||
?: global?.permanentKeyValues
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).permanentKeyValues
|
||||
override val excludeKeys
|
||||
get() = this@create.excludeKeys
|
||||
?: global?.excludeKeys
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).excludeKeys
|
||||
override val isEnableExcludeNonStringValue
|
||||
get() = this@create.isEnableExcludeNonStringValue
|
||||
?: selfBase?.isEnableExcludeNonStringValue
|
||||
?: global?.isEnableExcludeNonStringValue
|
||||
?: globalBase?.isEnableExcludeNonStringValue
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableExcludeNonStringValue
|
||||
override val isEnableTypeAutoConversion
|
||||
get() = this@create.isEnableTypeAutoConversion
|
||||
?: selfBase?.isEnableTypeAutoConversion
|
||||
?: global?.isEnableTypeAutoConversion
|
||||
?: globalBase?.isEnableTypeAutoConversion
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableTypeAutoConversion
|
||||
override val isEnableValueInterpolation
|
||||
get() = this@create.isEnableValueInterpolation
|
||||
?: selfBase?.isEnableValueInterpolation
|
||||
?: global?.isEnableValueInterpolation
|
||||
?: globalBase?.isEnableValueInterpolation
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableValueInterpolation
|
||||
override val generateLocationTypes
|
||||
get() = this@create.generateLocationTypes
|
||||
?: selfBase?.generateLocationTypes
|
||||
?: global?.generateLocationTypes
|
||||
?: globalBase?.generateLocationTypes
|
||||
?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).generateLocationTypes
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 [ISweetPropertyConfigs.IBuildScriptGenerateConfigs] 实例
|
||||
* @param name 名称
|
||||
* @param global 全局配置 - 默认 null
|
||||
* @param selfBase 自身继承配置 - 默认 null
|
||||
* @param globalBase 全局继承配置 - 默认 null
|
||||
* @return [ISweetPropertyConfigs.IBuildScriptGenerateConfigs]
|
||||
*/
|
||||
private fun SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtension.create(
|
||||
name: String,
|
||||
global: SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtension? = null,
|
||||
selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
|
||||
globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
|
||||
) = object : ISweetPropertyConfigs.IBuildScriptGenerateConfigs {
|
||||
override val name get() = name
|
||||
override val extensionName
|
||||
get() = this@create.extensionName.noBlank()
|
||||
?: global?.extensionName?.noBlank()
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).extensionName
|
||||
override val isEnable
|
||||
get() = this@create.isEnable
|
||||
?: selfBase?.isEnable
|
||||
?: global?.isEnable
|
||||
?: globalBase?.isEnable
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnable
|
||||
override val propertiesFileName
|
||||
get() = this@create.propertiesFileName.noBlank()
|
||||
?: global?.propertiesFileName?.noBlank()
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).propertiesFileName
|
||||
override val permanentKeyValues
|
||||
get() = this@create.permanentKeyValues
|
||||
?: global?.permanentKeyValues
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).permanentKeyValues
|
||||
override val excludeKeys
|
||||
get() = this@create.excludeKeys
|
||||
?: global?.excludeKeys
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).excludeKeys
|
||||
override val isEnableExcludeNonStringValue
|
||||
get() = this@create.isEnableExcludeNonStringValue
|
||||
?: selfBase?.isEnableExcludeNonStringValue
|
||||
?: global?.isEnableExcludeNonStringValue
|
||||
?: globalBase?.isEnableExcludeNonStringValue
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableExcludeNonStringValue
|
||||
override val isEnableTypeAutoConversion
|
||||
get() = this@create.isEnableTypeAutoConversion
|
||||
?: selfBase?.isEnableTypeAutoConversion
|
||||
?: global?.isEnableTypeAutoConversion
|
||||
?: globalBase?.isEnableTypeAutoConversion
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableTypeAutoConversion
|
||||
override val isEnableValueInterpolation
|
||||
get() = this@create.isEnableValueInterpolation
|
||||
?: selfBase?.isEnableValueInterpolation
|
||||
?: global?.isEnableValueInterpolation
|
||||
?: globalBase?.isEnableValueInterpolation
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableValueInterpolation
|
||||
override val generateLocationTypes
|
||||
get() = this@create.generateLocationTypes
|
||||
?: selfBase?.generateLocationTypes
|
||||
?: global?.generateLocationTypes
|
||||
?: globalBase?.generateLocationTypes
|
||||
?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).generateLocationTypes
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/25.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.config.proxy
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.generated.SweetPropertyProperties
|
||||
import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
|
||||
|
||||
/**
|
||||
* [SweetProperty] 配置类接口类
|
||||
*/
|
||||
internal interface ISweetPropertyConfigs {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 默认的生成目录路径
|
||||
*
|
||||
* "build/generated/[SweetPropertyProperties.PROJECT_MODULE_NAME]"
|
||||
*/
|
||||
internal const val DEFAULT_GENERATE_DIR_PATH = "build/generated/${SweetPropertyProperties.PROJECT_MODULE_NAME}"
|
||||
|
||||
/**
|
||||
* 默认的属性配置文件名称
|
||||
*
|
||||
* "gradle.properties"
|
||||
*/
|
||||
internal const val DEFAULT_PROPERTIES_FILE_NAME = "gradle.properties"
|
||||
|
||||
/**
|
||||
* 默认的构建脚本扩展方法名称
|
||||
*
|
||||
* "property"
|
||||
*/
|
||||
internal const val DEFAULT_EXTENSION_NAME = "property"
|
||||
|
||||
/**
|
||||
* 默认的生成位置类型数组
|
||||
*
|
||||
* arrayOf([GenerateLocationType.CURRENT_PROJECT], [GenerateLocationType.ROOT_PROJECT])
|
||||
*/
|
||||
internal val defaultGenerateLocationTypes = arrayOf(GenerateLocationType.CURRENT_PROJECT, GenerateLocationType.ROOT_PROJECT)
|
||||
}
|
||||
|
||||
/** 是否启用插件 */
|
||||
val isEnable: Boolean
|
||||
|
||||
/** 配置全部 */
|
||||
val global: ISubConfigs
|
||||
|
||||
/** 配置每个项目数组 */
|
||||
val projects: MutableMap<String, ISubConfigs>
|
||||
|
||||
/**
|
||||
* 子配置类接口类
|
||||
*/
|
||||
interface ISubConfigs {
|
||||
|
||||
/** 项目生成代码配置类接口 */
|
||||
val sourcesCode: ISourcesCodeGenerateConfigs
|
||||
|
||||
/** 构建脚本生成代码配置类接口 */
|
||||
val buildScript: IBuildScriptGenerateConfigs
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目生成代码配置类接口类
|
||||
*/
|
||||
interface ISourcesCodeGenerateConfigs : IBaseGenerateConfigs {
|
||||
|
||||
/** 自定义生成的目录路径 */
|
||||
val generateDirPath: String
|
||||
|
||||
/** 自定义生成的包名 */
|
||||
val packageName: String
|
||||
|
||||
/** 自定义生成的类名 */
|
||||
val className: String
|
||||
|
||||
/** 是否启用受限访问功能 */
|
||||
val isEnableRestrictedAccess: Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建脚本生成代码配置类接口类
|
||||
*/
|
||||
interface IBuildScriptGenerateConfigs : IBaseGenerateConfigs {
|
||||
|
||||
/** 自定义构建脚本扩展方法名称 */
|
||||
val extensionName: String
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用生成代码配置类接口类
|
||||
*/
|
||||
interface IBaseGenerateConfigs {
|
||||
|
||||
/** 名称 */
|
||||
val name: String
|
||||
|
||||
/** 是否为当前功能生成代码 */
|
||||
val isEnable: Boolean
|
||||
|
||||
/** 属性配置文件名称 */
|
||||
val propertiesFileName: String
|
||||
|
||||
/** 固定存在的属性键值数组 */
|
||||
val permanentKeyValues: MutableMap<String, Any>
|
||||
|
||||
/** 被排除的属性键值名称数组 */
|
||||
val excludeKeys: MutableList<Any>
|
||||
|
||||
/** 是否启用排除非字符串类型键值内容 */
|
||||
val isEnableExcludeNonStringValue: Boolean
|
||||
|
||||
/** 是否启用类型自动转换功能 */
|
||||
val isEnableTypeAutoConversion: Boolean
|
||||
|
||||
/** 是否启用键值内容插值功能 */
|
||||
val isEnableValueInterpolation: Boolean
|
||||
|
||||
/** 生成的位置类型数组 */
|
||||
val generateLocationTypes: Array<GenerateLocationType>
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/25.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.sweetproperty.plugin.config.type
|
||||
|
||||
/**
|
||||
* 生成位置类型定义类
|
||||
*/
|
||||
internal enum class GenerateLocationType {
|
||||
/** 当前项目 */
|
||||
CURRENT_PROJECT,
|
||||
|
||||
/** 根项目 */
|
||||
ROOT_PROJECT,
|
||||
|
||||
/** 全局 */
|
||||
GLOBAL,
|
||||
|
||||
/** 系统 */
|
||||
SYSTEM,
|
||||
|
||||
/** 系统环境变量 */
|
||||
SYSTEM_ENV
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.extension.accessors.proxy
|
||||
|
||||
/**
|
||||
* 扩展可访问 [Class] 定义空间接口
|
||||
*/
|
||||
internal interface IExtensionAccessors
|
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/25.
|
||||
*/
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate", "PropertyName")
|
||||
|
||||
package com.highcapable.sweetproperty.plugin.extension.dsl.configure
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.gradle.factory.isUnSafeExtName
|
||||
import com.highcapable.sweetproperty.plugin.config.factory.create
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.noEmpty
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
/**
|
||||
* [SweetProperty] 配置方法体实现类
|
||||
*/
|
||||
open class SweetPropertyConfigureExtension internal constructor() {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/** [SweetPropertyConfigureExtension] 扩展名称 */
|
||||
internal const val NAME = "sweetProperty"
|
||||
|
||||
/** 根项目标识名称 */
|
||||
private const val ROOT_PROJECT_TAG = "<ROOT_PROJECT>"
|
||||
}
|
||||
|
||||
/** 当前全局配置实例 */
|
||||
internal val globalConfigure = SubConfigureExtension()
|
||||
|
||||
/** 当前每个项目配置实例数组 */
|
||||
internal val projectConfigures = mutableMapOf<String, SubConfigureExtension>()
|
||||
|
||||
/** 当前项目 */
|
||||
@JvmField
|
||||
val CURRENT_PROJECT = "CURRENT_PROJECT"
|
||||
|
||||
/** 根项目 */
|
||||
@JvmField
|
||||
val ROOT_PROJECT = "ROOT_PROJECT"
|
||||
|
||||
/** 全局 (用户目录) */
|
||||
@JvmField
|
||||
val GLOBAL = "GLOBAL"
|
||||
|
||||
/** 系统 */
|
||||
@JvmField
|
||||
val SYSTEM = "SYSTEM"
|
||||
|
||||
/** 系统环境变量 */
|
||||
@JvmField
|
||||
val SYSTEM_ENV = "SYSTEM_ENV"
|
||||
|
||||
/**
|
||||
* 是否启用插件
|
||||
*
|
||||
* 默认启用 - 如果你想关闭插件 - 在这里设置就可以了
|
||||
*/
|
||||
var isEnable = true
|
||||
@JvmName("enable") set
|
||||
|
||||
/**
|
||||
* 配置全局
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun global(action: Action<SubConfigureExtension>) = action.execute(globalConfigure)
|
||||
|
||||
/**
|
||||
* 配置根项目
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun rootProject(action: Action<SubConfigureExtension>) = project(ROOT_PROJECT_TAG, action)
|
||||
|
||||
/**
|
||||
* 配置指定项目
|
||||
* @param name 项目完整名称
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun project(name: String, action: Action<SubConfigureExtension>) = action.execute(SubConfigureExtension().also { projectConfigures[name] = it })
|
||||
|
||||
/**
|
||||
* 子配置方法体实现类
|
||||
*/
|
||||
open inner class SubConfigureExtension internal constructor() {
|
||||
|
||||
/** 当前通用生成代码功能配置实例 */
|
||||
internal var allConfigure: BaseGenerateConfigureExtension? = null
|
||||
|
||||
/** 当前项目生成代码功能配置实例 */
|
||||
internal var sourcesCodeConfigure: SourcesCodeGenerateConfigureExtension? = null
|
||||
|
||||
/** 当前构建脚本生成代码功能配置实例 */
|
||||
internal var buildScriptConfigure: BuildScriptGenerateConfigureExtension? = null
|
||||
|
||||
/**
|
||||
* 配置通用生成代码功能
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun all(action: Action<BaseGenerateConfigureExtension>) {
|
||||
allConfigure = BaseGenerateConfigureExtension().also { action.execute(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置项目生成代码功能
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun sourcesCode(action: Action<SourcesCodeGenerateConfigureExtension>) {
|
||||
sourcesCodeConfigure = SourcesCodeGenerateConfigureExtension().also { action.execute(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置构建脚本生成代码功能
|
||||
* @param action 配置方法体
|
||||
*/
|
||||
fun buildScript(action: Action<BuildScriptGenerateConfigureExtension>) {
|
||||
buildScriptConfigure = BuildScriptGenerateConfigureExtension().also { action.execute(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目生成代码配置方法体实现类
|
||||
*/
|
||||
open inner class SourcesCodeGenerateConfigureExtension internal constructor() : BaseGenerateConfigureExtension() {
|
||||
|
||||
/**
|
||||
* 自定义生成的目录路径
|
||||
*
|
||||
* 你可以填写相对于当前项目的路径
|
||||
*
|
||||
* 默认为 [ISweetPropertyConfigs.DEFAULT_GENERATE_DIR_PATH]
|
||||
*/
|
||||
var generateDirPath = ""
|
||||
@JvmName("generateDirPath") set
|
||||
|
||||
/**
|
||||
* 自定义生成的包名
|
||||
*
|
||||
* Android 项目默认使用 "android" 配置方法块中的 "namespace"
|
||||
*
|
||||
* 普通的 Kotlin on Jvm 项目默认使用项目设置的 "project.group"
|
||||
*/
|
||||
var packageName = ""
|
||||
@JvmName("packageName") set
|
||||
|
||||
/**
|
||||
* 自定义生成的类名
|
||||
*
|
||||
* 默认使用当前项目的名称 + "Properties"
|
||||
*/
|
||||
var className = ""
|
||||
@JvmName("className") set
|
||||
|
||||
/**
|
||||
* 是否启用受限访问功能
|
||||
*
|
||||
* 默认不启用 - 启用后将为生成的类和方法添加 "internal" 修饰符
|
||||
*/
|
||||
var isEnableRestrictedAccess: Boolean? = null
|
||||
@JvmName("enableRestrictedAccess") set
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建脚本生成代码配置方法体实现类
|
||||
*/
|
||||
open inner class BuildScriptGenerateConfigureExtension internal constructor() : BaseGenerateConfigureExtension() {
|
||||
|
||||
/**
|
||||
* 自定义构建脚本扩展方法名称
|
||||
*
|
||||
* 默认为 [ISweetPropertyConfigs.DEFAULT_EXTENSION_NAME]
|
||||
*/
|
||||
var extensionName = ""
|
||||
@JvmName("extensionName") set
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用生成代码配置方法体实现类
|
||||
*/
|
||||
open inner class BaseGenerateConfigureExtension internal constructor() {
|
||||
|
||||
/** 当前固定存在的属性键值数组 */
|
||||
internal var permanentKeyValues: MutableMap<String, Any>? = null
|
||||
|
||||
/** 当前被排除的属性键值名称数组 */
|
||||
internal var excludeKeys: MutableList<Any>? = null
|
||||
|
||||
/** 当前生成位置类型数组 */
|
||||
internal var generateLocationTypes: Array<GenerateLocationType>? = null
|
||||
|
||||
/**
|
||||
* 是否为当前功能生成代码
|
||||
*
|
||||
* 默认启用
|
||||
*
|
||||
* [SourcesCodeGenerateConfigureExtension] 启用后将会为当前项目生成代码并添加到当前项目的 sourceSets 中
|
||||
*
|
||||
* [BuildScriptGenerateConfigureExtension] 启用后将会为构建脚本生成代码并为构建脚本生成扩展方法
|
||||
*
|
||||
* 在 [BuildScriptGenerateConfigureExtension] 中你可以使用 [BuildScriptGenerateConfigureExtension.extensionName] 来自定义构建脚本扩展方法名称
|
||||
*/
|
||||
var isEnable: Boolean? = null
|
||||
@JvmName("enable") set
|
||||
|
||||
/**
|
||||
* 设置属性配置文件名称
|
||||
*
|
||||
* 默认为 [ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME]
|
||||
*
|
||||
* - 注意:一般情况下不需要修改此设置 - 错误的文件名将导致获取到空键值内容
|
||||
*/
|
||||
var propertiesFileName = ""
|
||||
@JvmName("propertiesFileName") set
|
||||
|
||||
/**
|
||||
* 是否启用排除非字符串类型键值内容
|
||||
*
|
||||
* 默认启用 - 启用后将从属性键值中排除不是字符串类型的键值及内容
|
||||
*/
|
||||
var isEnableExcludeNonStringValue: Boolean? = null
|
||||
@JvmName("enableExcludeNonStringValue") set
|
||||
|
||||
/**
|
||||
* 是否启用类型自动转换功能
|
||||
*
|
||||
*
|
||||
* 默认启用 - 启用后将自动识别属性键值中的类型并转换为对应的类型
|
||||
*/
|
||||
var isEnableTypeAutoConversion: Boolean? = null
|
||||
@JvmName("enableTypeAutoConversion") set
|
||||
|
||||
/**
|
||||
* 是否启用键值内容插值功能
|
||||
*
|
||||
* 默认启用 - 启用后将自动识别属性键值内容中的 ${...} 内容并进行替换
|
||||
*
|
||||
* 注意:插值的内容仅会从当前 (当前配置文件) 属性键值列表进行查找
|
||||
*/
|
||||
var isEnableValueInterpolation: Boolean? = null
|
||||
@JvmName("enableValueInterpolation") set
|
||||
|
||||
/**
|
||||
* 设置固定存在的属性键值数组
|
||||
*
|
||||
* 在这里可以设置一些一定存在的键值 - 这些键值无论能否从属性键值中得到都会进行生成
|
||||
*
|
||||
* 这些键值在属性键值存在时使用属性键值的内容 - 不存在时使用这里设置的内容
|
||||
*
|
||||
* - 注意:属性键值名称不能存在特殊符号以及空格 - 否则可能会生成失败
|
||||
* @param pairs 键值数组
|
||||
*/
|
||||
@JvmName("-kotlin-dsl-only-permanentKeyValues-")
|
||||
fun permanentKeyValues(vararg pairs: Pair<String, Any>) {
|
||||
if (pairs.isEmpty()) SError.make("Permanent key-values must not be empty")
|
||||
if (pairs.any { it.first.isBlank() }) SError.make("Permanent key-values must not have blank contents")
|
||||
permanentKeyValues = mutableMapOf(*pairs)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置固定存在的属性键值数组 (Groovy 兼容方法)
|
||||
*
|
||||
* 在这里可以设置一些一定存在的键值 - 这些键值无论能否从属性键值中得到都会进行生成
|
||||
*
|
||||
* 这些键值在属性键值存在时使用属性键值的内容 - 不存在时使用这里设置的内容
|
||||
*
|
||||
* - 注意:属性键值名称不能存在特殊符号以及空格 - 否则可能会生成失败
|
||||
* @param keyValues 键值数组
|
||||
*/
|
||||
fun permanentKeyValues(keyValues: Map<String, Any>) {
|
||||
if (keyValues.isEmpty()) SError.make("Permanent key-values must not be empty")
|
||||
if (keyValues.any { it.key.isBlank() }) SError.make("Permanent key-values must not have blank contents")
|
||||
permanentKeyValues = keyValues.toMutableMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要排除的属性键值名称数组
|
||||
*
|
||||
* 在这里可以设置一些你希望从已知的属性键值中排除的键值名称
|
||||
*
|
||||
* 这些键值在属性键值存在它们时被排除 - 不会出现在生成的代码中
|
||||
*
|
||||
* - 注意:如果你排除了 [permanentKeyValues] 中设置的键值 -
|
||||
* 那么它们只会变为你设置的初始键值内容并继续保持存在
|
||||
* @param keys 键值名称数组 - 你可以传入 [Regex] 或使用 [String.toRegex] 以使用正则功能
|
||||
*/
|
||||
fun excludeKeys(vararg keys: Any) {
|
||||
if (keys.isEmpty()) SError.make("Exclude keys must not be empty")
|
||||
if (keys.any { it.toString().isBlank() }) SError.make("Exclude keys must not have blank contents")
|
||||
excludeKeys = keys.distinct().toMutableList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置从何处生成属性键值
|
||||
*
|
||||
* 默认为 [ISweetPropertyConfigs.defaultGenerateLocationTypes]
|
||||
*
|
||||
* 你可以使用以下类型来进行设置
|
||||
*
|
||||
* - [CURRENT_PROJECT]
|
||||
* - [ROOT_PROJECT]
|
||||
* - [GLOBAL]
|
||||
* - [SYSTEM]
|
||||
* - [SYSTEM_ENV]
|
||||
*
|
||||
* [SweetProperty] 将从你设置的生成位置依次生成属性键值 - 生成位置的顺序跟随你设置的顺序决定
|
||||
*
|
||||
* - 风险提示:[GLOBAL]、[SYSTEM]、[SYSTEM_ENV] 可能存在密钥和证书 - 请小心管理生成的代码
|
||||
* @param types 位置类型数组
|
||||
*/
|
||||
fun generateFrom(vararg types: String) {
|
||||
generateLocationTypes = mutableListOf<GenerateLocationType>().apply {
|
||||
types.toList().noEmpty()?.forEach {
|
||||
add(when (it) {
|
||||
CURRENT_PROJECT -> GenerateLocationType.CURRENT_PROJECT
|
||||
ROOT_PROJECT -> GenerateLocationType.ROOT_PROJECT
|
||||
GLOBAL -> GenerateLocationType.GLOBAL
|
||||
SYSTEM -> GenerateLocationType.SYSTEM
|
||||
SYSTEM_ENV -> GenerateLocationType.SYSTEM_ENV
|
||||
else -> SError.make("Invalid generate location type \"$it\"")
|
||||
})
|
||||
} ?: SError.make("Generate location types must not be empty")
|
||||
}.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 [ISweetPropertyConfigs]
|
||||
* @param settings 当前设置
|
||||
* @return [ISweetPropertyConfigs]
|
||||
*/
|
||||
internal fun build(settings: Settings): ISweetPropertyConfigs {
|
||||
/**
|
||||
* 检查是否以字母开头
|
||||
* @param description 描述
|
||||
*/
|
||||
fun String.checkingStartWithLetter(description: String) {
|
||||
firstOrNull()?.also {
|
||||
if (it !in 'A'..'Z' && it !in 'a'..'z') SError.make("$description name \"$this\" must start with a letter")
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查合法包名 */
|
||||
fun String.checkingValidPackageName() {
|
||||
if (isNotBlank() && matches("^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$".toRegex()).not())
|
||||
SError.make("Invalid package name \"$this\"")
|
||||
}
|
||||
|
||||
/** 检查合法类名 */
|
||||
fun String.checkingValidClassName() {
|
||||
if (isNotBlank() && matches("^[a-zA-Z][a-zA-Z0-9_]*$".toRegex()).not())
|
||||
SError.make("Invalid class name \"$this\"")
|
||||
}
|
||||
|
||||
/** 检查合法扩展方法名称 */
|
||||
fun String.checkingValidExtensionName() {
|
||||
checkingStartWithLetter(description = "Extension")
|
||||
if (isNotBlank() && isUnSafeExtName()) SError.make("This name \"$this\" is a Gradle built-in extension")
|
||||
}
|
||||
|
||||
/** 检查名称是否合法 */
|
||||
fun SweetPropertyConfigureExtension.SubConfigureExtension.checkingNames() {
|
||||
sourcesCodeConfigure?.packageName?.checkingValidPackageName()
|
||||
sourcesCodeConfigure?.className?.checkingValidClassName()
|
||||
buildScriptConfigure?.extensionName?.checkingValidExtensionName()
|
||||
}
|
||||
val currentEnable = isEnable
|
||||
globalConfigure.checkingNames()
|
||||
val currentGlobal = globalConfigure.create()
|
||||
val currentProjects = mutableMapOf<String, ISweetPropertyConfigs.ISubConfigs>()
|
||||
val rootName = settings.rootProject.name
|
||||
if (projectConfigures.containsKey(rootName))
|
||||
SError.make("This name \"$rootName\" is a root project, please use rootProject function to configure it, not project(\"$rootName\")")
|
||||
if (projectConfigures.containsKey(ROOT_PROJECT_TAG)) {
|
||||
projectConfigures[rootName] = projectConfigures[ROOT_PROJECT_TAG] ?: SError.make("Internal error")
|
||||
projectConfigures.remove(ROOT_PROJECT_TAG)
|
||||
}
|
||||
projectConfigures.forEach { (name, subConfigure) ->
|
||||
name.checkingStartWithLetter(description = "Project")
|
||||
subConfigure.checkingNames()
|
||||
currentProjects[name] = subConfigure.create(name, globalConfigure)
|
||||
}; return object : ISweetPropertyConfigs {
|
||||
override val isEnable get() = currentEnable
|
||||
override val global get() = currentGlobal
|
||||
override val projects get() = currentProjects
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.generator
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.generated.SweetPropertyProperties
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.extension.accessors.proxy.IExtensionAccessors
|
||||
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
|
||||
import com.highcapable.sweetproperty.plugin.generator.factory.parseTypedValue
|
||||
import com.highcapable.sweetproperty.utils.capitalize
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.firstNumberToLetter
|
||||
import com.highcapable.sweetproperty.utils.toNonJavaName
|
||||
import com.highcapable.sweetproperty.utils.uncapitalize
|
||||
import com.highcapable.sweetproperty.utils.uppercamelcase
|
||||
import com.squareup.javapoet.ClassName
|
||||
import com.squareup.javapoet.FieldSpec
|
||||
import com.squareup.javapoet.JavaFile
|
||||
import com.squareup.javapoet.MethodSpec
|
||||
import com.squareup.javapoet.TypeSpec
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import javax.lang.model.element.Modifier
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* 属性键值可访问 [Class] 生成实现类
|
||||
*/
|
||||
internal class PropertiesAccessorsGenerator {
|
||||
|
||||
private companion object {
|
||||
|
||||
/** 生成的 [Class] 所在包名 */
|
||||
private const val ACCESSORS_PACKAGE_NAME = "${SweetPropertyProperties.PROJECT_GROUP_NAME}.plugin.extension.accessors.generated"
|
||||
|
||||
/** 生成的 [Class] 后缀名 */
|
||||
private const val CLASS_SUFFIX_NAME = "Accessors"
|
||||
|
||||
/** 生成的首位 [Class] 后缀名 */
|
||||
private const val TOP_CLASS_SUFFIX_NAME = "Properties$CLASS_SUFFIX_NAME"
|
||||
|
||||
/** 标识首位生成的 [Class] TAG */
|
||||
private const val TOP_SUCCESSIVE_NAME = "_top_successive_name"
|
||||
}
|
||||
|
||||
/** 当前配置实例 */
|
||||
private var configs by Delegates.notNull<ISweetPropertyConfigs.IBuildScriptGenerateConfigs>()
|
||||
|
||||
/** 生成的属性键值 [Class] 构建器数组 */
|
||||
private val classSpecs = mutableMapOf<String, TypeSpec.Builder>()
|
||||
|
||||
/** 生成的属性键值构造方法构建器数组 */
|
||||
private val constructorSpecs = mutableMapOf<String, MethodSpec.Builder>()
|
||||
|
||||
/** 生成的属性键值预添加的构造方法名称数组 */
|
||||
private val preAddConstructorSpecNames = mutableListOf<Pair<String, String>>()
|
||||
|
||||
/** 生成的属性键值 [Class] 扩展类名数组 */
|
||||
private val memoryExtensionClasses = mutableMapOf<String, String>()
|
||||
|
||||
/** 生成的属性键值连续名称记录数组 */
|
||||
private val grandSuccessiveNames = mutableListOf<String>()
|
||||
|
||||
/** 生成的属性键值连续名称重复次数数组 */
|
||||
private val grandSuccessiveDuplicateIndexs = mutableMapOf<String, Int>()
|
||||
|
||||
/** 生成的属性键值不重复 TAG 数组 */
|
||||
private val usedSuccessiveTags = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* 不重复调用
|
||||
* @param tags 当前 TAG 数组
|
||||
* @param block 执行的方法块
|
||||
*/
|
||||
private inline fun noRepeated(vararg tags: String, block: () -> Unit) {
|
||||
val allTag = tags.joinToString("-")
|
||||
if (usedSuccessiveTags.contains(allTag).not()) block()
|
||||
usedSuccessiveTags.add(allTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串首字母大写并添加 [CLASS_SUFFIX_NAME] 后缀
|
||||
* @return [String]
|
||||
*/
|
||||
private fun String.capitalized() = "${capitalize()}$CLASS_SUFFIX_NAME"
|
||||
|
||||
/**
|
||||
* 字符串首字母小写并添加 [CLASS_SUFFIX_NAME] 后缀
|
||||
* @return [String]
|
||||
*/
|
||||
private fun String.uncapitalized() = "${uncapitalize()}$CLASS_SUFFIX_NAME"
|
||||
|
||||
/**
|
||||
* 字符串类名转换为 [ClassName]
|
||||
* @param packageName 包名 - 默认空
|
||||
* @return [ClassName]
|
||||
*/
|
||||
private fun String.asClassType(packageName: String = "") = ClassName.get(packageName, this)
|
||||
|
||||
/**
|
||||
* 通过 [TypeSpec] 创建 [JavaFile]
|
||||
* @return [JavaFile]
|
||||
*/
|
||||
private fun TypeSpec.createJavaFile(packageName: String) = JavaFile.builder(packageName, this).build()
|
||||
|
||||
/**
|
||||
* 创建通用构建器描述类
|
||||
* @param name 名称
|
||||
* @param accessorsName 接续名 - 默认空
|
||||
* @param isInner 是否为内部类 - 默认是
|
||||
* @return [TypeSpec.Builder]
|
||||
*/
|
||||
private fun createClassSpec(name: String, accessorsName: String = "", isInner: Boolean = true) =
|
||||
TypeSpec.classBuilder(if (isInner) name.capitalized() else name).apply {
|
||||
if (isInner) {
|
||||
addJavadoc("The \"$accessorsName\" accessors")
|
||||
addSuperinterface(IExtensionAccessors::class.java)
|
||||
addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
} else {
|
||||
addJavadoc(
|
||||
"""
|
||||
This class is generated by ${SweetProperty.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
|
||||
<br/>
|
||||
The content here is automatically generated according to the properties of your projects
|
||||
<br/>
|
||||
You can visit <a href="${SweetProperty.PROJECT_URL}">here</a> for more help
|
||||
""".trimIndent()
|
||||
)
|
||||
addModifiers(Modifier.PUBLIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通用构造方法构建器描述类
|
||||
* @return [MethodSpec.Builder]
|
||||
*/
|
||||
private fun createConstructorSpec() = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
|
||||
|
||||
/**
|
||||
* 向通用构建器描述类添加变量
|
||||
* @param accessorsName 接续名
|
||||
* @param className 类名
|
||||
* @return [TypeSpec.Builder]
|
||||
*/
|
||||
private fun TypeSpec.Builder.addSuccessiveField(accessorsName: String, className: String) = addField(
|
||||
FieldSpec.builder(className.capitalized().asClassType(), className.uncapitalized(), Modifier.PRIVATE, Modifier.FINAL)
|
||||
.addJavadoc("Create the \"$accessorsName\" accessors")
|
||||
.build()
|
||||
)
|
||||
|
||||
/**
|
||||
* 向通用构建器描述类添加方法
|
||||
* @param accessorsName 接续名
|
||||
* @param methodName 方法名
|
||||
* @param className 类名
|
||||
* @return [TypeSpec.Builder]
|
||||
*/
|
||||
private fun TypeSpec.Builder.addSuccessiveMethod(accessorsName: String, methodName: String, className: String) =
|
||||
addMethod(
|
||||
MethodSpec.methodBuilder("get${methodName.capitalize()}")
|
||||
.addJavadoc("Resolve the \"$accessorsName\" accessors")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.returns(className.capitalized().asClassType())
|
||||
.addStatement("return ${className.uncapitalized()}")
|
||||
.build()
|
||||
)
|
||||
|
||||
/**
|
||||
* 向通用构建器描述类添加最终键值方法
|
||||
* @param accessorsName 接续名
|
||||
* @param methodName 方法名
|
||||
* @param value 键值内容
|
||||
* @return [TypeSpec.Builder]
|
||||
*/
|
||||
private fun TypeSpec.Builder.addFinalValueMethod(accessorsName: String, methodName: String, value: Any) =
|
||||
addMethod(
|
||||
MethodSpec.methodBuilder("get${methodName.capitalize()}")
|
||||
.addJavadoc("Resolve the \"$accessorsName\" value \"$value\"")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL).apply {
|
||||
val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
|
||||
returns(typedValue.first.java)
|
||||
addStatement("return ${typedValue.second}")
|
||||
}.build()
|
||||
)
|
||||
|
||||
/**
|
||||
* 向通用构造方法构建器描述类添加变量实例化语句
|
||||
* @param className 类名
|
||||
* @return [MethodSpec.Builder]
|
||||
*/
|
||||
private fun MethodSpec.Builder.addSuccessiveStatement(className: String) =
|
||||
addStatement("${className.uncapitalized()} = new ${className.capitalized()}()")
|
||||
|
||||
/**
|
||||
* 获取、创建通用构建器描述类
|
||||
* @param name 名称
|
||||
* @param accessorsName 接续名 - 默认空
|
||||
* @return [TypeSpec.Builder]
|
||||
*/
|
||||
private fun getOrCreateClassSpec(name: String, accessorsName: String = "") =
|
||||
classSpecs[name] ?: createClassSpec(name, accessorsName).also { classSpecs[name] = it }
|
||||
|
||||
/**
|
||||
* 获取、创建通用构造方法构建器描述类
|
||||
* @param name 名称
|
||||
* @return [MethodSpec.Builder]
|
||||
*/
|
||||
private fun getOrCreateConstructorSpec(name: String) = constructorSpecs[name] ?: createConstructorSpec().also { constructorSpecs[name] = it }
|
||||
|
||||
/**
|
||||
* 解析并生成所有类的构建器 (核心方法)
|
||||
*
|
||||
* 解析开始前需要确保已调用 [createTopClassSpec] 并调用一次 [clearGeneratedData] 防止数据混淆
|
||||
*
|
||||
* 解析完成后需要调用 [releaseParseTypeSpec] 完成解析
|
||||
* @param successiveName 连续的名称
|
||||
* @param value 键值内容
|
||||
*/
|
||||
private fun parseTypeSpec(successiveName: String, value: Any) {
|
||||
/**
|
||||
* 获取生成的属性键值连续名称重复次数
|
||||
* @return [Int]
|
||||
*/
|
||||
fun String.duplicateGrandSuccessiveIndex() = lowercase().let { name ->
|
||||
if (grandSuccessiveDuplicateIndexs.contains(name)) {
|
||||
grandSuccessiveDuplicateIndexs[name] = (grandSuccessiveDuplicateIndexs[name] ?: 1) + 1
|
||||
grandSuccessiveDuplicateIndexs[name] ?: 2
|
||||
} else 2.also { grandSuccessiveDuplicateIndexs[name] = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 (拆分) 名称到数组
|
||||
*
|
||||
* 形如 "com.mytest" → "ComMytest" → "mytest"
|
||||
* @return [List]<[Triple]<[String], [String], [String]>>
|
||||
*/
|
||||
fun String.parseSuccessiveNames(): List<Triple<String, String, String>> {
|
||||
var grandAcccessorsName = ""
|
||||
var grandSuccessiveName = ""
|
||||
val successiveNames = mutableListOf<Triple<String, String, String>>()
|
||||
val splitNames = replace(".", "|").replace("-", "|")
|
||||
.replace("_", "|").replace(" ", "_")
|
||||
.split("|").dropWhile { it.isBlank() }
|
||||
.ifEmpty { listOf(this) }
|
||||
splitNames.forEach { eachName ->
|
||||
val name = eachName.capitalize().toNonJavaName().firstNumberToLetter()
|
||||
grandAcccessorsName += if (grandAcccessorsName.isNotBlank()) ".$eachName" else eachName
|
||||
grandSuccessiveName += name
|
||||
if (grandSuccessiveNames.any { it != grandSuccessiveName && it.lowercase() == grandSuccessiveName.lowercase() })
|
||||
grandSuccessiveName += duplicateGrandSuccessiveIndex().toString()
|
||||
grandSuccessiveNames.add(grandSuccessiveName)
|
||||
successiveNames.add(Triple(grandAcccessorsName, grandSuccessiveName, name))
|
||||
}; return successiveNames.distinct()
|
||||
}
|
||||
val successiveNames = successiveName.parseSuccessiveNames()
|
||||
successiveNames.forEachIndexed { index, (accessorsName, className, methodName) ->
|
||||
val nextItem = successiveNames.getOrNull(index + 1)
|
||||
val lastItem = successiveNames.getOrNull(successiveNames.lastIndex)
|
||||
val nextAccessorsName = nextItem?.first ?: ""
|
||||
val nextClassName = nextItem?.second ?: ""
|
||||
val nextMethodName = nextItem?.third ?: ""
|
||||
val lastMethodName = lastItem?.third ?: ""
|
||||
val isPreLastIndex = index == successiveNames.lastIndex - 1
|
||||
if (successiveNames.size == 1) getOrCreateClassSpec(TOP_SUCCESSIVE_NAME).addFinalValueMethod(successiveName, methodName, value)
|
||||
if (index == successiveNames.lastIndex) return@forEachIndexed
|
||||
if (index == 0) noRepeated(TOP_SUCCESSIVE_NAME, methodName, className) {
|
||||
getOrCreateClassSpec(TOP_SUCCESSIVE_NAME, accessorsName)
|
||||
.addSuccessiveField(accessorsName, className)
|
||||
.addSuccessiveMethod(accessorsName, methodName, className)
|
||||
getOrCreateConstructorSpec(TOP_SUCCESSIVE_NAME).addSuccessiveStatement(className)
|
||||
}
|
||||
noRepeated(className, nextMethodName, nextClassName) {
|
||||
getOrCreateClassSpec(className, accessorsName).apply {
|
||||
if (isPreLastIndex.not()) {
|
||||
addSuccessiveField(nextAccessorsName, nextClassName)
|
||||
addSuccessiveMethod(nextAccessorsName, nextMethodName, nextClassName)
|
||||
} else addFinalValueMethod(successiveName, lastMethodName, value)
|
||||
}
|
||||
if (isPreLastIndex.not()) preAddConstructorSpecNames.add(className to nextClassName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 完成生成所有类的构建器 (释放) */
|
||||
private fun releaseParseTypeSpec() =
|
||||
preAddConstructorSpecNames.onEach { (topClassName, innerClassName) ->
|
||||
getOrCreateConstructorSpec(topClassName)?.addSuccessiveStatement(innerClassName)
|
||||
}.clear()
|
||||
|
||||
/**
|
||||
* 解析并生成所有类的构建器
|
||||
* @return [TypeSpec]
|
||||
*/
|
||||
private fun buildTypeSpec(): TypeSpec {
|
||||
classSpecs.forEach { (name, typeSpec) ->
|
||||
constructorSpecs[name]?.build()?.let { typeSpec.addMethod(it) }
|
||||
if (name != TOP_SUCCESSIVE_NAME) classSpecs[TOP_SUCCESSIVE_NAME]?.addType(typeSpec.build())
|
||||
}; return classSpecs[TOP_SUCCESSIVE_NAME]?.build() ?: SError.make("Merge accessors classes failed")
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建首位构建器
|
||||
* @param configs 当前配置
|
||||
* @throws IllegalStateException 如果名称为空
|
||||
*/
|
||||
private fun createTopClassSpec(configs: ISweetPropertyConfigs.IBuildScriptGenerateConfigs) {
|
||||
if (configs.name.isBlank()) SError.make("Class name cannot be empty or blank")
|
||||
this.configs = configs
|
||||
val topClassName = "${configs.name.replace(":", "_").uppercamelcase()}$TOP_CLASS_SUFFIX_NAME"
|
||||
memoryExtensionClasses[configs.name] = "$ACCESSORS_PACKAGE_NAME.$topClassName"
|
||||
classSpecs[TOP_SUCCESSIVE_NAME] = createClassSpec(topClassName, isInner = false)
|
||||
constructorSpecs[TOP_SUCCESSIVE_NAME] = createConstructorSpec()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有已生成的数据
|
||||
* @param isClearAll 是否全部清空 - 包括添加的 [memoryExtensionClasses] - 默认否
|
||||
*/
|
||||
private fun clearGeneratedData(isClearAll: Boolean = false) {
|
||||
classSpecs.clear()
|
||||
constructorSpecs.clear()
|
||||
preAddConstructorSpecNames.clear()
|
||||
grandSuccessiveNames.clear()
|
||||
grandSuccessiveDuplicateIndexs.clear()
|
||||
usedSuccessiveTags.clear()
|
||||
if (isClearAll) memoryExtensionClasses.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 [JavaFile] 数组
|
||||
*
|
||||
* - 注意:[allConfigs] 与 [allKeyValues] 数量必须相等
|
||||
* @param allConfigs 全部配置实例
|
||||
* @param allKeyValues 全部键值数组
|
||||
* @return [MutableList]<[JavaFile]>
|
||||
* @throws IllegalStateException 如果生成失败
|
||||
*/
|
||||
internal fun build(
|
||||
allConfigs: MutableList<ISweetPropertyConfigs.IBuildScriptGenerateConfigs>,
|
||||
allKeyValues: MutableList<PropertyMap>
|
||||
) = runCatching {
|
||||
val files = mutableListOf<JavaFile>()
|
||||
if (allConfigs.size != allKeyValues.size) SError.make("Invalid build arguments")
|
||||
if (allConfigs.isEmpty()) return@runCatching files
|
||||
clearGeneratedData(isClearAll = true)
|
||||
allConfigs.forEachIndexed { index, configs ->
|
||||
val keyValues = allKeyValues[index]
|
||||
clearGeneratedData()
|
||||
createTopClassSpec(configs)
|
||||
keyValues.forEach { (key, value) ->
|
||||
parseTypeSpec(key, value)
|
||||
releaseParseTypeSpec()
|
||||
}; files.add(buildTypeSpec().createJavaFile(ACCESSORS_PACKAGE_NAME))
|
||||
}; files
|
||||
}.getOrElse { SError.make("Failed to generated accessors classes\n$it") }
|
||||
|
||||
/**
|
||||
* 获取参与编译的 Stub [JavaFile] 数组
|
||||
* @return [List]<[JavaFile]>
|
||||
*/
|
||||
internal val compileStubFiles get(): List<JavaFile> {
|
||||
val stubFiles = mutableListOf<JavaFile>()
|
||||
val iExtensionAccessorsFile =
|
||||
TypeSpec.interfaceBuilder(IExtensionAccessors::class.java.simpleName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.build().createJavaFile(IExtensionAccessors::class.java.packageName)
|
||||
stubFiles.add(iExtensionAccessorsFile)
|
||||
return stubFiles
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展功能预置 [Class]
|
||||
* @param name 名称
|
||||
* @return [String]
|
||||
* @throws IllegalStateException 如果 [Class] 不存在
|
||||
*/
|
||||
internal fun propertiesClass(name: String) = memoryExtensionClasses[name] ?: SError.make("Could not found class \"$name\"")
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.generator
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
|
||||
import com.highcapable.sweetproperty.plugin.generator.factory.parseTypedValue
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.firstNumberToLetter
|
||||
import com.highcapable.sweetproperty.utils.underscore
|
||||
import com.squareup.kotlinpoet.FileSpec
|
||||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.PropertySpec
|
||||
import com.squareup.kotlinpoet.TypeSpec
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 属性键值代码生成实现类
|
||||
*/
|
||||
internal class PropertiesSourcesGenerator {
|
||||
|
||||
/**
|
||||
* 生成 [FileSpec]
|
||||
* @param configs 当前配置实例
|
||||
* @param keyValues 键值数组
|
||||
* @param packageName 包名
|
||||
* @param className 类名
|
||||
* @return [FileSpec]
|
||||
* @throws IllegalStateException 如果生成失败
|
||||
*/
|
||||
internal fun build(
|
||||
configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs,
|
||||
keyValues: PropertyMap,
|
||||
packageName: String,
|
||||
className: String
|
||||
) = runCatching {
|
||||
FileSpec.builder(packageName, className).apply {
|
||||
addType(TypeSpec.objectBuilder(className).apply {
|
||||
addKdoc(
|
||||
"""
|
||||
This class is generated by ${SweetProperty.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
|
||||
|
||||
The content here is automatically generated according to the properties of your projects
|
||||
|
||||
You can visit [here](${SweetProperty.PROJECT_URL}) for more help
|
||||
""".trimIndent()
|
||||
)
|
||||
if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
|
||||
keyValues.forEach { (key, value) ->
|
||||
val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
|
||||
addProperty(PropertySpec.builder(key.firstNumberToLetter().underscore(), typedValue.first).apply {
|
||||
addKdoc("Resolve the \"$key\" value \"$value\"")
|
||||
if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
|
||||
addModifiers(KModifier.CONST)
|
||||
initializer(typedValue.second.toKotlinPoetSpace())
|
||||
}.build())
|
||||
}
|
||||
}.build())
|
||||
}.build()
|
||||
}.getOrElse { SError.make("Failed to generated Kotlin file\n$it") }
|
||||
|
||||
/**
|
||||
* 转换到 KotlinPoet 声明的空格
|
||||
* @return [String]
|
||||
*/
|
||||
private fun String.toKotlinPoetSpace() = replace(" ", "·")
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.generator.factory
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/** 属性键值数组类型定义 */
|
||||
internal typealias PropertyMap = MutableMap<String, Any>
|
||||
|
||||
/**
|
||||
* 解析到键值内容类型
|
||||
* @param isAutoConversion 是否自动转换类型
|
||||
* @return [Pair]<[KClass], [String]>
|
||||
*/
|
||||
internal fun Any.parseTypedValue(isAutoConversion: Boolean): Pair<KClass<*>, String> {
|
||||
val valueString = toString()
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
if (isAutoConversion.not()) return Pair(String::class, "\"$valueString\"")
|
||||
val typeSpec = when {
|
||||
valueString.trim().toIntOrNull() != null -> Int::class
|
||||
valueString.trim().toLongOrNull() != null -> Long::class
|
||||
valueString.trim().toDoubleOrNull() != null -> Double::class
|
||||
valueString.trim().toFloatOrNull() != null -> Float::class
|
||||
valueString.trim() == "true" || valueString.trim() == "false" -> Boolean::class
|
||||
else -> String::class
|
||||
}; return Pair(typeSpec, if (typeSpec == String::class) "\"$valueString\"" else valueString.let {
|
||||
if (typeSpec == Long::class && it.endsWith("L").not()) "${it}L" else it
|
||||
})
|
||||
}
|
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/30.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.plugin.helper
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
import com.highcapable.sweetproperty.generated.SweetPropertyProperties
|
||||
import com.highcapable.sweetproperty.gradle.entity.ProjectDescriptor
|
||||
import com.highcapable.sweetproperty.gradle.factory.addDependencyToBuildScript
|
||||
import com.highcapable.sweetproperty.gradle.factory.fullName
|
||||
import com.highcapable.sweetproperty.gradle.factory.get
|
||||
import com.highcapable.sweetproperty.gradle.factory.getOrCreate
|
||||
import com.highcapable.sweetproperty.gradle.factory.hasExtension
|
||||
import com.highcapable.sweetproperty.gradle.factory.loadBuildScriptClass
|
||||
import com.highcapable.sweetproperty.plugin.config.factory.with
|
||||
import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
|
||||
import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
|
||||
import com.highcapable.sweetproperty.plugin.generator.PropertiesAccessorsGenerator
|
||||
import com.highcapable.sweetproperty.plugin.generator.PropertiesSourcesGenerator
|
||||
import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
|
||||
import com.highcapable.sweetproperty.utils.camelcase
|
||||
import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
|
||||
import com.highcapable.sweetproperty.utils.code.factory.compile
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.flatted
|
||||
import com.highcapable.sweetproperty.utils.hasInterpolation
|
||||
import com.highcapable.sweetproperty.utils.isEmpty
|
||||
import com.highcapable.sweetproperty.utils.noBlank
|
||||
import com.highcapable.sweetproperty.utils.noEmpty
|
||||
import com.highcapable.sweetproperty.utils.parseFileSeparator
|
||||
import com.highcapable.sweetproperty.utils.replaceInterpolation
|
||||
import com.highcapable.sweetproperty.utils.uppercamelcase
|
||||
import org.gradle.api.DomainObjectCollection
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.initialization.Settings
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* 属性键值部署工具类
|
||||
*/
|
||||
internal object PropertiesDeployHelper {
|
||||
|
||||
/** 属性键值可访问 [Class] 标识名称 */
|
||||
private const val ACCESSORS_NAME = "properties-accessors"
|
||||
|
||||
/** 属性键值默认生成包名 */
|
||||
private const val DEFAULT_PACKAGE_NAME = "${SweetPropertyProperties.PROJECT_GROUP_NAME}.defaultproperties"
|
||||
|
||||
/** 当前配置实例 */
|
||||
private var configs by Delegates.notNull<ISweetPropertyConfigs>()
|
||||
|
||||
/** 当前缓存的属性键值数组 (初始化装载) */
|
||||
private var cachedSettingsProperties = mutableListOf<PropertyMap>()
|
||||
|
||||
/** 当前缓存的属性键值数组 (每个项目) */
|
||||
private var cachedProjectProperties = mutableMapOf<String, PropertyMap>()
|
||||
|
||||
/** 上次修改的 Hash Code */
|
||||
private var lastModifiedHashCode = 0
|
||||
|
||||
/** 配置是否已被修改 */
|
||||
private var isConfigsModified = true
|
||||
|
||||
/** 属性键值可访问 [Class] 生成目录 */
|
||||
private var accessorsDir by Delegates.notNull<File>()
|
||||
|
||||
/** 属性键值可访问 [Class] 虚拟依赖数据 */
|
||||
private val accessorsPomData = MavenPomData(SweetPropertyProperties.PROJECT_GROUP_NAME, ACCESSORS_NAME, SweetProperty.VERSION)
|
||||
|
||||
/** 属性键值可访问 [Class] 生成实例 */
|
||||
private val accessorsGenerator = PropertiesAccessorsGenerator()
|
||||
|
||||
/** 属性键值代码生成实例 */
|
||||
private val sourcesGenerator = PropertiesSourcesGenerator()
|
||||
|
||||
/**
|
||||
* 装载并初始化
|
||||
* @param settings 当前设置
|
||||
* @param configs 当前配置
|
||||
*/
|
||||
internal fun initialize(settings: Settings, configs: ISweetPropertyConfigs) {
|
||||
this.configs = configs
|
||||
checkingConfigsModified(settings)
|
||||
if (configs.isEnable.not()) return
|
||||
generatedAccessors(settings)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始处理
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
internal fun resolve(rootProject: Project) {
|
||||
if (configs.isEnable.not()) return
|
||||
resolveAccessors(rootProject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始部署
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
internal fun deploy(rootProject: Project) {
|
||||
if (configs.isEnable.not()) return
|
||||
deployAccessors(rootProject)
|
||||
deploySourcesCode(rootProject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置是否已被修改
|
||||
* @param settings 当前设置
|
||||
*/
|
||||
private fun checkingConfigsModified(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
|
||||
isConfigsModified = gradleHashCode == -1 || lastModifiedHashCode != gradleHashCode
|
||||
lastModifiedHashCode = gradleHashCode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成构建脚本代码
|
||||
* @param settings 当前设置
|
||||
*/
|
||||
private fun generatedAccessors(settings: Settings) {
|
||||
accessorsDir = generatedAccessorsDir(settings)
|
||||
val allConfigs = mutableListOf<ISweetPropertyConfigs.IBuildScriptGenerateConfigs>()
|
||||
val allProperties = mutableListOf<PropertyMap>()
|
||||
if (configs.global.buildScript.isEnable) {
|
||||
allProperties.add(generatedProperties(configs.global.buildScript, ProjectDescriptor.create(settings)))
|
||||
allConfigs.add(configs.global.buildScript)
|
||||
}
|
||||
configs.projects.forEach { (name, subConfigs) ->
|
||||
if (subConfigs.buildScript.isEnable.not()) return@forEach
|
||||
allProperties.add(generatedProperties(subConfigs.buildScript, ProjectDescriptor.create(settings, name)))
|
||||
allConfigs.add(subConfigs.buildScript)
|
||||
}
|
||||
if (isConfigsModified.not() && allProperties == cachedSettingsProperties && accessorsDir.isEmpty().not()) return
|
||||
cachedSettingsProperties = allProperties
|
||||
accessorsGenerator.build(allConfigs, allProperties).compile(accessorsPomData, accessorsDir.absolutePath, accessorsGenerator.compileStubFiles)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理构建脚本代码
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
private fun resolveAccessors(rootProject: Project) {
|
||||
if (accessorsDir.isEmpty().not()) rootProject.addDependencyToBuildScript(accessorsDir.absolutePath, accessorsPomData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 部署构建脚本代码
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
private fun deployAccessors(rootProject: Project) {
|
||||
/** 部署扩展方法 */
|
||||
fun Project.deploy() {
|
||||
val configs = configs.with(this).buildScript
|
||||
if (configs.isEnable.not()) return
|
||||
getOrCreate(configs.extensionName.camelcase(), loadBuildScriptClass(accessorsGenerator.propertiesClass(configs.name)))
|
||||
}
|
||||
rootProject.deploy()
|
||||
rootProject.subprojects.forEach { it.deploy() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 部署项目代码
|
||||
* @param rootProject 当前根项目
|
||||
*/
|
||||
private fun deploySourcesCode(rootProject: Project) {
|
||||
/** 生成代码 */
|
||||
fun Project.generate() {
|
||||
val configs = configs.with(this).sourcesCode
|
||||
val outputDir = file(configs.generateDirPath)
|
||||
if (configs.isEnable.not()) return
|
||||
val properties = generatedProperties(configs, ProjectDescriptor.create(project = this))
|
||||
if (isConfigsModified.not() && properties == cachedProjectProperties[fullName] && outputDir.isEmpty().not()) {
|
||||
if (configs.isEnable) configureSourceSets(project = this)
|
||||
return
|
||||
}; outputDir.apply { if (exists()) deleteRecursively() }
|
||||
cachedProjectProperties[fullName] = properties
|
||||
val packageName = generatedPackageName(configs, project = this)
|
||||
val className = generatedClassName(configs, project = this)
|
||||
sourcesGenerator.build(configs, properties, packageName, className).writeTo(outputDir)
|
||||
configureSourceSets(project = this)
|
||||
}
|
||||
rootProject.generate()
|
||||
rootProject.subprojects.forEach { it.afterEvaluate { generate() } }
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置项目 SourceSets
|
||||
* @param project 当前项目
|
||||
*/
|
||||
private fun configureSourceSets(project: Project) {
|
||||
fun Project.deploySourceSets(name: String) = runCatching {
|
||||
val extension = get(name)
|
||||
val collection = extension.javaClass.getMethod("getSourceSets").invoke(extension) as DomainObjectCollection<*>
|
||||
collection.configureEach {
|
||||
val kotlin = javaClass.getMethod("getKotlin").invoke(this)
|
||||
kotlin.javaClass.getMethod("srcDir", Any::class.java).invoke(kotlin, configs.with(project).sourcesCode.generateDirPath)
|
||||
}
|
||||
}
|
||||
if (project.hasExtension("kotlin")) project.deploySourceSets(name = "kotlin")
|
||||
if (project.hasExtension("android")) project.deploySourceSets(name = "android")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性键值可访问 [Class] 生成目录
|
||||
* @param settings 当前设置
|
||||
* @return [File]
|
||||
*/
|
||||
private fun generatedAccessorsDir(settings: Settings) =
|
||||
settings.rootDir.resolve(".gradle").resolve(SweetPropertyProperties.PROJECT_MODULE_NAME).resolve(ACCESSORS_NAME).apply { mkdirs() }
|
||||
|
||||
/**
|
||||
* 获取生成的属性键值数组
|
||||
* @param configs 当前配置
|
||||
* @param descriptor 当前描述
|
||||
* @return [PropertyMap]
|
||||
*/
|
||||
private fun generatedProperties(configs: ISweetPropertyConfigs.IBaseGenerateConfigs, descriptor: ProjectDescriptor): PropertyMap {
|
||||
val propteries = mutableMapOf<String, Any>()
|
||||
configs.permanentKeyValues.forEach { (key, value) -> propteries[key] = value }
|
||||
configs.generateLocationTypes.forEach {
|
||||
val nativeKeyValues = when (it) {
|
||||
GenerateLocationType.CURRENT_PROJECT -> createProperties(configs, descriptor.currentDir)
|
||||
GenerateLocationType.ROOT_PROJECT -> createProperties(configs, descriptor.rootDir)
|
||||
GenerateLocationType.GLOBAL -> createProperties(configs, descriptor.homeDir)
|
||||
GenerateLocationType.SYSTEM -> System.getProperties()
|
||||
GenerateLocationType.SYSTEM_ENV -> System.getenv()
|
||||
} ?: emptyMap()
|
||||
val resolveKeyValues = mutableMapOf<String, Any>()
|
||||
nativeKeyValues.forEach native@{ (key, value) ->
|
||||
val hasExcludeKeys = configs.excludeKeys.noEmpty()?.any { content ->
|
||||
when (content) {
|
||||
is Regex -> content.toString().isNotBlank() && content.matches(key.toString())
|
||||
else -> content.toString() == key
|
||||
}
|
||||
} ?: false
|
||||
if (hasExcludeKeys) return@native
|
||||
val isAvailableKeyValue = if (configs.isEnableExcludeNonStringValue)
|
||||
key is CharSequence && key.isNotBlank() && value is CharSequence
|
||||
else key.toString().isNotBlank() && value != null
|
||||
if (isAvailableKeyValue) resolveKeyValues[key.toString()] = value
|
||||
}
|
||||
resolveKeyValues.forEach { (key, value) ->
|
||||
val resolveKeys = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* 处理键值内容
|
||||
* @return [String]
|
||||
*/
|
||||
fun String.resolveValue(): String = replaceInterpolation { matchKey ->
|
||||
if (resolveKeys.size > 5) SError.make("Key \"$key\" has been called recursively multiple times of those $resolveKeys")
|
||||
resolveKeys.add(matchKey)
|
||||
val resolveValue = if (configs.isEnableValueInterpolation) resolveKeyValues[matchKey]?.toString() ?: "" else matchKey
|
||||
if (resolveValue.hasInterpolation()) resolveValue.resolveValue()
|
||||
else resolveValue
|
||||
}
|
||||
if (value.toString().hasInterpolation()) resolveKeyValues[key] = value.toString().resolveValue()
|
||||
}; propteries.putAll(resolveKeyValues)
|
||||
}; return propteries
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成的包名
|
||||
* @param configs 当前配置
|
||||
* @param project 当前项目
|
||||
* @return [String]
|
||||
*/
|
||||
private fun generatedPackageName(configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs, project: Project): String {
|
||||
/**
|
||||
* 获取 Android 项目的 "namespace"
|
||||
* @return [String] or null
|
||||
*/
|
||||
fun Project.namespace() = runCatching {
|
||||
val extension = get("android")
|
||||
extension.javaClass.getMethod("getNamespace").invoke(extension) as String
|
||||
}.getOrNull()
|
||||
val packageName = configs.packageName.noBlank()
|
||||
?: project.namespace()
|
||||
?: project.group.toString().noBlank()
|
||||
?: "$DEFAULT_PACKAGE_NAME.${project.fullName.replace(":", "").flatted()}"
|
||||
return "$packageName.generated"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生成的类名
|
||||
* @param configs 当前配置
|
||||
* @param project 当前项目
|
||||
* @return [String]
|
||||
*/
|
||||
private fun generatedClassName(configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs, project: Project): String {
|
||||
val className = configs.className.noBlank()
|
||||
?: project.fullName.replace(":", "_").uppercamelcase().noBlank()
|
||||
?: "Undefined"
|
||||
return "${className.uppercamelcase()}Properties"
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新的 [Properties]
|
||||
* @param configs 当前配置
|
||||
* @param dir 当前目录
|
||||
* @return [Properties] or null
|
||||
*/
|
||||
private fun createProperties(configs: ISweetPropertyConfigs.IBaseGenerateConfigs, dir: File?) = runCatching {
|
||||
Properties().apply { load(FileReader(dir?.resolve(configs.propertiesFileName)?.absolutePath?.parseFileSeparator() ?: "")) }
|
||||
}.getOrNull()
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.sweetproperty.utils
|
||||
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 字符串路径转换为文件
|
||||
*
|
||||
* 自动调用 [parseFileSeparator]
|
||||
* @return [File]
|
||||
*/
|
||||
internal fun String.toFile() = File(parseFileSeparator())
|
||||
|
||||
/**
|
||||
* 格式化到当前操作系统的文件分隔符
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.parseFileSeparator() = replace("/", File.separator).replace("\\", File.separator)
|
||||
|
||||
/**
|
||||
* 格式化到 Unix 操作系统的文件分隔符
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.parseUnixFileSeparator() = replace("\\", "/")
|
||||
|
||||
/**
|
||||
* 检查目录是否为空
|
||||
*
|
||||
* - 如果不是目录 (可能是文件) - 返回 true
|
||||
* - 如果文件不存在 - 返回 true
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal fun File.isEmpty() = exists().not() || isDirectory.not() || listFiles().isNullOrEmpty()
|
||||
|
||||
/** 删除目录下的空子目录 */
|
||||
internal fun File.deleteEmptyRecursively() {
|
||||
listFiles { file -> file.isDirectory }?.forEach { subDir ->
|
||||
subDir.deleteEmptyRecursively()
|
||||
if (subDir.listFiles()?.isEmpty() == true) subDir.delete()
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/26.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.utils
|
||||
|
||||
/**
|
||||
* 当数组不为空时返回非空
|
||||
* @return [T] or null
|
||||
*/
|
||||
internal inline fun <reified T : Collection<*>> T.noEmpty() = takeIf { it.isNotEmpty() }
|
||||
|
||||
/**
|
||||
* 当字符串不为空白时返回非空
|
||||
* @return [T] or null
|
||||
*/
|
||||
internal inline fun <reified T : CharSequence> T.noBlank() = takeIf { it.isNotBlank() }
|
||||
|
||||
/**
|
||||
* 扁平化字符串处理
|
||||
*
|
||||
* 移除所有空格并转换为小写字母
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.flatted() = replace(" ", "").lowercase()
|
||||
|
||||
/**
|
||||
* 驼峰、"-"、"." 转大写下划线命名
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.underscore() = replace(".", "_").replace("-", "_").replace(" ", "_").replace("([a-z])([A-Z]+)".toRegex(), "$1_$2").uppercase()
|
||||
|
||||
/**
|
||||
* 下划线、分隔线、点、空格命名字符串转小驼峰命名字符串
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.camelcase() = runCatching {
|
||||
split("_", ".", "-", " ").map { it.replaceFirstChar { e -> e.titlecase() } }.let { words ->
|
||||
words.first().replaceFirstChar { it.lowercase() } + words.drop(1).joinToString("")
|
||||
}
|
||||
}.getOrNull() ?: this
|
||||
|
||||
/**
|
||||
* 下划线、分隔线、点、空格命名字符串转大驼峰命名字符串
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.uppercamelcase() = camelcase().capitalize()
|
||||
|
||||
/**
|
||||
* 字符串首字母大写
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.capitalize() = replaceFirstChar { it.uppercaseChar() }
|
||||
|
||||
/**
|
||||
* 字符串首字母小写
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.uncapitalize() = replaceFirstChar { it.lowercaseChar() }
|
||||
|
||||
/**
|
||||
* 转换字符串第一位数字到外观近似大写字母
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.firstNumberToLetter() =
|
||||
if (isNotBlank()) (mapOf(
|
||||
'0' to 'O', '1' to 'I',
|
||||
'2' to 'Z', '3' to 'E',
|
||||
'4' to 'A', '5' to 'S',
|
||||
'6' to 'G', '7' to 'T',
|
||||
'8' to 'B', '9' to 'P'
|
||||
)[first()] ?: first()) + substring(1)
|
||||
else this
|
||||
|
||||
/**
|
||||
* 转换字符串为非 Java 关键方法引用名称
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.toNonJavaName() = if (lowercase() == "class") replace("lass", "lazz") else this
|
||||
|
||||
/**
|
||||
* 字符串中是否存在插值符号 ${...}
|
||||
* @return [Boolean]
|
||||
*/
|
||||
internal fun String.hasInterpolation() = contains("\${") && contains("}")
|
||||
|
||||
/**
|
||||
* 替换字符串中的插值符号 ${...}
|
||||
* @param result 回调结果
|
||||
* @return [String]
|
||||
*/
|
||||
internal fun String.replaceInterpolation(result: (groupValue: String) -> CharSequence) =
|
||||
"\\$\\{(.+?)}".toRegex().replace(this) { result(it.groupValues[1]) }
|
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.utils.code
|
||||
|
||||
import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
|
||||
import com.highcapable.sweetproperty.utils.debug.SError
|
||||
import com.highcapable.sweetproperty.utils.deleteEmptyRecursively
|
||||
import com.highcapable.sweetproperty.utils.parseFileSeparator
|
||||
import com.highcapable.sweetproperty.utils.toFile
|
||||
import java.io.File
|
||||
import java.util.jar.JarEntry
|
||||
import java.util.jar.JarOutputStream
|
||||
import javax.tools.DiagnosticCollector
|
||||
import javax.tools.JavaFileObject
|
||||
import javax.tools.StandardLocation
|
||||
import javax.tools.ToolProvider
|
||||
|
||||
/**
|
||||
* 代码编译处理类
|
||||
*/
|
||||
internal object CodeCompiler {
|
||||
|
||||
/** Maven 模型版本 */
|
||||
private const val MAVEN_MODEL_VERSION = "4.0.0"
|
||||
|
||||
/**
|
||||
* 编译 [JavaFileObject] 为 Maven 依赖
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDirPath 编译输出目录路径
|
||||
* @param files [JavaFileObject] 数组
|
||||
* @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
|
||||
* @throws IllegalStateException 如果编译失败
|
||||
*/
|
||||
internal fun compile(
|
||||
pomData: MavenPomData,
|
||||
outputDirPath: String,
|
||||
files: List<JavaFileObject>,
|
||||
compileOnlyFiles: List<JavaFileObject> = mutableListOf()
|
||||
) {
|
||||
val outputDir = outputDirPath.toFile()
|
||||
if (files.isEmpty()) {
|
||||
if (outputDir.exists()) outputDir.deleteRecursively()
|
||||
return
|
||||
} else outputDir.also { if (it.exists().not()) it.mkdirs() }
|
||||
val outputBuildDir = "$outputDirPath/build".toFile().also { if (it.exists()) it.deleteRecursively(); it.mkdirs() }
|
||||
val outputClassesDir = "${outputBuildDir.absolutePath}/classes".toFile().apply { mkdirs() }
|
||||
val outputSourcesDir = "${outputBuildDir.absolutePath}/sources".toFile().apply { mkdirs() }
|
||||
val compiler = ToolProvider.getSystemJavaCompiler()
|
||||
val diagnostics = DiagnosticCollector<JavaFileObject>()
|
||||
val fileManager = compiler.getStandardFileManager(diagnostics, null, null)
|
||||
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(outputClassesDir))
|
||||
val task = compiler.getTask(null, fileManager, diagnostics, null, null, compileOnlyFiles + files)
|
||||
val result = task.call()
|
||||
var diagnosticsMessage = ""
|
||||
diagnostics.diagnostics?.forEach { diagnostic ->
|
||||
diagnosticsMessage += " > Error on line ${diagnostic.lineNumber} in ${diagnostic.source?.toUri()}\n"
|
||||
diagnosticsMessage += " ${diagnostic.getMessage(null)}\n"
|
||||
}
|
||||
runCatching { fileManager.close() }
|
||||
if (result) {
|
||||
compileOnlyFiles.forEach { "${outputClassesDir.absolutePath}/${it.name}".replace(".java", ".class").toFile().delete() }
|
||||
files.forEach {
|
||||
it.toFiles(outputSourcesDir).also { (sourceDir, sourceFile) ->
|
||||
sourceDir.mkdirs()
|
||||
sourceFile.writeText(it.getCharContent(true).toString())
|
||||
}
|
||||
}; outputClassesDir.deleteEmptyRecursively()
|
||||
writeMetaInf(outputClassesDir.absolutePath)
|
||||
writeMetaInf(outputSourcesDir.absolutePath)
|
||||
createJarAndPom(pomData, outputDir, outputBuildDir, outputClassesDir, outputSourcesDir)
|
||||
} else SError.make("Failed to compile java files into path: $outputDirPath\n$diagnosticsMessage")
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包 JAR 并写入 POM
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDir 编译输出目录
|
||||
* @param buildDir 编译目录
|
||||
* @param classesDir 编译二进制目录
|
||||
* @param sourcesDir 编译源码目录
|
||||
*/
|
||||
private fun createJarAndPom(pomData: MavenPomData, outputDir: File, buildDir: File, classesDir: File, sourcesDir: File) {
|
||||
val pomPath = "${pomData.groupId.toPomPathName()}/${pomData.artifactId}/${pomData.version}"
|
||||
val pomDir = "${outputDir.absolutePath}/$pomPath".toFile().also { if (it.exists().not()) it.mkdirs() }
|
||||
packageToJar(classesDir, pomDir, pomData, isSourcesJar = false)
|
||||
packageToJar(sourcesDir, pomDir, pomData, isSourcesJar = true)
|
||||
writePom(pomDir.absolutePath, pomData)
|
||||
buildDir.deleteRecursively()
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 META-INF/MANIFEST.MF
|
||||
* @param dirPath 当前目录路径
|
||||
*/
|
||||
private fun writeMetaInf(dirPath: String) {
|
||||
val metaInfFile = "$dirPath/META-INF".toFile().apply { mkdirs() }
|
||||
"${metaInfFile.absolutePath}/MANIFEST.MF".toFile().writeText("Manifest-Version: 1.0")
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 POM
|
||||
* @param dirPath 当前目录路径
|
||||
* @param pomData Maven POM 实体
|
||||
*/
|
||||
private fun writePom(dirPath: String, pomData: MavenPomData) =
|
||||
"$dirPath/${pomData.artifactId}-${pomData.version}.pom".toFile().writeText(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project
|
||||
xmlns="http://maven.apache.org/POM/$MAVEN_MODEL_VERSION"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/$MAVEN_MODEL_VERSION https://maven.apache.org/xsd/maven-$MAVEN_MODEL_VERSION.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>$MAVEN_MODEL_VERSION</modelVersion>
|
||||
<groupId>${pomData.groupId}</groupId>
|
||||
<artifactId>${pomData.artifactId}</artifactId>
|
||||
<version>${pomData.version}</version>
|
||||
</project>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
/**
|
||||
* 转换到 [MavenPomData] 目录名称
|
||||
* @return [String]
|
||||
*/
|
||||
private fun String.toPomPathName() = trim().replace(".", "/").replace("_", "/").replace(":", "/").replace("-", "/")
|
||||
|
||||
/**
|
||||
* 转换到文件
|
||||
* @param outputDir 输出目录
|
||||
* @return [Pair]<[File], [File]>
|
||||
*/
|
||||
private fun JavaFileObject.toFiles(outputDir: File): Pair<File, File> {
|
||||
val outputDirPath = outputDir.absolutePath
|
||||
val separator = if (name.contains("/")) "/" else "\\"
|
||||
val names = name.split(separator)
|
||||
val fileName = names[names.lastIndex]
|
||||
val folderName = name.replace(fileName, "")
|
||||
return "$outputDirPath/$folderName".toFile() to "$outputDirPath/$name".toFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包编译输出目录到 JAR
|
||||
* @param buildDir 编译目录
|
||||
* @param outputDir 输出目录
|
||||
* @param pomData Maven POM 实体
|
||||
* @param isSourcesJar 是否为源码 JAR
|
||||
* @throws IllegalStateException 如果编译输出目录不存在
|
||||
*/
|
||||
private fun packageToJar(buildDir: File, outputDir: File, pomData: MavenPomData, isSourcesJar: Boolean) {
|
||||
if (buildDir.exists().not()) SError.make("Jar file output path not found: ${buildDir.absolutePath}")
|
||||
/**
|
||||
* 添加文件到 JAR
|
||||
* @param jos 当前输出流
|
||||
* @param parentPath 父级路径
|
||||
*/
|
||||
fun File.addToJar(jos: JarOutputStream, parentPath: String = "") {
|
||||
val currentPath = "$parentPath$name".replace("${buildDir.name}|", "").replace("|", "/").parseFileSeparator()
|
||||
if (isFile) {
|
||||
if (name.startsWith(".")) return
|
||||
jos.putNextEntry(JarEntry(currentPath))
|
||||
inputStream().use { fis ->
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
while (fis.read(buffer).also { bytesRead = it } != -1) jos.write(buffer, 0, bytesRead)
|
||||
}
|
||||
jos.closeEntry()
|
||||
} else listFiles()?.forEach { it.addToJar(jos, parentPath = "$currentPath|") }
|
||||
}
|
||||
val jarFile = "${outputDir.absolutePath}/${pomData.artifactId}-${pomData.version}${if (isSourcesJar) "-sources" else ""}.jar".toFile()
|
||||
if (jarFile.exists()) jarFile.delete()
|
||||
jarFile.outputStream().use { fos -> JarOutputStream(fos).use { jos -> buildDir.addToJar(jos) } }
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.utils.code.entity
|
||||
|
||||
/**
|
||||
* Maven POM 实体
|
||||
* @param groupId Group ID
|
||||
* @param artifactId Artifact Id
|
||||
* @param version 版本
|
||||
*/
|
||||
internal data class MavenPomData(internal val groupId: String, internal val artifactId: String, internal val version: String)
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.sweetproperty.utils.code.factory
|
||||
|
||||
import com.highcapable.sweetproperty.utils.code.CodeCompiler
|
||||
import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
|
||||
import com.squareup.javapoet.JavaFile
|
||||
import javax.tools.JavaFileObject
|
||||
|
||||
/**
|
||||
* 编译 [JavaFile] 为 Maven 依赖
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDirPath 编译输出目录路径
|
||||
* @param compileOnlyFiles [JavaFile] 仅编译数组 - 默认空
|
||||
* @throws IllegalStateException 如果编译失败
|
||||
*/
|
||||
@JvmName("compileWithJavaFile")
|
||||
internal fun JavaFile.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List<JavaFile> = mutableListOf()) =
|
||||
CodeCompiler.compile(
|
||||
pomData = pomData,
|
||||
outputDirPath = outputDirPath,
|
||||
files = listOf(toJavaFileObject()),
|
||||
compileOnlyFiles = mutableListOf<JavaFileObject>().also { compileOnlyFiles.forEach { e -> it.add(e.toJavaFileObject()) } }
|
||||
)
|
||||
|
||||
/**
|
||||
* 编译 [JavaFile] 为 Maven 依赖
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDirPath 编译输出目录路径
|
||||
* @param compileOnlyFiles [JavaFile] 仅编译数组 - 默认空
|
||||
* @throws IllegalStateException 如果编译失败
|
||||
*/
|
||||
@JvmName("compileWithJavaFile")
|
||||
internal fun List<JavaFile>.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List<JavaFile> = mutableListOf()) =
|
||||
CodeCompiler.compile(
|
||||
pomData = pomData,
|
||||
outputDirPath = outputDirPath,
|
||||
files = mutableListOf<JavaFileObject>().also { forEach { e -> it.add(e.toJavaFileObject()) } },
|
||||
compileOnlyFiles = mutableListOf<JavaFileObject>().also { compileOnlyFiles.forEach { e -> it.add(e.toJavaFileObject()) } }
|
||||
)
|
||||
|
||||
/**
|
||||
* 编译 [JavaFileObject] 为 Maven 依赖
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDirPath 编译输出目录路径
|
||||
* @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
|
||||
* @throws IllegalStateException 如果编译失败
|
||||
*/
|
||||
@JvmName("compileWithJavaFileObject")
|
||||
internal fun JavaFileObject.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List<JavaFileObject> = mutableListOf()) =
|
||||
CodeCompiler.compile(pomData, outputDirPath, listOf(this), compileOnlyFiles)
|
||||
|
||||
/**
|
||||
* 编译 [JavaFileObject] 为 Maven 依赖
|
||||
* @param pomData Maven POM 实体
|
||||
* @param outputDirPath 编译输出目录路径
|
||||
* @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
|
||||
* @throws IllegalStateException 如果编译失败
|
||||
*/
|
||||
@JvmName("compileWithJavaFileObject")
|
||||
internal fun List<JavaFileObject>.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List<JavaFileObject> = mutableListOf()) =
|
||||
CodeCompiler.compile(pomData, outputDirPath, files = this, compileOnlyFiles)
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
package com.highcapable.sweetproperty.utils.debug
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
|
||||
/**
|
||||
* 全局异常管理类
|
||||
*/
|
||||
internal object SError {
|
||||
|
||||
/**
|
||||
* 抛出异常
|
||||
* @param msg 消息内容
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
internal fun make(msg: String): Nothing = error("[${SweetProperty.TAG}] $msg")
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.sweetproperty.utils.debug
|
||||
|
||||
import com.highcapable.sweetproperty.SweetProperty
|
||||
|
||||
/**
|
||||
* 全局 Log 管理类
|
||||
*/
|
||||
internal object SLog {
|
||||
|
||||
/**
|
||||
* 打印 Log
|
||||
* @param msg 消息内容
|
||||
*/
|
||||
internal fun log(msg: String) = println("[${SweetProperty.TAG}] $msg")
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SweetProperty - An easy get project properties anywhere Gradle plugin
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/HighCapable/SweetProperty
|
||||
*
|
||||
* 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 2023/8/27.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package org.gradle.kotlin.dsl
|
||||
|
||||
import com.highcapable.sweetproperty.gradle.factory.configure
|
||||
import com.highcapable.sweetproperty.gradle.factory.get
|
||||
import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
/**
|
||||
* WORKAROUND: for some reason a type-safe accessor is not generated for the extension,
|
||||
* even though it is present in the extension container where the plugin is applied.
|
||||
* This seems to work fine, and the extension methods are only available when the plugin
|
||||
* is actually applied.
|
||||
*
|
||||
* See related link [here](https://stackoverflow.com/questions/72627792/gradle-settings-plugin-extension)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves the [SweetPropertyConfigureExtension] extension.
|
||||
* @return [SweetPropertyConfigureExtension]
|
||||
*/
|
||||
val Settings.sweetProperty get() = get<SweetPropertyConfigureExtension>(SweetPropertyConfigureExtension.NAME)
|
||||
|
||||
/**
|
||||
* Configures the [SweetPropertyConfigureExtension] extension.
|
||||
* @param configure
|
||||
*/
|
||||
fun Settings.sweetProperty(configure: Action<SweetPropertyConfigureExtension>) = configure(SweetPropertyConfigureExtension.NAME, configure)
|
Reference in New Issue
Block a user