Initial commit

This commit is contained in:
2023-09-03 01:11:35 +08:00
commit 416f05460e
164 changed files with 12089 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
.gradle
build/

View File

@@ -0,0 +1,69 @@
plugins {
`kotlin-dsl`
autowire(libs.plugins.kotlin.jvm)
autowire(libs.plugins.kotlin.serialization)
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(org.jetbrains.kotlin.kotlin.gradle.plugin.api)
implementation(org.snakeyaml.snakeyaml.engine)
implementation(com.charleskorn.kaml.kaml)
implementation(com.squareup.okhttp3.okhttp)
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()
}

View File

@@ -0,0 +1,54 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
package com.highcapable.sweetdependency
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
/**
* [SweetDependency] 的装载调用类
*/
object SweetDependency {
/** Banner 内容 */
private const val BANNER_CONTENT = """
_____ _ _____ _
/ ____| | | | __ \ | |
| (_____ _____ ___| |_ | | | | ___ _ __ ___ _ __ __| | ___ _ __ ___ _ _
\___ \ \ /\ / / _ \/ _ \ __| | | | |/ _ \ '_ \ / _ \ '_ \ / _` |/ _ \ '_ \ / __| | | |
____) \ V V / __/ __/ |_ | |__| | __/ |_) | __/ | | | (_| | __/ | | | (__| |_| |
|_____/ \_/\_/ \___|\___|\__| |_____/ \___| .__/ \___|_| |_|\__,_|\___|_| |_|\___|\__, |
| | __/ |
|_| |___/
"""
/** Banner 内容 */
val bannerContent = BANNER_CONTENT.trimIndent()
/** 标签名称 */
const val TAG = SweetDependencyProperties.PROJECT_NAME
/** 版本 */
const val VERSION = SweetDependencyProperties.PROJECT_VERSION
/** 项目地址 */
const val PROJECT_URL = SweetDependencyProperties.PROJECT_URL
}

View File

@@ -0,0 +1,101 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/18.
*/
@file:Suppress("unused")
package com.highcapable.sweetdependency.document
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.manager.content.Repositories
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.toSpaceList
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* 依赖每项文档实体
* @param alias 别名
* @param version 版本
* @param versionRef 版本引用
* @param versions 版本别名数组
* @param isAutoUpdate 是否自动更新
* @param versionFilter 版本过滤器文档实体
* @param repositories 指定使用的存储库名称
*/
@Serializable
internal data class DependencyDocument(
@SerialName("alias")
internal var alias: String = "",
@SerialName("version")
internal var version: String = "",
@SerialName("version-ref")
internal var versionRef: String = "",
@SerialName("versions")
internal var versions: MutableMap<String, String> = mutableMapOf(),
@SerialName("auto-update")
internal var isAutoUpdate: Boolean = true,
@SerialName("version-filter")
internal var versionFilter: VersionFilterDocument? = null,
@SerialName("repositories")
internal var repositories: String = ""
) : IYamlDocument {
/**
* 获取版本
* @return [DependencyVersion]
*/
internal fun version() = DependencyVersion(version)
/**
* 获取版本别名数组
* @return <[MutableMap]><[String], [DependencyVersion]>
*/
internal fun versions() = mutableMapOf<String, DependencyVersion>().also {
versions.forEach { (key, value) -> it[key] = DependencyVersion(value.replace(DependencyVersion.LATEST_VERSION_NAME, version)) }
}
/**
* 更新版本
* @param newVersion 新版本
*/
internal fun updateVersion(newVersion: DependencyVersion) {
version = newVersion.current
}
/**
* 更新版本
* @param document 当前文档实例
*/
internal fun updateVersion(document: DependencyDocument) {
version = document.version
}
/**
* 获取指定使用的存储库数组
* @return [MutableList]<[RepositoryDocument]>
*/
internal fun repositories() = mutableListOf<RepositoryDocument>().apply {
repositories.toSpaceList().forEach {
add(Repositories.all().firstOrNull { e -> e.nodeName == it } ?: SError.make("Could not found repository with name \"$it\""))
}
}.distinctBy { it.url.ifBlank { it.path } }.toMutableList()
}

View File

@@ -0,0 +1,125 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
package com.highcapable.sweetdependency.document
import com.highcapable.sweetdependency.document.factory.checkingName
import com.highcapable.sweetdependency.utils.camelcase
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.gradle.api.initialization.resolve.RepositoriesMode as GradleRepositoriesMode
/**
* 偏好配置项文档实体
* @param autowireOnSyncMode Gradle Sync 自动装配、更新依赖模式
* @param repositoriesMode 存储库装载模式
* @param dependenciesNamespace 依赖命名空间
* @param versionFilter 版本过滤器文档实体
*/
@Serializable
internal data class PreferencesDocument(
@SerialName("autowire-on-sync-mode")
internal var autowireOnSyncMode: AutowireOnSyncMode = AutowireOnSyncMode.UPDATE_OPTIONAL_DEPENDENCIES,
@SerialName("repositories-mode")
internal var repositoriesMode: RepositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS,
@SerialName("dependencies-namespace")
internal var dependenciesNamespace: DependenciesNamespaceDocument = DependenciesNamespaceDocument(),
@SerialName("version-filter")
internal var versionFilter: VersionFilterDocument = VersionFilterDocument()
) : IYamlDocument {
@Serializable
internal data class DependenciesNamespaceDocument(
@SerialName("plugins")
var plugins: String = "libs",
@SerialName("libraries")
var libraries: String = ""
) : IYamlDocument {
init {
if (plugins.isNotBlank() && libraries.isNotBlank() && plugins == libraries)
SError.make("Duplicated dependencies namespace \"$plugins\"")
}
/**
* 获取插件依赖命名空间
* @return [String]
*/
internal fun plugins() = plugins.apply { checkingName("plugins namespace", isCheckExtName = true) }.camelcase()
/**
* 获取库依赖命名空间
* @return [String]
*/
internal fun libraries() = libraries.apply { checkingName("libraries namespace", isCheckExtName = true) }.camelcase()
}
/**
* Gradle Sync 自动装配、更新依赖模式定义类
*/
internal enum class AutowireOnSyncMode {
/** 自动装配和更新可选依赖 (插件依赖 + 库依赖) */
UPDATE_OPTIONAL_DEPENDENCIES,
/** 自动装配和更新所有依赖 (插件依赖 + 库依赖) */
UPDATE_ALL_DEPENDENCIES,
/** 仅自动装配使用“+”填充版本的依赖 (插件依赖 + 库依赖) */
ONLY_AUTOWIRE_DEPENDENCIES,
/** 自动装配和更新可选依赖 (插件依赖) */
UPDATE_OPTIONAL_PLUGINS,
/** 自动装配和更新所有依赖 (插件依赖) */
UPDATE_ALL_PLUGINS,
/** 仅自动装配使用“+”填充版本的依赖 (插件依赖) */
ONLY_AUTOWIRE_PLUGINS,
/** 自动装配和更新可选依赖 (库依赖) */
UPDATE_OPTIONAL_LIBRARIES,
/** 自动装配和更新所有依赖 (库依赖) */
UPDATE_ALL_LIBRARIES,
/** 仅自动装配使用“+”填充版本的依赖 (库依赖) */
ONLY_AUTOWIRE_LIBRARIES,
/** 什么也不做 - 关闭所有功能 */
OFF
}
/**
* 存储库装载模式定义类 (跟随 Gradle 进行配置调整)
*/
internal enum class RepositoriesMode {
/** 参考 [GradleRepositoriesMode.PREFER_PROJECT] */
PREFER_PROJECT,
/** 参考 [GradleRepositoriesMode.PREFER_SETTINGS] */
PREFER_SETTINGS,
/** 参考 [GradleRepositoriesMode.FAIL_ON_PROJECT_REPOS] */
FAIL_ON_PROJECT_REPOS
}
}

View File

@@ -0,0 +1,273 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
package com.highcapable.sweetdependency.document
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.manager.const.InternalRepositories
import com.highcapable.sweetdependency.manager.content.Repositories
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.parseUnixFileSeparator
import com.highcapable.sweetdependency.utils.toSpaceList
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.gradle.api.artifacts.repositories.PasswordCredentials
import org.gradle.api.artifacts.repositories.RepositoryContentDescriptor
/**
* 存储库配置项文档实体
* @param isEnable 是否启用
* @param scope 作用域
* @param content 内容过滤器
* @param credentials 身份验证配置项文档实体
* @param url URL 地址
* @param path 文件路径
*/
@Serializable
internal data class RepositoryDocument(
@SerialName("enable")
internal var isEnable: Boolean = true,
@SerialName("scope")
internal var scope: RepositoryScope = RepositoryScope.ALL,
@SerialName("content")
internal var content: ContentDocument = ContentDocument(),
@SerialName("credentials")
internal var credentials: CredentialsDocument = CredentialsDocument(),
@SerialName("url")
internal var url: String = "",
@SerialName("path")
internal var path: String = "",
) : IYamlDocument {
/**
* 身份验证配置项文档实体
*
* 这些内容来自 [PasswordCredentials]
* @param username 用户名
* @param password 密码
*/
@Serializable
internal data class CredentialsDocument(
@SerialName("username")
internal var username: String = "",
@SerialName("password")
internal var password: String = ""
) : IYamlDocument
/**
* 内容配置文档实体
*
* 这些内容来自 [RepositoryContentDescriptor]
* @param exclude 排除配置文档实体
* @param include 包含配置文档实体
*/
@Serializable
internal data class ContentDocument(
@SerialName("exclude")
internal var exclude: FilterDocument = FilterDocument(),
@SerialName("include")
internal var include: FilterDocument = FilterDocument()
) : IYamlDocument {
/**
* 内容过滤器配置文档实体
*
* 这些内容来自 [RepositoryContentDescriptor]
* @param group 过滤器条件内容
* @param groupAndSubgroups 过滤器条件内容
* @param groupByRegex 过滤器条件内容
* @param module 过滤器条件内容
* @param moduleByRegex 过滤器条件内容
* @param version 过滤器条件内容
* @param versionByRegex 过滤器条件内容
*/
@Serializable
internal data class FilterDocument(
@SerialName("group")
internal var group: String = "",
@SerialName("group-and-subgroups")
internal var groupAndSubgroups: String = "",
@SerialName("group-by-regex")
internal var groupByRegex: String = "",
@SerialName("module")
internal var module: String = "",
@SerialName("module-by-regex")
internal var moduleByRegex: String = "",
@SerialName("version")
internal var version: String = "",
@SerialName("version-by-regex")
internal var versionByRegex: String = ""
) : IYamlDocument {
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun group() = group.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun groupAndSubgroups() = groupAndSubgroups.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun groupByRegex() = groupByRegex.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun module() = module.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun moduleByRegex() = moduleByRegex.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun version() = version.toSpaceList()
/**
* 获取过滤器条件内容
* @return [List]<[String]>
*/
internal fun versionByRegex() = versionByRegex.toSpaceList()
/**
* 当前规则是否为空
* @return [Boolean]
*/
internal fun isEmpty() =
group.isBlank() && groupAndSubgroups.isBlank() && groupByRegex.isBlank() &&
module.isBlank() && moduleByRegex.isBlank() &&
version.isBlank() && versionByRegex.isBlank()
}
/**
* 当前规则是否为空
* @return [Boolean]
*/
internal fun isEmpty() = exclude.isEmpty() && include.isEmpty()
}
/** 节点名称 */
@Transient
internal var nodeName = ""
/** 节点类型 */
@Transient
internal var nodeType = RepositoryType.UNSPECIFIED
/**
* 存储库作用域定义类
*/
internal enum class RepositoryScope {
/** 作用于所有类型依赖 */
ALL,
/** 作用于插件依赖 */
PLUGINS,
/** 作用于库依赖 */
LIBRARIES
}
/**
* 存储库已知类型定义类
*/
internal enum class RepositoryType {
/** 未指定 */
UNSPECIFIED,
/** Google Maven */
GOOGLE,
/** 中央存储库 */
MAVEN_CENTRAL,
/** 本地存储库 */
MAVEN_LOCAL,
/** 自定义存储库 */
MAVEN,
/** Gradle Plugin 存储库 */
GRADLE_PLUGIN_PORTAL
}
/**
* 获取是否包含在作用域内
* @param isPlugins 当前类型是否为插件依赖
* @return [Boolean]
*/
internal fun isIncludeScope(isPlugins: Boolean) =
if (isPlugins) scope == RepositoryScope.ALL || scope == RepositoryScope.PLUGINS
else scope == RepositoryScope.ALL || scope == RepositoryScope.LIBRARIES
/**
* 创建当前实体
* @param name 键值名称
* @return [RepositoryDocument]
*/
internal fun build(name: String) = apply {
when (name) {
InternalRepositories.Name.GOOGLE -> {
url = url.ifBlank { InternalRepositories.GOOGLE }
nodeType = RepositoryType.GOOGLE
}
InternalRepositories.Name.MAVEN_CENTRAL -> {
url = url.ifBlank { InternalRepositories.MAVEN_CENTRAL }
nodeType = RepositoryType.MAVEN_CENTRAL
}
InternalRepositories.Name.GRADLE_PLUGIN_PORTAL -> {
url = url.ifBlank { InternalRepositories.GRADLE_PLUGIN_PORTAL }
nodeType = RepositoryType.GRADLE_PLUGIN_PORTAL
}
InternalRepositories.Name.MAVEN_LOCAL -> {
path = path.ifBlank { Repositories.defaultMavenLocalPath }
nodeType = RepositoryType.MAVEN_LOCAL
}
InternalRepositories.Name.MAVEN -> SError.make("Use \"maven\" as a repository name is an error, please choose another name")
InternalRepositories.Name.IVY -> SError.make("Ivy is not support on ${SweetDependency.TAG} ${SweetDependency.VERSION}")
else -> {
url = url.ifBlank {
Repositories.findAdditional(name).ifBlank {
SError.make("Could not found internal or additional repository URL by repository name \"$name\", you must specify a URL")
}
}; nodeType = RepositoryType.MAVEN
}
}; nodeName = name
if (url.isNotBlank() && path.isNotBlank()) SError.make("There can only be one \"url\" and \"path\" parameter of \"$name\"")
if (path.isNotBlank() && (path.startsWith("https://") || path.startsWith("http://"))) SError.make("Invalid repository path: $path")
if (url.isNotBlank() && url.startsWith("https://").not() && url.startsWith("http://").not()) SError.make("Invalid repository URL: $url")
if (path.isNotBlank()) path = path.parseUnixFileSeparator()
}
}

View File

@@ -0,0 +1,253 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.document
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.factory.DependencyMap
import com.highcapable.sweetdependency.document.factory.RepositoryList
import com.highcapable.sweetdependency.document.factory.checkingName
import com.highcapable.sweetdependency.document.factory.convertToDependencyAmbiguousName
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.utils.capitalize
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.findDuplicates
import com.highcapable.sweetdependency.utils.hasDuplicate
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* 项目 (根节点) 配置文档实体
* @param preferences 偏好配置项文档实体
* @param repositories 每项存储库配置项文档实体
* @param plugins 每项插件依赖文档实体
* @param libraries 每项库依赖文档实体
* @param versions 每项版本定义数组
*/
@Serializable
internal data class RootConfigDocument(
@SerialName("preferences")
internal var preferences: PreferencesDocument? = PreferencesDocument(),
@SerialName("repositories")
internal var repositories: MutableMap<String, RepositoryDocument?>? = null,
@SerialName("plugins")
internal var plugins: MutableMap<String, DependencyDocument>? = null,
@SerialName("libraries")
internal var libraries: MutableMap<String, MutableMap<String, DependencyDocument>>? = null,
@SerialName("versions")
internal var versions: MutableMap<String, String>? = null
) : IYamlDocument {
internal companion object {
/** 默认文档内容 */
private const val DEFAULT_CONTENT = """
# SweetDependency project configuration file
# You can adjust your custom configuration to your liking here
# You can visit ${SweetDependency.PROJECT_URL} for more help
#
# SweetDependency 项目配置文件
# 你可以在这里调整你喜欢的自定义配置
# 你可以前往 ${SweetDependency.PROJECT_URL} 以获得更多帮助
# Configure preferences
# 配置偏好设置
preferences:
autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES
repositories-mode: FAIL_ON_PROJECT_REPOS
# Configure repositories used by dependencies
# 配置依赖使用的存储库
repositories:
gradle-plugin-portal:
scope: PLUGINS
google:
maven-central:
# Configure plugins that need to be used
# For example:
# plugins:
# org.jetbrains.kotlin.jvm:
# version: +
#
# 配置需要使用的插件依赖
# 例如:
# plugins:
# org.jetbrains.kotlin.jvm:
# version: +
plugins:
# Configure libraries that need to be used
# For example:
# libraries:
# com.google.code.gson:
# gson:
# version: +
#
# 配置需要使用的库依赖
# 例如:
# libraries:
# com.google.code.gson:
# gson:
# version: +
libraries:
"""
/** 默认文档内容 */
internal val defaultContent = DEFAULT_CONTENT.trimIndent()
}
/**
* 获取当前偏好配置项文档实体
* @return [PreferencesDocument]
*/
internal fun preferences() = preferences ?: PreferencesDocument()
/**
* 获取当前存储库配置项文档实体
* @return [RepositoryList]
*/
internal fun repositories() = repositories?.let {
mutableListOf<RepositoryDocument>().apply {
it.forEach { (name, repository) -> (repository ?: RepositoryDocument()).build(name).also { if (it.isEnable) add(it) } }
}
} ?: mutableListOf()
/**
* 获取当前插件依赖数组
* @param duplicate 允许重复 - 忽略处理后版本重复的异常 - 默认否
* @return [DependencyMap]
*/
internal fun plugins(duplicate: Boolean = false) = createPlugins().resolveDependencies(typeName = "plugin", duplicate)
/**
* 获取当前库依赖数组
* @param duplicate 允许重复 - 忽略处理后版本重复的异常 - 默认否
* @return [DependencyMap]
*/
internal fun libraries(duplicate: Boolean = false) = createLibraries().resolveDependencies(typeName = "library", duplicate)
/**
* 处理依赖数组
* @param typeName 依赖类型名称
* @param duplicate 允许重复 - 忽略处理后版本重复的异常 - 默认否
*/
private fun DependencyMap.resolveDependencies(typeName: String, duplicate: Boolean = false) = apply {
val firstTypeName = typeName.capitalize()
val checkDuplicateAlias = mutableMapOf<String, String>()
val refLibraries = mutableListOf<Triple<DependencyName, String, DependencyVersion>>()
val ambiguousNames = mutableListOf<String>()
eachDependencies { dependencyName, artifact ->
artifact.alias.checkingName("$typeName \"$dependencyName\" alias", isCheckMultiName = true)
artifact.versions().forEach { (name, _) -> name.checkingName("$typeName \"$dependencyName\" version alias") }
if (artifact.alias.isNotBlank())
if (checkDuplicateAlias.contains(artifact.alias).not())
checkDuplicateAlias[artifact.alias] = dependencyName.current
else SError.make(
"Duplicated alias \"${artifact.alias}\", " +
"already declared in $typeName \"${checkDuplicateAlias[artifact.alias]}\""
)
if (artifact.version().isNoSpecific && (artifact.versions().isNotEmpty() || artifact.versionRef.isNotBlank()))
SError.make(
"$firstTypeName \"$dependencyName\" has declared that it does not specify a version, " +
"so it cannot use \"versions\" or \"version-ref\""
)
if (artifact.versionRef.isNotBlank() && artifact.versionRef.startsWith("<this>::"))
artifact.versionRef = artifact.versionRef.replace("<this>:", dependencyName.groupId)
refLibraries.add(Triple(dependencyName, artifact.alias, artifact.version()))
}
eachDependencies { dependencyName, artifact ->
/** 处理版本引用 */
fun resolveVersionRef() {
refLibraries.firstOrNull { artifact.versionRef.let { e -> e == it.first.current || e == it.second } }?.also {
if (dependencyName == it.first || dependencyName.current == it.second)
SError.make("$firstTypeName \"$dependencyName\" declared \"version-ref\" from itself (recursive call found)")
when {
it.third.isNoSpecific -> SError.make(
"$firstTypeName \"${it.first}\" does not specify a version, so it can no longer be " +
"declared as \"version-ref\" by $typeName \"$dependencyName\""
)
it.third.isBlank -> SError.make(
"$firstTypeName \"${it.first}\" already has \"version-ref\" declared, so it can no longer" +
" be declared as \"version-ref\" by $typeName \"$dependencyName\" (recursive call found)"
)
}; artifact.updateVersion(it.third)
} ?: SError.make(
"Could not found any versions or dependencies associated with " +
"version-ref \"${artifact.versionRef}\" of $typeName \"$dependencyName\""
)
}
if (artifact.version().isNoSpecific) return@eachDependencies
if (artifact.version().isBlank)
if (artifact.versionRef.isNotBlank())
versions()[artifact.versionRef]?.also { artifact.version = it } ?: resolveVersionRef()
else SError.make("Missing declared version when configuring $typeName \"$dependencyName\"")
else if (artifact.version().isBlank.not() && artifact.versionRef.isNotBlank() && duplicate.not())
SError.make("$firstTypeName \"$dependencyName\" can only have one \"version\" or \"version-ref\" node, please delete one")
}
eachDependencies { dependencyName, artifact ->
ambiguousNames.add(dependencyName.ambiguousName())
if (artifact.alias.isNotBlank()) {
artifact.alias.checkingName("$typeName \"$dependencyName\" alias", isCheckMultiName = true)
ambiguousNames.add(artifact.alias.convertToDependencyAmbiguousName())
}; this[dependencyName] = artifact
}
if (ambiguousNames.hasDuplicate()) ambiguousNames.findDuplicates().forEach {
SError.make("Found ambiguous name \"$it\" in declared dependencies, please checking your $typeName aliases that your declared")
} else ambiguousNames.clear()
}
/**
* 获取当前版本定义数组
* @return [MutableMap]<[String], [String]>
*/
internal fun versions() = versions?.onEach { (name, _) -> name.checkingName("versions name") } ?: mutableMapOf()
/**
* 重新创建 [plugins]
* @return [DependencyMap]
*/
private fun createPlugins() = mutableMapOf<DependencyName, DependencyDocument>().apply {
plugins?.forEach { (notation, artifact) -> this[DependencyName.plugin(notation)] = artifact }
}
/**
* 重新创建 [libraries]
* @return [DependencyMap]
*/
private fun createLibraries() = mutableMapOf<DependencyName, DependencyDocument>().apply {
libraries?.forEach { (groupId, libraries) ->
libraries.forEach { (artifactId, artifact) -> this[DependencyName.library(groupId, artifactId)] = artifact }
}
}
/**
* 循环每项 [plugins]、[libraries]
* @param result 回调每项结果
*/
private inline fun DependencyMap.eachDependencies(result: (dependencyName: DependencyName, artifact: DependencyDocument) -> Unit) =
forEach { (dependencyName, artifact) -> result(dependencyName, artifact) }
}

View File

@@ -0,0 +1,105 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/9.
*/
package com.highcapable.sweetdependency.document
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.manager.content.Repositories
import com.highcapable.sweetdependency.utils.filter
import com.highcapable.sweetdependency.utils.toSpaceList
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* 版本过滤器文档实体
* @param isUseInternal 使用内置过滤器
* @param exclusionList 排除列表
*/
@Serializable
internal data class VersionFilterDocument(
@SerialName("use-internal")
internal var isUseInternal: Boolean = true,
@SerialName("exclusion-list")
internal var exclusionList: String = ""
) : IYamlDocument {
/**
* 版本排除列表实体
* @param list 当前排除列表数组
*/
internal class ExclusionList internal constructor(private val list: MutableList<String>) {
/**
* 获取当前排除列表数组
* @return [MutableList]<[String]>
*/
internal fun all() = list
/**
* 当前是否存在排除列表
* @return [Boolean]
*/
internal fun isEmpty() = all().isEmpty()
/**
* 当前是否不存在排除列表
* @return [Boolean]
*/
internal fun isNotEmpty() = isEmpty().not()
/**
* 依赖于当前 [version] 提供的版本并在 [all] 中排除 (不区分大小写)
*
* 此操作会调用 [clone] 创建一个新实例并返回
* @param version 当前版本
* @return [ExclusionList]
*/
internal fun depends(version: DependencyVersion) = clone().apply {
if (version.isAutowire.not() && version.isBlank.not()) all().removeAll { version.deployed.lowercase().contains(it.lowercase()) }
}
/**
* 使用 [all] 过滤当前版本字符串 (不区分大小写)
* @param versions 当前版本字符串数组
* @return [MutableList]<[DependencyVersion]>
*/
internal fun filter(versions: MutableList<DependencyVersion>) =
if (all().isEmpty()) versions else versions.filter { version -> all().none { version.current.lowercase().contains(it.lowercase()) } }
/**
* 克隆并创建一个新实例
* @return [ExclusionList]
*/
private fun clone() = ExclusionList(mutableListOf<String>().apply { addAll(all()) })
override fun toString() = all().toString()
}
/**
* 获取排除列表
* @return [ExclusionList]
*/
internal fun exclusionList() = ExclusionList(mutableListOf<String>().apply {
if (isUseInternal) addAll(Repositories.defaultVersionFilterExclusionList)
addAll(exclusionList.toSpaceList())
})
}

View File

@@ -0,0 +1,160 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/31.
*/
package com.highcapable.sweetdependency.document.factory
import com.highcapable.sweetdependency.document.DependencyDocument
import com.highcapable.sweetdependency.document.PreferencesDocument
import com.highcapable.sweetdependency.document.RepositoryDocument
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.gradle.factory.isUnSafeExtName
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.firstNumberToLetter
/** 存储库文档实体类型定义 */
internal typealias RepositoryList = MutableList<RepositoryDocument>
/** 依赖文档实体类型定义 */
internal typealias DependencyMap = MutableMap<DependencyName, DependencyDocument>
/** 依赖文档更新实体类型定义 */
internal typealias DependencyUpdateMap = MutableMap<String, Pair<DependencyName, DependencyVersion>>
/** 依赖文档查找条件类型定义 */
internal typealias DependenciesCondition = (dependencyName: DependencyName, artifact: DependencyDocument) -> Boolean
/**
* 转换 [PreferencesDocument.AutowireOnSyncMode] 到 [DependencyUpdateMode]
*
* 如果为 [PreferencesDocument.AutowireOnSyncMode.OFF] 则会返回 null
* @return [DependencyUpdateMode] or null
*/
internal fun PreferencesDocument.AutowireOnSyncMode.toUpdateMode() = when (this) {
PreferencesDocument.AutowireOnSyncMode.UPDATE_OPTIONAL_DEPENDENCIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.ALL, DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL)
PreferencesDocument.AutowireOnSyncMode.UPDATE_ALL_DEPENDENCIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.ALL, DependencyUpdateMode.UpdateType.UPDATE_ALL)
PreferencesDocument.AutowireOnSyncMode.ONLY_AUTOWIRE_DEPENDENCIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.ALL, DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE)
PreferencesDocument.AutowireOnSyncMode.UPDATE_OPTIONAL_PLUGINS ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.PLUGINS, DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL)
PreferencesDocument.AutowireOnSyncMode.UPDATE_ALL_PLUGINS ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.PLUGINS, DependencyUpdateMode.UpdateType.UPDATE_ALL)
PreferencesDocument.AutowireOnSyncMode.ONLY_AUTOWIRE_PLUGINS ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.PLUGINS, DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE)
PreferencesDocument.AutowireOnSyncMode.UPDATE_OPTIONAL_LIBRARIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.LIBRARIES, DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL)
PreferencesDocument.AutowireOnSyncMode.UPDATE_ALL_LIBRARIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.LIBRARIES, DependencyUpdateMode.UpdateType.UPDATE_ALL)
PreferencesDocument.AutowireOnSyncMode.ONLY_AUTOWIRE_LIBRARIES ->
DependencyUpdateMode(DependencyUpdateMode.DependencyType.LIBRARIES, DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE)
PreferencesDocument.AutowireOnSyncMode.OFF -> null
}
/**
* 合并到依赖名称
* @param groupId Group ID
* @param artifactId Artifact ID
* @return [String]
*/
internal fun spliceToDependencyNotation(groupId: String, artifactId: String) = "$groupId:$artifactId"
/**
* 分离到依赖名称数组
*
* "com.mylibrary:library-core" → "com.mylibrary" | "library-core"
* @return [List]<[String]>
*/
internal fun String.splitToDependencyNames() = trim().split(":").apply { if (size != 2) SError.make("Invalid dependency name \"$this\"") }
/**
* 分离到依赖生成名称数组
*
* "com.mylibrary:library-core" → "com" | "mylibrary" | "library" | "core"
* @return [List]<[String]>
*/
internal fun String.splitToDependencyGenerateNames() =
trim().replace("_", "|").replace(".", "|").replace(":", "|").replace("-", "|").split("|").filter { it.isNotBlank() }
/**
* 转换到依赖 URL 名称
*
* "com.mylibrary:library-core" → "com/mylibrary/library-core"
* @return [String]
*/
internal fun String.convertToDependencyUrlName() = splitToDependencyNames().let { "${it[0].replace(".", "/")}/${it[1]}" }
/**
* 转换到依赖模糊分离名称 (使用 [symbol] 进行分离)
*
* "com.mylibrary:library-core" → "com[symbol]mylibrary[symbol]library[symbol]core"
* @param symbol 分隔符 - 默认 "."
* @param isReplaceFirstChar 是否使用 [firstNumberToLetter] 替换每一段第一个字符 - 默认否
* @param isLowerCase 是否全部转换为小写 - 默认是
* @return [String]
*/
internal fun String.convertToDependencyAmbiguousName(symbol: String = ".", isReplaceFirstChar: Boolean = false, isLowerCase: Boolean = true) =
mutableListOf<String>().apply {
trim().replace(".", "|").replace("_", "|").replace(":", "|").replace("-", "|").split("|").forEach {
add(if (isReplaceFirstChar) it.firstNumberToLetter() else it)
}
}.joinToString(symbol).let { if (isLowerCase) it.lowercase() else it }
/**
* 检查名称、别名是否合法
*
* - 只能包含:'0-9'、'A-Z'、'a-z'、'.'、'_'、'-' 且必须以字母开头 (长度至少为 3 位)
* - 不能是 [isUnSafeExtName]
* @param content 内容
* @param isCheckExtName 是否同时检查是否为 Gradle 使用的关键字名称 - 默认否
* @param isCheckMultiName 是否同时检查是否可被 [splitToDependencyGenerateNames] 分割为两位及以上名称 - 默认否
* @throws IllegalArgumentException 如果名称、别名不合法
*/
internal fun String.checkingName(content: String, isCheckExtName: Boolean = false, isCheckMultiName: Boolean = false) {
if (isBlank()) return
if (length < 3) SError.make("Illegal $content \"$this\", the length of $content must be >= 3")
/**
* 检查是否为 Gradle 使用的关键字名称
* @param isEnable 默认跟随 [isCheckExtName]
* @throws IllegalArgumentException 如果名称、别名不合法
*/
fun String.checkUnSafeExtName(isEnable: Boolean = isCheckExtName) {
if (isEnable && isUnSafeExtName()) SError.make("This $content \"$this\" of \"${this@checkingName}\" is a Gradle built-in extension")
}
checkUnSafeExtName()
if (isCheckMultiName) splitToDependencyGenerateNames().also { splitedNames ->
if (splitedNames.isEmpty()) SError.make("This $content \"$this\" cannot be split, please check and try again")
if (splitedNames.size < 2) SError.make("This $content \"$this\" must be able to be split into at least 2 parts")
splitedNames[0].checkUnSafeExtName(isEnable = true)
splitedNames.forEach {
if (it.first() !in 'A'..'Z' && it.first() !in 'a'..'z')
SError.make("Illegal $content \"$it\" of \"$this\", it must start with a letter")
}
}
forEachIndexed { index, char ->
if ((char !in 'A'..'Z' && char !in 'a'..'z' && index == 0) ||
(char !in 'A'..'Z' && char !in 'a'..'z' &&
char !in '0'..'9' && char != '_' && char != '-' && char != '.')
) SError.make("Illegal $content \"$this\", it only allow 26 letters (upper and lower case) and '.', '_', '-' and must start with a letter")
}
}

View File

@@ -0,0 +1,197 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/1.
*/
package com.highcapable.sweetdependency.document.mapping
import com.highcapable.sweetdependency.document.RootConfigDocument
import com.highcapable.sweetdependency.document.factory.DependencyUpdateMap
import com.highcapable.sweetdependency.document.factory.spliceToDependencyNotation
import com.highcapable.sweetdependency.document.mapping.entity.DependencyMapping
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.filter
import com.highcapable.sweetdependency.utils.joinToContent
import com.highcapable.sweetdependency.utils.toFile
import com.highcapable.sweetdependency.utils.yaml.Yaml
import com.highcapable.sweetdependency.utils.yaml.factory.asMap
import com.highcapable.sweetdependency.utils.yaml.factory.isKeyExist
import java.io.File
/**
* [RootConfigDocument] 的测绘实例实现类
* @param configs 当前配置
*/
internal class RootConfigDocumentMapping internal constructor(private val configs: ISweetDependencyConfigs) {
private companion object {
/** 偏好设置节点名称 */
private const val PREFERENCES_NODE_NAME = "preferences:"
/** 存储库节点名称 */
private const val REPOSITORIES_NODE_NAME = "repositories:"
/** 版本节点名称 */
private const val VERSIONS_NODE_NAME = "versions:"
/** 插件依赖节点名称 */
private const val PLUGINS_NODE_NAME = "plugins:"
/** 库依赖节点名称 */
private const val LIBRARIES_NODE_NAME = "libraries:"
/** 依赖版本起始内容 */
private const val VERSION_NODE_CONTENT = "version:"
/** 依赖版本引用节点名称 (不包括结尾的“:”) */
private const val VERSION_REF_NODE_NAME = "version-ref"
/** 4 个空格 (缩进) 内容 */
private const val SPACE_OF_4 = " "
/** 6 个空格 (缩进) 内容 */
private const val SPACE_OF_6 = " "
}
/** 当前配置文件 */
private var configFile: File? = null
/** 配置文件行内容数组 */
private val configFileContents = mutableListOf<String>()
/** 插件依赖测绘实体数组 */
private val pluginsMapping = mutableListOf<DependencyMapping>()
/** 库依赖测绘实体数组 */
private val librariesMapping = mutableListOf<DependencyMapping>()
init {
runCatching { createMapping() }.onFailure {
when (it) {
is SweetDependencyUnresolvedException -> throw it
else -> SLog.error("Failed to create config file's mapping, this will cause problem")
}
}
}
/** 创建测绘数据 */
private fun createMapping() {
configFileContents.clear()
configFileContents.addAll(configs.configFilePath.toFile().apply { configFile = this }.readText().split("\n"))
var isFoundPluginsStartLine = false
var pluginsStartLine = -1
var pluginsLine = 0
var isFoundLibrariesStartLine = false
var librariesStartLine = -1
var librariesLine = 0
val pluginsContents = mutableListOf<String>()
val librariesContents = mutableListOf<String>()
configFileContents.forEachIndexed { index, content ->
if (content.contains("\"\"")) SError.make("Character declared like -> \"\" <- are not allowed, detected at line ${index + 1}")
}
configFileContents.forEachIndexed { index, content ->
if (content.startsWith(PREFERENCES_NODE_NAME) ||
content.startsWith(REPOSITORIES_NODE_NAME) ||
content.startsWith(VERSIONS_NODE_NAME) ||
content.startsWith(LIBRARIES_NODE_NAME)
) {
isFoundPluginsStartLine = false
return@forEachIndexed
}
if (content.startsWith(PLUGINS_NODE_NAME)) {
isFoundPluginsStartLine = true
pluginsStartLine = index
}
if (isFoundPluginsStartLine) pluginsContents.add(content)
}
configFileContents.forEachIndexed { index, content ->
if (content.startsWith(PREFERENCES_NODE_NAME) ||
content.startsWith(REPOSITORIES_NODE_NAME) ||
content.startsWith(VERSIONS_NODE_NAME) ||
content.startsWith(PLUGINS_NODE_NAME)
) {
isFoundLibrariesStartLine = false
return@forEachIndexed
}
if (content.startsWith(LIBRARIES_NODE_NAME)) {
isFoundLibrariesStartLine = true
librariesStartLine = index
}
if (isFoundLibrariesStartLine) librariesContents.add(content)
}
if (pluginsContents.isNotEmpty())
Yaml.loadFromStringAsNode(pluginsContents.joinToContent()).forEach { (_, rootNode) ->
rootNode.asMap()?.forEach { (notation, artifactNode) ->
if (artifactNode.asMap()?.isKeyExist(VERSION_REF_NODE_NAME) == false)
pluginsMapping.add(DependencyMapping(notation.content))
}
}
if (librariesContents.isNotEmpty())
Yaml.loadFromStringAsNode(librariesContents.joinToContent()).forEach { (_, rootNode) ->
rootNode.asMap()?.forEach { (groupId, libraryNode) ->
libraryNode.asMap()?.forEach { (artifactId, artifactNode) ->
val notation = spliceToDependencyNotation(groupId.content, artifactId.content)
if (artifactNode.asMap()?.isKeyExist(VERSION_REF_NODE_NAME) == false)
librariesMapping.add(DependencyMapping(notation))
}
}
}
pluginsContents.onEachIndexed { index, content ->
if ((content.trim().startsWith(VERSION_NODE_CONTENT) && content.startsWith(SPACE_OF_4)).not()) return@onEachIndexed
pluginsMapping[pluginsLine].versionLine = index + pluginsStartLine
pluginsLine++
}.clear()
librariesContents.onEachIndexed { index, content ->
if ((content.trim().startsWith(VERSION_NODE_CONTENT) && content.startsWith(SPACE_OF_6)).not()) return@onEachIndexed
librariesMapping[librariesLine].versionLine = index + librariesStartLine
librariesLine++
}.clear()
}
/**
* 使用测绘数据更新依赖版本内容
* @param dependencies 需要更新的依赖名称和版本数组
*/
internal fun updateDependencies(dependencies: DependencyUpdateMap) {
/**
* 写入更新的依赖数据到文件内容
* @param dependencies 依赖数组
* @param spaceContent 空格内容
*/
fun List<DependencyMapping>.dumpToContents(dependencies: DependencyUpdateMap, spaceContent: String) =
filter { dependencies.containsKey(it.notation) }.forEach {
var codeNote = ""
val originContent = configFileContents[it.versionLine]
if (originContent.contains("#")) originContent.indexOf("#")
.also { e -> if (e > 0) codeNote = originContent.substring(e - 1..originContent.lastIndex) }
configFileContents[it.versionLine] = "$spaceContent$VERSION_NODE_CONTENT ${dependencies[it.notation]?.second?.mapped}$codeNote"
}
val plugins = dependencies.filter { it.value.first.type == DependencyName.Type.PLUGIN }
val libraries = dependencies.filter { it.value.first.type == DependencyName.Type.LIBRARY }
if (plugins.isNotEmpty()) pluginsMapping.dumpToContents(plugins, SPACE_OF_4)
if (libraries.isNotEmpty()) librariesMapping.dumpToContents(libraries, SPACE_OF_6)
if (configFileContents.isNotEmpty()) configFile?.writeText(buildString { configFileContents.forEach { append("$it\n") } }.trim())
}
}

View File

@@ -0,0 +1,29 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/1.
*/
package com.highcapable.sweetdependency.document.mapping.entity
/**
* 每项依赖测绘实体
* @param notation 依赖名称或 ID
* @param versionLine 版本所处行号
*/
internal data class DependencyMapping(internal var notation: String = "", internal var versionLine: Int = -1)

View File

@@ -0,0 +1,95 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/28.
*/
package com.highcapable.sweetdependency.environment
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.utils.toFile
import java.io.File
/**
* [SweetDependency] 环境工具类
*/
internal object Environment {
/** [SweetDependency] 缓存存放目录 */
private const val MEMORY_DIR_PATH = ".gradle/${SweetDependencyProperties.PROJECT_MODULE_NAME}"
/** [SweetDependency] 功能存放目录 */
private const val RESOURCES_DIR_PATH = "gradle/${SweetDependencyProperties.PROJECT_MODULE_NAME}"
/**
* 获取 [SweetDependency] 缓存存放目录
* @return [File]
*/
private val memoryDir get() = "${GradleHelper.rootDir.absolutePath}/$MEMORY_DIR_PATH".toFile().also { if (it.exists().not()) it.mkdirs() }
/**
* 获取 [SweetDependency] 功能存放目录
* @return [File]
*/
private val resourcesDir get() = "${GradleHelper.rootDir.absolutePath}/$RESOURCES_DIR_PATH".toFile().also { if (it.exists().not()) it.mkdirs() }
/**
* 获取系统信息
* @return [String]
*/
internal val systemInfo get() = "${System.getProperty("os.name")} ${System.getProperty("os.version")}"
/**
* 获取字符集名称
* @return [String]
*/
internal val characterEncoding get() = System.getProperty("file.encoding")
/**
* 获取 Java 版本
* @return [String]
*/
internal val javaVersion get() = System.getProperty("java.version")
/**
* 获取 [SweetDependency] 缓存存放目录
* @param dirOrFileName 子路径目录、文件名称数组
* @return [File]
*/
internal fun memoryDir(vararg dirOrFileName: String) = memoryDir.parseDir(*dirOrFileName)
/**
* 获取 [SweetDependency] 功能存放目录
* @param dirOrFileName 子路径目录、文件名称数组
* @return [File]
*/
internal fun resourcesDir(vararg dirOrFileName: String) = resourcesDir.parseDir(*dirOrFileName)
/**
* 解析 [SweetDependency] 存放目录
* @param dirOrFileName 子路径目录、文件名称数组
* @return [File]
*/
private fun File.parseDir(vararg dirOrFileName: String): File {
var splitPath = ""
dirOrFileName.forEach { splitPath += "$it/" }
return "$absolutePath/${splitPath.ifBlank { "/" }.dropLast(1)}".toFile()
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/31.
*/
package com.highcapable.sweetdependency.exception
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.utils.dumpToString
/**
* [SweetDependency] 异常定义类
* @param msg 异常内容
* @param parent 父级异常 - 默认空
*/
internal class SweetDependencyUnresolvedException internal constructor(private val msg: String, parent: Throwable? = null) : Exception(
("[${SweetDependency.TAG}] The project initialization could not be completed, please check the following for errors\n" +
"If you need help, visit ${SweetDependency.PROJECT_URL}\n" +
"* What went wrong:\n" +
"$msg\n${if (parent != null) (when (parent) {
is SweetDependencyUnresolvedException -> "* Caused by:"
else -> "* Exception is:"
} + "\n${parent.dumpToString()}") else ""}").trim()
) {
override fun toString() = "${javaClass.simpleName}: $msg"
}

View File

@@ -0,0 +1,94 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/26.
*/
package com.highcapable.sweetdependency.gradle.delegate
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.gradle.proxy.IGradleLifecycle
import com.highcapable.sweetdependency.utils.debug.SError
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
/**
* Gradle 代理工具类
*/
internal object GradleDelegate {
/** 当前 Gradle 生命周期接口实例 */
private var lifecycle: IGradleLifecycle? = null
/**
* 创建 Gradle 生命周期 (插件) [T]
* @param settings 当前设置
*/
internal inline fun <reified T : IGradleLifecycle> create(settings: Settings) {
runCatching {
lifecycle = T::class.java.getConstructor().newInstance()
}.onFailure { SError.make("Failed to create Gradle lifecycle of \"${T::class.java}\"") }
GradleHelper.attach(settings)
callOnSettingsLoaded(settings)
settings.gradle.settingsEvaluated { callOnSettingsEvaluate(settings = this) }
settings.gradle.projectsLoaded {
callOnProjectLoaded(rootProject, isRoot = true)
rootProject.afterEvaluate { callOnProjectEvaluate(project = this, isRoot = true) }
rootProject.subprojects.forEach {
callOnProjectLoaded(it, isRoot = false)
it.afterEvaluate { callOnProjectEvaluate(project = this, isRoot = false) }
}
}
}
/**
* 调用 Gradle 开始装载事件
* @param settings 当前实例
*/
private fun callOnSettingsLoaded(settings: Settings) {
lifecycle?.onSettingsLoaded(settings)
}
/**
* 调用 Gradle 装载完成事件
* @param settings 当前实例
*/
private fun callOnSettingsEvaluate(settings: Settings) {
lifecycle?.onSettingsEvaluate(settings)
}
/**
* 调用 Gradle 开始装载项目事件
* @param project 当前项目
* @param isRoot 是否为根项目
*/
private fun callOnProjectLoaded(project: Project, isRoot: Boolean) {
if (isRoot) GradleHelper.cachingProjectList(project)
lifecycle?.onProjectLoaded(project, isRoot)
}
/**
* 调用 Gradle 项目装载完成事件
* @param project 当前项目
* @param isRoot 是否为根项目
*/
private fun callOnProjectEvaluate(project: Project, isRoot: Boolean) {
GradleHelper.cachingDependencyList(project, isRoot)
lifecycle?.onProjectEvaluate(project, isRoot)
}
}

View File

@@ -0,0 +1,63 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/29.
*/
package com.highcapable.sweetdependency.gradle.delegate
import org.gradle.api.Project
import kotlin.properties.Delegates
/**
* 项目事务实例实现类
*/
internal class ProjectTransaction {
internal companion object {
/** 当前项目 (当前生命周期静态) */
internal var current by Delegates.notNull<Project>()
/** 是否为根项目 (当前生命周期静态) */
internal var isRoot by Delegates.notNull<Boolean>()
}
/** 当前装载实例方法体数组 */
internal val evaluateCallbacks = mutableSetOf<((Project, Boolean) -> Unit)>()
/**
* 获取当前项目
* @return [Project]
*/
internal val current get() = Companion.current
/**
* 获取是否为根项目
* @return [Boolean]
*/
internal val isRoot get() = Companion.isRoot
/**
* 创建装载实例监听
* @param evaluate 回调装载监听 - ([Project] 当前项目,[Boolean] 师傅为根项目)
*/
internal fun evaluation(evaluate: (project: Project, isRoot: Boolean) -> Unit) {
evaluateCallbacks.add(evaluate)
}
}

View File

@@ -0,0 +1,67 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/25.
*/
@file:Suppress("USELESS_ELVIS", "KotlinRedundantDiagnosticSuppress")
package com.highcapable.sweetdependency.gradle.delegate.entity
import com.highcapable.sweetdependency.document.factory.spliceToDependencyNotation
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.manager.GradleTaskManager
import com.highcapable.sweetdependency.utils.debug.SError
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
/**
* 外部存储库依赖实体代理类
*
* 代理 [DefaultExternalModuleDependency]
* @param groupId Group ID
* @param artifactId Artifact ID
* @param version 版本
*/
internal open class ExternalDependencyDelegate internal constructor(
@get:JvmName("getDelegateGroupId")
@set:JvmName("setDelegateGroupId")
var groupId: String,
@get:JvmName("getDelegateArtifactId")
@set:JvmName("setDelegateArtifactId")
var artifactId: String,
@get:JvmName("getDelegateVersion")
@set:JvmName("setDelegateVersion")
var version: String
) : DefaultExternalModuleDependency(groupId, artifactId, version) {
override fun getVersion(): String {
val notation = spliceToDependencyNotation(groupId, artifactId)
if (version == DependencyVersion.AUTOWIRE_VERSION_NAME && GradleTaskManager.isInternalRunningTask.not()) SError.make(
"""
This library "$notation" is not autowired and cannot be deployed
You can try the following solutions to resolve this problem:
1. Manually re-run Gradle Sync (make sure "autowire-on-sync-mode" not be "OFF")
2. Manually run "${GradleTaskManager.AUTOWIRE_LIBRARIES_TASK_NAME}" task and re-run Gradle Sync
3. Fill an existing version for dependency "$notation" and re-run Gradle Sync
If you get this error again after doing the above, maybe the currently set repositories cannot find this library
""".trimIndent()
); return super.getVersion() ?: version
}
override fun toString() = "ExternalDependencyDelegate(groupId = $groupId, artifactId = $artifactId, version = $version)"
}

View File

@@ -0,0 +1,127 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.gradle.entity
import com.highcapable.sweetdependency.document.factory.convertToDependencyAmbiguousName
import com.highcapable.sweetdependency.document.factory.convertToDependencyUrlName
import com.highcapable.sweetdependency.document.factory.spliceToDependencyNotation
import com.highcapable.sweetdependency.document.factory.splitToDependencyNames
import com.highcapable.sweetdependency.utils.firstNumberToLetter
/**
* 依赖名称实体类
* @param type 名称类型
* @param groupId Group ID
* @param artifactId Artifact ID
*/
internal class DependencyName private constructor(internal val type: Type, internal val groupId: String, internal val artifactId: String) {
internal companion object {
/** 标识 Gradle 插件后缀名称 */
private const val GRADLE_PLUGIN_SUFFIX = "gradle.plugin"
/**
* 创建为插件依赖名称
* @param notation 完整名称
*/
internal fun plugin(notation: String) = DependencyName(Type.PLUGIN, notation, "$notation.$GRADLE_PLUGIN_SUFFIX")
/**
* 创建为库依赖名称
* @param notation 完整名称
*/
internal fun library(notation: String) = notation.splitToDependencyNames().let { names -> DependencyName(Type.LIBRARY, names[0], names[1]) }
/**
* 创建为库依赖名称
* @param groupId Group ID
* @param artifactId Artifact ID
*/
internal fun library(groupId: String, artifactId: String) = DependencyName(Type.LIBRARY, groupId, artifactId)
}
/**
* 获取当前模糊分离名称 (使用 [symbol] 进行分离)
* @param symbol 分隔符 - 默认 "."
* @param isReplaceFirstChar 是否使用 [firstNumberToLetter] 替换每一段第一个字符 - 默认否
* @param isLowerCase 是否全部转换为小写 - 默认是
* @return [String]
*/
internal fun ambiguousName(symbol: String = ".", isReplaceFirstChar: Boolean = false, isLowerCase: Boolean = true) =
current.convertToDependencyAmbiguousName(symbol, isReplaceFirstChar, isLowerCase)
/**
* 获取当前 URL 名称
* @return [String]
*/
internal val urlName get() = notation.convertToDependencyUrlName()
/**
* 获取当前描述内容
* @return [String]
*/
internal val description get() = "$typeName \"$current\""
/**
* 获取当前类型名称
* @return [String]
*/
internal val typeName get() = when (type) {
Type.PLUGIN -> "Plugin"
Type.LIBRARY -> "Library"
}
/**
* 获取当前名称
* @return [String]
*/
internal val current get() = when (type) {
Type.PLUGIN -> groupId
Type.LIBRARY -> notation
}
/**
* 获取当前完整名称
* @return [String]
*/
internal val notation get() = spliceToDependencyNotation(groupId, artifactId)
override fun equals(other: Any?) = other.toString() == toString()
override fun hashCode() = toString().hashCode()
override fun toString() = current
/**
* 名称类型定义类
*/
internal enum class Type {
/** 插件依赖 */
PLUGIN,
/** 库依赖 */
LIBRARY
}
}

View File

@@ -0,0 +1,58 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/18.
*/
package com.highcapable.sweetdependency.gradle.entity
/**
* 依赖更新模式实体类
* @param dependencyType 依赖类型
* @param updateType 依赖更新模式
*/
internal data class DependencyUpdateMode(internal var dependencyType: DependencyType, internal var updateType: UpdateType) {
/**
* 依赖类型定义类
*/
internal enum class DependencyType {
/** 全部类型 */
ALL,
/** 插件依赖 */
PLUGINS,
/** 库依赖 */
LIBRARIES,
}
/**
* 依赖更新模式类型定义类
*/
internal enum class UpdateType {
/** 可选更新 */
UPDATE_OPTIONAL,
/** 全部更新 */
UPDATE_ALL,
/** 仅自动装配 */
ONLY_AUTOWIRE,
}
}

View File

@@ -0,0 +1,157 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/16.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.gradle.entity
import com.highcapable.sweetdependency.utils.debug.SError
/**
* 依赖版本实体类
* @param actual 当前实际版本 - 如果为 [DependencyVersion.isOptional] 则会显示在第一位
* @param optionalType 可选更新类型 - 默认为 [DependencyVersion.OptionalUpdateType.NONE]
*/
internal class DependencyVersion(internal var actual: String, optionalType: OptionalUpdateType = OptionalUpdateType.NONE) {
internal companion object {
/** 标识自动装配版本的名称 */
const val AUTOWIRE_VERSION_NAME = "+"
/** 标识不指定版本的名称 */
const val NO_SPECIFIC_VERSION_NAME = "<no-spec>"
/** 标识当前最新版本的名称 */
const val LATEST_VERSION_NAME = "<latest>"
/** 标识常规可选更新版本前缀名称 */
private const val OPTIONAL_VERSION_NORMAL_PREFIX = "^"
/** 标识常驻可选更新版本前缀名称 */
private const val OPTIONAL_VERSION_PERMANENT_PREFIX = "^^"
}
init {
if (current.startsWith("<") && current.endsWith(">"))
if (current != NO_SPECIFIC_VERSION_NAME && current != LATEST_VERSION_NAME)
SError.make("The parameter \"$current\" is not recognized as any available function")
if (isOptional.not()) when (optionalType) {
OptionalUpdateType.NONE -> {}
OptionalUpdateType.NORMAL -> actual = "$OPTIONAL_VERSION_NORMAL_PREFIX$actual"
OptionalUpdateType.PERMANENT -> actual = "$OPTIONAL_VERSION_PERMANENT_PREFIX$actual"
}
}
/**
* 获取当前版本
* @return [String]
*/
internal val current get() = actual.replace(OPTIONAL_VERSION_PERMANENT_PREFIX, "").replace(OPTIONAL_VERSION_NORMAL_PREFIX, "")
/**
* 获取当前测绘数据使用的版本
*
* 它会自动识别 [optionalType] 决定是否继续保留可选更新的符号
* @return [String]
*/
internal val mapped get() = when (optionalType) {
OptionalUpdateType.PERMANENT -> actual
else -> current
}
/**
* 获取部署版本
*
* 如果为 [isNoSpecific] 则会返回空
* @return [String]
*/
internal val deployed get() = current.takeIf { isNoSpecific.not() } ?: ""
/**
* 获取存在版本
*
* 如果为空则会返回 [NO_SPECIFIC_VERSION_NAME]
* @return [String]
*/
internal val existed get() = current.ifBlank { NO_SPECIFIC_VERSION_NAME }
/**
* 是否为空白
* @return [Boolean]
*/
internal val isBlank get() = current.isBlank()
/**
* 是否为自动装配版本
* @return [Boolean]
*/
internal val isAutowire get() = current == AUTOWIRE_VERSION_NAME
/**
* 是否为不指定版本
* @return [Boolean]
*/
internal val isNoSpecific get() = current == NO_SPECIFIC_VERSION_NAME
/**
* 是否为可选更新版本
* @return [Boolean]
*/
internal val isOptional get() = optionalType != OptionalUpdateType.NONE
/**
* 获取当前可选更新类型
* @return [OptionalUpdateType]
*/
internal val optionalType get() = when {
actual.startsWith(OPTIONAL_VERSION_PERMANENT_PREFIX) -> OptionalUpdateType.PERMANENT
actual.startsWith(OPTIONAL_VERSION_NORMAL_PREFIX) -> OptionalUpdateType.NORMAL
else -> OptionalUpdateType.NONE
}
/**
* 克隆为新的 [DependencyVersion] 实体
* @param version 当前版本
* @return [DependencyVersion]
*/
internal fun clone(version: String) = DependencyVersion(version, optionalType)
override fun equals(other: Any?) = other.toString() == toString()
override fun hashCode() = toString().hashCode()
override fun toString() = current
/**
* 可选更新类型定义类
*/
internal enum class OptionalUpdateType {
/** 无 */
NONE,
/** 常规 */
NORMAL,
/** 常驻 */
PERMANENT,
}
}

View File

@@ -0,0 +1,44 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/23.
*/
package com.highcapable.sweetdependency.gradle.entity
/**
* 外部存储库依赖实体类
* @param dependencyName 依赖名称
* @param version 版本
*/
internal data class ExternalDependency(private val dependencyName: DependencyName, internal val version: DependencyVersion) {
/**
* 获取 Group ID
* @return [String]
*/
internal val groupId get() = dependencyName.groupId
/**
* 获取 Artifact ID
* @return [String]
*/
internal val artifactId get() = dependencyName.artifactId
override fun toString() = dependencyName.current
}

View File

@@ -0,0 +1,117 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/27.
*/
@file:Suppress("unused", "USELESS_CAST", "KotlinRedundantDiagnosticSuppress")
package com.highcapable.sweetdependency.gradle.factory
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
import com.highcapable.sweetdependency.utils.camelcase
import com.highcapable.sweetdependency.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 SweetDependencyUnresolvedException 如果类型不是 [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

View File

@@ -0,0 +1,190 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/2.
*/
@file:Suppress("USELESS_ELVIS", "KotlinRedundantDiagnosticSuppress", "UselessCallOnNotNull")
package com.highcapable.sweetdependency.gradle.factory
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.gradle.wrapper.LibraryDependencyWrapper
import com.highcapable.sweetdependency.gradle.wrapper.PluginDependencyWrapper
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
import com.highcapable.sweetdependency.utils.code.entity.MavenPomData
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.noBlank
import com.highcapable.sweetdependency.utils.orEmpty
import com.highcapable.sweetdependency.utils.toFile
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.internal.GeneratedSubclasses
import org.gradle.api.internal.plugins.PluginManagerInternal
import org.gradle.api.plugins.PluginManager
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderConvertible
import org.gradle.kotlin.dsl.buildscript
import org.gradle.kotlin.dsl.repositories
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependency
/**
* 获取指定项目的完整名称
* @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)
/**
* 获取指定项目部署的插件依赖数组 (实时)
*
* @param isUseCache 是否使用缓存 - 默认启用 - 启用后将使用 [GradleHelper.projectPlugins] 进行获取
* @return [MutableList]<[PluginDependencyWrapper]>
*/
internal fun Project.plugins(isUseCache: Boolean = true) =
if (isUseCache) GradleHelper.projectPlugins[this].orEmpty() else mutableListOf<PluginDependencyWrapper>().apply {
plugins.configureEach {
pluginManager.findPluginId(this).noBlank()?.also { add(PluginDependencyWrapper(instance = this, it)) }
}
}
/**
* 获取指定项目部署的库依赖数组 (实时)
*
* @param isUseCache 是否使用缓存 - 默认启用 - 启用后将使用 [GradleHelper.projectLibraries] 进行获取
* @return [MutableList]<[LibraryDependencyWrapper]>
*/
internal fun Project.libraries(isUseCache: Boolean = true) =
if (isUseCache) GradleHelper.projectLibraries[this].orEmpty() else mutableListOf<LibraryDependencyWrapper>().apply {
/**
* 检查依赖是否有效
* @return [Boolean]
*/
fun Dependency.checkingValid() = when (this) {
is ExternalDependency -> group.isNullOrBlank().not() && name.isNullOrBlank().not()
is FileCollectionDependency -> runCatching { files.files.isNotEmpty() }.getOrNull() ?: false
else -> true
}
/** 在一些项目 (例如 Kotlin Multiplatform 中会发生异常 [java.util.ConcurrentModificationException] - 这里直接做拦截处理 */
runCatching {
configurations.forEach { config ->
config.dependencies.forEach { if (it.checkingValid()) add(LibraryDependencyWrapper(it, config.name ?: "")) }
}
}
}
/**
* 等待并监听当指定插件被添加时回调
* @param id 插件 ID
* @param action 回调插件实例
*/
internal fun Project.waitForPluginAdded(id: String, action: (Plugin<*>) -> Unit) {
plugins.whenPluginAdded { if (pluginManager.findPluginId(this) == id) action(this) }
}
/**
* 创建 Gradle Task [T]
* @param group Task 分组
* @param name Task 名称
* @return [T]
*/
internal inline fun <reified T : BaseTask> Project.createTask(group: String, name: String) = runCatching {
T::class.java.getConstructor().newInstance().also { instance ->
task(name) {
this.group = group
outputs.upToDateWhen { false }
doFirst { instance.onTransaction() }
}
}
}.getOrNull() ?: SError.make("Gradle task \"$name\" with group \"$group\" create failed")
/**
* 应用插件
* @param id 插件 ID
* @param version 版本
*/
internal fun PluginDependenciesSpec.applyPlugin(id: String, version: String) =
id(id).apply { if (version.isNotBlank()) version(version) } ?: SError.make("Plugin \"$id\" not apply")
/**
* 应用插件
* @param alias 别名实例
*/
internal fun PluginDependenciesSpec.applyPlugin(alias: Any) = when (alias) {
is Provider<*> ->
@Suppress("UNCHECKED_CAST")
alias(alias as? Provider<PluginDependency> ?: SError.make("The $alias is not a valid plugin"))
?: SError.make("Plugin $alias not apply")
is ProviderConvertible<*> ->
@Suppress("UNCHECKED_CAST")
alias(alias as? ProviderConvertible<PluginDependency> ?: SError.make("The $alias is not a valid plugin"))
?: SError.make("Plugin $alias not apply")
else -> SError.make("The $alias is not a valid plugin (unknown type)")
}
/**
* 通过 [PluginManager] 查找 [Plugin] 真实 ID
*
* 如果找不到会返回空字符串
* @param plugin 当前实例
* @return [String]
*/
private fun PluginManager.findPluginId(plugin: Plugin<*>) = runCatching {
@Suppress("UNCHECKED_CAST")
val pluginIds = (this as PluginManagerInternal).findPluginIdForClass(GeneratedSubclasses.unpackType(plugin) as Class<Plugin<*>>)
if (pluginIds.isEmpty.not()) pluginIds.get() else null
}.getOrNull()?.id ?: ""

View File

@@ -0,0 +1,155 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/19.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.gradle.helper
import com.highcapable.sweetdependency.gradle.factory.libraries
import com.highcapable.sweetdependency.gradle.factory.plugins
import com.highcapable.sweetdependency.gradle.wrapper.LibraryDependencyWrapper
import com.highcapable.sweetdependency.gradle.wrapper.PluginDependencyWrapper
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.parseFileSeparator
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
import java.io.File
import java.io.FileReader
import java.util.*
/**
* Gradle 工具类
*/
internal object GradleHelper {
/** Gradle 配置文件名称 */
private const val GRADLE_PROPERTIES_FILE_NAME = "gradle.properties"
/** 当前 [Gradle] 静态实例 */
private var instance: Gradle? = null
/** 当前 [Settings] 静态实例 */
private var settings: Settings? = null
/** 当前装载的所有项目 */
internal val allProjects = mutableSetOf<Project>()
/** 当前装载的项目插件依赖数组 */
internal val projectPlugins = mutableMapOf<Project, MutableList<PluginDependencyWrapper>>()
/** 当前装载的项目库依赖数组 */
internal val projectLibraries = mutableMapOf<Project, MutableList<LibraryDependencyWrapper>>()
/**
* 绑定当前使用的 [Settings] 静态实例
* @param settings 当前设置
*/
internal fun attach(settings: Settings) {
instance = settings.also { this.settings = it }.gradle
}
/**
* 缓存所有项目列表
* @param rootProject 当前根项目
*/
internal fun cachingProjectList(rootProject: Project) {
allProjects.clear()
allProjects.addAll(rootProject.allprojects)
}
/**
* 缓存所有依赖列表
* @param project 当前项目
* @param isRoot 是否为根项目
*/
internal fun cachingDependencyList(project: Project, isRoot: Boolean) {
if (isRoot) {
projectPlugins.clear()
projectLibraries.clear()
}
project.plugins(isUseCache = false).forEach {
if (projectPlugins[project] == null) projectPlugins[project] = mutableListOf()
projectPlugins[project]?.add(it)
}
project.libraries(isUseCache = false).forEach {
if (projectLibraries[project] == null) projectLibraries[project] = mutableListOf()
projectLibraries[project]?.add(it)
}
}
/**
* 获取用户目录的 [Properties]
* @return [Properties] or null
*/
internal val userProperties get() = createProperties(instance?.gradleUserHomeDir)
/**
* 获取当前项目的 [Properties]
* @return [Properties] or null
*/
internal val projectProperties get() = createProperties(settings?.rootDir)
/**
* 获取当前 Gradle 项目的根目录
* @return [File]
*/
internal val rootDir get() = rootProject?.projectDir ?: settings?.rootDir ?: SError.make("Gradle is unavailable")
/**
* 获取当前 Gradle 项目的根项目 (Root Project)
* @return [Project] or null
*/
internal val rootProject get() = runCatching { instance?.rootProject }.getOrNull()
/**
* 获取当前 Gradle 版本
* @return [String]
*/
internal val version get() = instance?.gradle?.gradleVersion ?: ""
/**
* 获取当前 Gradle 是否处于离线模式
* @return [Boolean]
*/
internal val isOfflineMode get() = instance?.startParameter?.isOffline == true
/**
* 获取当前 Gradle 是否处于同步模式
* @return [Boolean]
*/
internal val isSyncMode get() = runningTaskNames.isNullOrEmpty()
/**
* 获取当前正在运行的 Task 名称数组
* @return [MutableList]<[String]> or null
*/
internal val runningTaskNames get() = instance?.startParameter?.taskRequests?.getOrNull(0)?.args
/**
* 创建新的 [Properties]
* @param dir 当前目录
* @return [Properties] or null
*/
private fun createProperties(dir: File?) = runCatching {
Properties().apply { load(FileReader(dir?.resolve(GRADLE_PROPERTIES_FILE_NAME)?.absolutePath?.parseFileSeparator() ?: "")) }
}.getOrNull()
}

View File

@@ -0,0 +1,57 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/26.
*/
package com.highcapable.sweetdependency.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 project 当前项目
* @param isRoot 是否为根项目
*/
fun onProjectLoaded(project: Project, isRoot: Boolean)
/**
* 当 Gradle 项目装载完成时回调
* @param project 当前项目
* @param isRoot 是否为根项目
*/
fun onProjectEvaluate(project: Project, isRoot: Boolean)
}

View File

@@ -0,0 +1,92 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/28.
*/
@file:Suppress("USELESS_ELVIS", "KotlinRedundantDiagnosticSuppress")
package com.highcapable.sweetdependency.gradle.wrapper
import com.highcapable.sweetdependency.document.factory.spliceToDependencyNotation
import com.highcapable.sweetdependency.gradle.delegate.entity.ExternalDependencyDelegate
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.gradle.wrapper.type.LibraryDependencyType
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.artifacts.ProjectDependency
import java.io.File
/**
* 库依赖包装实例实现类
* @param instance 当前实例
* @param configurationName 配置名称
*/
internal data class LibraryDependencyWrapper internal constructor(private val instance: Dependency, internal val configurationName: String) {
/**
* 获取当前依赖类型
* @return [LibraryDependencyType]
*/
val type
get() = when (instance) {
is ExternalDependencyDelegate -> LibraryDependencyType.EXTERNAL_DELEGATE
is ExternalDependency -> LibraryDependencyType.EXTERNAL
is ProjectDependency -> LibraryDependencyType.PROJECT
is FileCollectionDependency -> LibraryDependencyType.FILES
else -> LibraryDependencyType.OTHERS
}
/**
* 依赖的文件数组
*
* - [type] 需要为 [LibraryDependencyType.FILES] 否则始终为 null
* @return [MutableSet]<[File]> or null
*/
val files get() = runCatching { (instance as? FileCollectionDependency?)?.files?.files?.toMutableSet() }.getOrNull()
/**
* 依赖的项目
*
* - [type] 需要为 [LibraryDependencyType.PROJECT] 否则始终为 null
* @return [Project] or null
*/
val project get() = runCatching { (instance as? ProjectDependency?)?.dependencyProject }.getOrNull()
/**
* Group ID
* @return [String]
*/
val groupId get() = instance.group ?: ""
/**
* Artifact ID
* @return [String]
*/
val artifactId get() = instance.name ?: ""
/**
* 版本
* @return [DependencyVersion]
*/
val version get() = DependencyVersion(instance.version ?: "")
override fun toString() = spliceToDependencyNotation(groupId, artifactId)
}

View File

@@ -0,0 +1,34 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/17.
*/
package com.highcapable.sweetdependency.gradle.wrapper
import org.gradle.api.Plugin
/**
* 插件依赖包装实例实现类
* @param instance 当前实例
* @param id 插件 ID
*/
internal data class PluginDependencyWrapper internal constructor(private val instance: Plugin<*>, internal val id: String) {
override fun toString() = id
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/10.
*/
package com.highcapable.sweetdependency.gradle.wrapper.type
/**
* 库依赖类型定义类
*/
internal enum class LibraryDependencyType {
/** 其它类型 */
OTHERS,
/** 外部存储库 */
EXTERNAL,
/** 外部存储库 (代理) */
EXTERNAL_DELEGATE,
/** 项目 */
PROJECT,
/** 文件 */
FILES
}

View File

@@ -0,0 +1,335 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/16.
*/
package com.highcapable.sweetdependency.manager
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.PreferencesDocument
import com.highcapable.sweetdependency.document.VersionFilterDocument
import com.highcapable.sweetdependency.document.factory.DependenciesCondition
import com.highcapable.sweetdependency.document.factory.DependencyMap
import com.highcapable.sweetdependency.document.factory.RepositoryList
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.gradle.factory.get
import com.highcapable.sweetdependency.gradle.factory.getOrCreate
import com.highcapable.sweetdependency.gradle.factory.waitForPluginAdded
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.manager.content.Dependencies
import com.highcapable.sweetdependency.manager.content.Repositories
import com.highcapable.sweetdependency.manager.helper.DependencyAutowireLogHelper
import com.highcapable.sweetdependency.manager.helper.DependencyDeployHelper
import com.highcapable.sweetdependency.manager.maven.MavenParser
import com.highcapable.sweetdependency.manager.maven.entity.MavenMetadata
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.extension.dsl.manager.SweetDependencyAutowireExtension
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.noEmpty
import com.highcapable.sweetdependency.utils.single
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.plugins.ExtensionAware
/**
* 依赖部署、自动装配、更新管理类
*/
internal object DependencyManager {
/** 生成并应用依赖数组 */
internal fun generateAndApply() {
Dependencies.generate(SweetDependencyConfigs.document.plugins(), SweetDependencyConfigs.document.libraries())
val pluginsSize = Dependencies.plugins().size
val librariesSize = Dependencies.libraries().size
if (Dependencies.isNotEmpty()) SLog.verbose(
"${SweetDependency.TAG} help you autowired ${pluginsSize + librariesSize} dependencies" +
" (plugins: $pluginsSize, libraries: $librariesSize)", SLog.STRNG
)
}
/**
* 生成并应用依赖数组
* @param settings 当前设置
*/
internal fun generateAndApply(settings: Settings) {
val isDisableOnSync = SweetDependencyConfigs.document.preferences().autowireOnSyncMode == PreferencesDocument.AutowireOnSyncMode.OFF
var requiresAutowiringLibrariesSize = 0
Dependencies.all().forEach { (_, artifact) -> if (artifact.version().isAutowire) requiresAutowiringLibrariesSize++ }
if (requiresAutowiringLibrariesSize > 0 && GradleHelper.isSyncMode && isDisableOnSync) SLog.warn(
"Found $requiresAutowiringLibrariesSize dependencies need to be autowired, " +
"please manually run \"${GradleTaskManager.AUTOWIRE_DEPENDENCIES_TASK_NAME}\" task and re-run Gradle Sync"
)
DependencyDeployHelper.generateVersionCatalogs(settings)
}
/**
* 初始化库依赖可访问类
* @param rootProject 当前根项目
*/
internal fun resolve(rootProject: Project) = DependencyDeployHelper.resolveAccessors(rootProject)
/**
* 部署依赖
* @param rootProject 当前根项目
*/
internal fun deploy(rootProject: Project) {
/**
* 为 Groovy 创建扩展方法
* @param extension 当前扩展实例
*/
fun Project.deployForGroovy(extension: ExtensionAware) {
if (buildFile.name.endsWith(".gradle"))
extension.getOrCreate<SweetDependencyAutowireExtension>(SweetDependencyAutowireExtension.NAME, this)
}
/**
* 部署到当前项目
* @param extension 当前扩展实例
*/
fun Project.deployEach(extension: ExtensionAware) = DependencyDeployHelper.deployAccessors(project = this, extension)
/** 适配 Kotlin Multiplatform */
fun Project.deployForKotlinMultiplatform() =
waitForPluginAdded("org.jetbrains.kotlin.multiplatform") {
get("kotlin").also { extension ->
deployForGroovy(extension)
deployEach(extension)
}
}
/** 部署到当前项目 */
fun Project.deploy() {
deployForGroovy(dependencies)
deployEach(dependencies)
deployForKotlinMultiplatform()
}
rootProject.deploy()
rootProject.subprojects.forEach { it.deploy() }
}
/**
* 自动装配、更新依赖
* @param updateMode 更新模式
* @param isRunningOnSync 是否在 Gradle Sync 时运行 - 默认是
*/
internal fun autowireAndUpdate(updateMode: DependencyUpdateMode, isRunningOnSync: Boolean = true) {
/**
* 在适当时候打印 Log
* @param msg 消息内容
* @param symbol 前缀符号
*/
fun logIfNeeded(msg: String, symbol: String) = if (isRunningOnSync) SLog.verbose(msg, symbol) else SLog.info(msg, symbol)
/**
* 通过指定类型和条件查找依赖数组
* @param condition 条件方法体
* @return [DependencyMap]
*/
fun findByType(condition: DependenciesCondition) = when (updateMode.dependencyType) {
DependencyUpdateMode.DependencyType.ALL -> Dependencies.findAll(condition)
DependencyUpdateMode.DependencyType.PLUGINS -> Dependencies.findPlugins(condition)
DependencyUpdateMode.DependencyType.LIBRARIES -> Dependencies.findLibraries(condition)
}
if (Repositories.isEmpty()) return SLog.warn(
"""
Repositories is empty, ${SweetDependency.TAG} will stop auto update dependencies
This will cause Gradle fail to load dependencies, you must add some repositories and enable them
""".trimIndent()
)
if (GradleHelper.isOfflineMode) SLog.warn("Gradle is in offline mode, some dependencies on online repositories will be ignored")
val isOnlyAutowireMode = updateMode.updateType == DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE
var currentIndex = 0
var updPluginsCount = 0
var updLbrariesCount = 0
val needUpdateDependencies = mutableMapOf<String, Pair<DependencyName, DependencyVersion>>()
findByType { _, artifact ->
artifact.version().isNoSpecific.not() && (artifact.versionRef.isBlank() &&
((updateMode.updateType == DependencyUpdateMode.UpdateType.UPDATE_ALL ||
(updateMode.updateType == DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL &&
(artifact.version().isOptional || artifact.version().isAutowire)) ||
(updateMode.updateType == DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE && artifact.version().isAutowire))))
}.apply {
if (isNotEmpty()) SLog.info("Starting ${when (updateMode.updateType) {
DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL -> "autowire and update $size optional dependencies"
DependencyUpdateMode.UpdateType.UPDATE_ALL -> "autowire and update all $size dependencies"
DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE -> "autowire $size dependencies"
}}", SLog.ANLZE)
forEach { (dependencyName, artifact) ->
val versionFilterExclusionList = artifact.versionFilter?.exclusionList()
?: SweetDependencyConfigs.document.preferences().versionFilter.exclusionList()
fetchUpdate(
positionTagName = "${++currentIndex}/$size",
dependencyName = dependencyName,
currentVersion = artifact.version(),
alternateVersions = artifact.versions().values.toMutableList(),
isAutoUpdate = artifact.isAutoUpdate,
versionFilterExclusionList = versionFilterExclusionList,
repositories = artifact.repositories()
) { newVersion ->
DependencyAutowireLogHelper.record(dependencyName, artifact.version(), newVersion)
artifact.updateVersion(newVersion)
needUpdateDependencies[dependencyName.current] = dependencyName to newVersion
when (dependencyName.type) {
DependencyName.Type.PLUGIN -> updPluginsCount++
DependencyName.Type.LIBRARY -> updLbrariesCount++
}
}
}
}
/** 找出所有版本引用依赖 - 为其设置版本 */
needUpdateDependencies.forEach { (notation, dependencyData) ->
val alias = findByType { key, _ -> dependencyData.first == key }.single()?.value?.alias ?: ""
findByType { _, artifact ->
artifact.versionRef.isNotBlank() && (artifact.versionRef == notation || artifact.versionRef == alias)
}.forEach { (dependencyName, artifact) ->
artifact.updateVersion(dependencyData.second)
SLog.info("Link ${dependencyName.description} to ${dependencyData.first.description} version ${dependencyData.second}", SLog.LINK)
}
}
if (needUpdateDependencies.isNotEmpty()) {
SLog.info(
(if (isOnlyAutowireMode) "Autowired" else "Autowired and updated") +
" ${needUpdateDependencies.size} dependencies (plugins: $updPluginsCount, libraries: $updLbrariesCount)", SLog.DONE
)
if (SweetDependencyConfigs.configs.isEnableDependenciesAutowireLog)
logIfNeeded(msg = "Autowiring logs have been automatically written to: ${DependencyAutowireLogHelper.logFile}", SLog.LINK)
SweetDependencyConfigs.documentMapping.updateDependencies(needUpdateDependencies)
if (isRunningOnSync.not()) SLog.warn(
"""
**************************** NOTICE ****************************
${needUpdateDependencies.size} dependencies (plugins: $updPluginsCount, libraries: $updLbrariesCount) has been changed
You must to manually re-run Gradle Sync to apply those changes
**************************** NOTICE ****************************
""".trimIndent(), noTag = true
)
Dependencies.refreshState(isOutdate = true)
} else logIfNeeded(msg = "No dependencies need to ${if (isOnlyAutowireMode) "autowire" else "autowire and update"}", SLog.DONE)
}
/**
* 自动装配或更新当前依赖
* @param positionTagName 当前位置标签名称
* @param dependencyName 依赖名称
* @param currentVersion 当前依赖版本
* @param alternateVersions 备选依赖版本数组
* @param isAutoUpdate 是否自动更新
* @param versionFilterExclusionList 版本管理器排除列表
* @param repositories 使用的存储库数组
* @param result 回调新版本
*/
private inline fun fetchUpdate(
positionTagName: String,
dependencyName: DependencyName,
currentVersion: DependencyVersion,
alternateVersions: MutableList<DependencyVersion>,
isAutoUpdate: Boolean,
versionFilterExclusionList: VersionFilterDocument.ExclusionList,
repositories: RepositoryList,
result: (newVersion: DependencyVersion) -> Unit
) {
val poms = mutableListOf<MavenMetadata>()
val headerInfo = if (GradleHelper.isOfflineMode) "$positionTagName > OFFLINE" else "$positionTagName > NOT-FOUND"
val displayInfo = "${dependencyName.description} ${currentVersion.let{ if (it.isAutowire) "" else "version $it" }}"
(repositories.noEmpty() ?: Repositories.all()).apply {
forEachIndexed { index, entry ->
val currentVersionFilterExclusionList = versionFilterExclusionList.depends(currentVersion)
val availableVersions = mutableListOf<DependencyVersion>()
poms.add(MavenParser.acquire(dependencyName, entry, currentVersion))
if (index == lastIndex) poms.noEmpty()
?.sortedByDescending { it.lastUpdated }
?.let { if (it.all { e -> e.lastUpdated <= 0L }) it.sortedByDescending { e -> e.versions.size } else it }
?.filter { it.versions.isNotEmpty() }
?.let { linkedSetOf<DependencyVersion>().apply { it.forEach { entity -> addAll(entity.versions) } }.toMutableList() }
?.let { availableVersions.addAll(it); currentVersionFilterExclusionList.filter(it) }
?.also {
if (currentVersionFilterExclusionList.isNotEmpty() && availableVersions.isNotEmpty() && it.isEmpty()) SLog.warn(
"""
${dependencyName.description} available versions exclusion to nothing by version filter
All available versions have been filtered, if this is wrong, please reconfigure your version filter
You can disable internal version filter like following:
""".trimIndent() + "\n" + when (dependencyName.type) {
DependencyName.Type.PLUGIN -> """
${dependencyName.groupId}:
version-filter:
use-internal: false
...
""".trimIndent()
DependencyName.Type.LIBRARY -> """
${dependencyName.groupId}:
${dependencyName.artifactId}:
version-filter:
use-internal: false
...
""".trimIndent()
} + "\n" + """
Available versions: $availableVersions
Version filter: $currentVersionFilterExclusionList
""".trimIndent()
)
}?.noEmpty()?.also { versions ->
resolveEachUpdate(
positionTagName, dependencyName, versions, currentVersion,
versions.first(), alternateVersions, isAutoUpdate, result
)
} ?: if (GradleHelper.isOfflineMode) SLog.warn("$headerInfo $displayInfo") else SLog.error("$headerInfo $displayInfo")
}
}
}
/**
* 自动装配或更新每项依赖
* @param positionTagName 当前位置标签名称
* @param dependencyName 依赖名称
* @param versions 全部可用依赖版本数组
* @param currentVersion 当前依赖版本
* @param latestVersion 最新依赖版本
* @param alternateVersions 备选依赖版本数组
* @param isAutoUpdate 是否自动更新
* @param result 回调新版本
*/
private inline fun resolveEachUpdate(
positionTagName: String,
dependencyName: DependencyName,
versions: MutableList<DependencyVersion>,
currentVersion: DependencyVersion,
latestVersion: DependencyVersion,
alternateVersions: MutableList<DependencyVersion>,
isAutoUpdate: Boolean,
result: (newVersion: DependencyVersion) -> Unit
) = when {
currentVersion.isAutowire.not() && versions.contains(currentVersion).not() ->
SLog.warn("$positionTagName > MISSING ${dependencyName.description} version $currentVersion, available are $versions")
currentVersion.isAutowire.not() && alternateVersions.isNotEmpty() && alternateVersions.all { versions.contains(it) }.not() ->
SLog.warn("$positionTagName > MISSING ${dependencyName.description} version alias $alternateVersions, available are $versions")
latestVersion != currentVersion -> when {
currentVersion.isAutowire -> {
SLog.info("$positionTagName > AUTOWIRE ${dependencyName.description} version $latestVersion", SLog.WIRE)
result(latestVersion)
}
isAutoUpdate -> {
SLog.info("$positionTagName > AUTO-UPDATE ${dependencyName.description} version $currentVersion -> $latestVersion", SLog.UP)
result(latestVersion)
}
else -> SLog.note("$positionTagName > UPDATE-AVAILABLE ${dependencyName.description} version $currentVersion -> $latestVersion", SLog.ROTATE)
}
else -> SLog.info("$positionTagName > UP-TO-DATE ${dependencyName.description} version $currentVersion", SLog.DONE)
}
}

View File

@@ -0,0 +1,120 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.manager
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
import com.highcapable.sweetdependency.gradle.factory.createTask
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.plugin.task.AutowireDependenciesTask
import com.highcapable.sweetdependency.plugin.task.AutowireLibrariesTask
import com.highcapable.sweetdependency.plugin.task.AutowirePluginsTask
import com.highcapable.sweetdependency.plugin.task.CreateDependenciesMigrationTemplateTask
import com.highcapable.sweetdependency.plugin.task.SweetDependencyDebugTask
import com.highcapable.sweetdependency.plugin.task.UpdateAllDependenciesTask
import com.highcapable.sweetdependency.plugin.task.UpdateAllLibrariesTask
import com.highcapable.sweetdependency.plugin.task.UpdateAllPluginsTask
import com.highcapable.sweetdependency.plugin.task.UpdateOptionalDependenciesTask
import com.highcapable.sweetdependency.plugin.task.UpdateOptionalLibrariesTask
import com.highcapable.sweetdependency.plugin.task.UpdateOptionalPluginsTask
import org.gradle.api.Project
/**
* Gradle Task 管理类
*/
internal object GradleTaskManager {
/** Gradle Task 分组名称 */
internal const val TASK_GROUP_NAME = SweetDependencyProperties.PROJECT_MODULE_NAME
/** 创建依赖迁移模板 Gradle Task 名称 */
internal const val CREATE_DEPENDENCIES_MIGRATION_TEMPLATE_TASK_NAME = "createDependenciesMigrationTemplate"
/** 依赖自动装配、更新 (可选) (插件依赖 + 库依赖) Gradle Task 名称 */
internal const val UPDATE_OPTIONAL_DEPENDENCIES_TASK_NAME = "updateOptionalDependencies"
/** 依赖自动装配、更新 (全部) (插件依赖 + 库依赖) Gradle Task 名称 */
internal const val UPDATE_ALL_DEPENDENCIES_TASK_NAME = "updateAllDependencies"
/** 依赖自动装配 (插件依赖 + 库依赖) Gradle Task 名称 */
internal const val AUTOWIRE_DEPENDENCIES_TASK_NAME = "autowireDependencies"
/** 依赖自动装配、更新 (可选) (插件依赖) Gradle Task 名称 */
internal const val UPDATE_OPTIONAL_PLUGINS_TASK_NAME = "updateOptionalPlugins"
/** 依赖自动装配、更新 (全部) (插件依赖) Gradle Task 名称 */
internal const val UPDATE_ALL_PLUGINS_TASK_NAME = "updateAllPlugins"
/** 插件依赖自动装配 (插件依赖) Gradle Task 名称 */
internal const val AUTOWIRE_PLUGINS_TASK_NAME = "autowirePlugins"
/** 依赖自动装配、更新 (可选) (库依赖) Gradle Task 名称 */
internal const val UPDATE_OPTIONAL_LIBRARIES_TASK_NAME = "updateOptionalLibraries"
/** 依赖自动装配、更新 (全部) (库依赖) Gradle Task 名称 */
internal const val UPDATE_ALL_LIBRARIES_TASK_NAME = "updateAllLibraries"
/** 依赖自动装配 (库依赖) Gradle Task 名称 */
internal const val AUTOWIRE_LIBRARIES_TASK_NAME = "autowireLibraries"
/** 调试 Gradle Task 名称 */
internal const val SWEET_DEPENDENCY_DEBUG_TASK_NAME = "sweetDependencyDebug"
/**
* 当前正在运行的是否为 [SweetDependency] 内部 Gradle Task
* @return [Boolean]
*/
internal val isInternalRunningTask
get() = GradleHelper.runningTaskNames.orEmpty().any {
it == CREATE_DEPENDENCIES_MIGRATION_TEMPLATE_TASK_NAME ||
it == UPDATE_OPTIONAL_DEPENDENCIES_TASK_NAME ||
it == UPDATE_ALL_DEPENDENCIES_TASK_NAME ||
it == AUTOWIRE_DEPENDENCIES_TASK_NAME ||
it == UPDATE_OPTIONAL_PLUGINS_TASK_NAME ||
it == UPDATE_ALL_PLUGINS_TASK_NAME ||
it == AUTOWIRE_PLUGINS_TASK_NAME ||
it == UPDATE_OPTIONAL_LIBRARIES_TASK_NAME ||
it == UPDATE_ALL_LIBRARIES_TASK_NAME ||
it == AUTOWIRE_LIBRARIES_TASK_NAME ||
it == SWEET_DEPENDENCY_DEBUG_TASK_NAME
}
/**
* 注册 [SweetDependency] 全部 Gradle Task
* @param rootProject 根项目
*/
internal fun register(rootProject: Project) {
rootProject.createTask<CreateDependenciesMigrationTemplateTask>(TASK_GROUP_NAME, CREATE_DEPENDENCIES_MIGRATION_TEMPLATE_TASK_NAME)
rootProject.createTask<UpdateOptionalDependenciesTask>(TASK_GROUP_NAME, UPDATE_OPTIONAL_DEPENDENCIES_TASK_NAME)
rootProject.createTask<UpdateAllDependenciesTask>(TASK_GROUP_NAME, UPDATE_ALL_DEPENDENCIES_TASK_NAME)
rootProject.createTask<AutowireDependenciesTask>(TASK_GROUP_NAME, AUTOWIRE_DEPENDENCIES_TASK_NAME)
rootProject.createTask<UpdateOptionalPluginsTask>(TASK_GROUP_NAME, UPDATE_OPTIONAL_PLUGINS_TASK_NAME)
rootProject.createTask<UpdateAllPluginsTask>(TASK_GROUP_NAME, UPDATE_ALL_PLUGINS_TASK_NAME)
rootProject.createTask<AutowirePluginsTask>(TASK_GROUP_NAME, AUTOWIRE_PLUGINS_TASK_NAME)
rootProject.createTask<UpdateOptionalLibrariesTask>(TASK_GROUP_NAME, UPDATE_OPTIONAL_LIBRARIES_TASK_NAME)
rootProject.createTask<UpdateAllLibrariesTask>(TASK_GROUP_NAME, UPDATE_ALL_LIBRARIES_TASK_NAME)
rootProject.createTask<AutowireLibrariesTask>(TASK_GROUP_NAME, AUTOWIRE_LIBRARIES_TASK_NAME)
rootProject.createTask<SweetDependencyDebugTask>(TASK_GROUP_NAME, SWEET_DEPENDENCY_DEBUG_TASK_NAME)
}
}

View File

@@ -0,0 +1,131 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/6.
*/
package com.highcapable.sweetdependency.manager
import com.highcapable.sweetdependency.document.PreferencesDocument
import com.highcapable.sweetdependency.document.RepositoryDocument
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
import com.highcapable.sweetdependency.manager.const.AdditionalRepositories
import com.highcapable.sweetdependency.manager.content.Repositories
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.noBlank
import com.highcapable.sweetdependency.utils.noEmpty
import com.highcapable.sweetdependency.utils.toFile
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.artifacts.repositories.AuthenticationSupported
import org.gradle.api.artifacts.repositories.UrlArtifactRepository
import org.gradle.api.initialization.Settings
import java.net.URI
import org.gradle.api.initialization.resolve.RepositoriesMode as GradleRepositoriesMode
/**
* 存储库装配管理类
*/
internal object RepositoryManager {
/**
* 生成并应用存储库数组
* @param settings 当前设置
*/
internal fun generateAndApply(settings: Settings) {
val repositories = SweetDependencyConfigs.document.repositories()
Repositories.generate(repositories)
/**
* 应用存储库数组到 Gradle
* @param isPlugins 当前应用类型是否为插件依赖
*/
fun RepositoryHandler.apply(isPlugins: Boolean) = repositories.forEach {
if (it.isIncludeScope(isPlugins)) when (it.nodeType) {
RepositoryDocument.RepositoryType.GOOGLE -> google { applyToArtifact(it) }
RepositoryDocument.RepositoryType.MAVEN_CENTRAL -> mavenCentral { applyToArtifact(it) }
RepositoryDocument.RepositoryType.MAVEN_LOCAL -> mavenLocal { applyToArtifact(it) }
RepositoryDocument.RepositoryType.MAVEN -> maven { applyToArtifact(it) }
RepositoryDocument.RepositoryType.GRADLE_PLUGIN_PORTAL -> gradlePluginPortal { applyToArtifact(it) }
else -> {}
}
}
settings.pluginManagement {
this.repositories.clear()
this.repositories.apply(isPlugins = true)
}
settings.dependencyResolutionManagement {
this.repositoriesMode.set(when (SweetDependencyConfigs.document.preferences().repositoriesMode) {
PreferencesDocument.RepositoriesMode.PREFER_PROJECT -> GradleRepositoriesMode.PREFER_PROJECT
PreferencesDocument.RepositoriesMode.PREFER_SETTINGS -> GradleRepositoriesMode.PREFER_SETTINGS
PreferencesDocument.RepositoriesMode.FAIL_ON_PROJECT_REPOS -> GradleRepositoriesMode.FAIL_ON_PROJECT_REPOS
})
this.repositories.clear()
this.repositories.apply(isPlugins = false)
}
}
/**
* 应用存储库到 [ArtifactRepository]
* @param document 存储库配置项文档实体
*/
private fun ArtifactRepository.applyToArtifact(document: RepositoryDocument) {
document.nodeName.noBlank()?.also { docName -> this.name = docName }
if (this is AuthenticationSupported && document.credentials.let { it.username.isNotBlank() || it.password.isNotBlank() })
credentials { this.username = document.credentials.username; this.password = document.credentials.password }
if (document.content.isEmpty().not()) content {
/**
* 使用 ":" 分割字符串
* @param size 期望的个数
* @param result 回调每项
* @return [List]<[String]>
* @throws SweetDependencyUnresolvedException 如果 [size] 不是期望的个数
*/
fun List<String>.forEachParams(size: Int, result: (List<String>) -> Unit) = forEach {
result(it.split(":").also { e -> if (e.size != size) SError.make("Missing argument in content configuration") })
}
document.content.exclude.also {
it.group().noEmpty()?.forEach { e -> excludeGroup(e) }
it.groupAndSubgroups().noEmpty()?.forEach { e -> excludeGroupAndSubgroups(e) }
it.groupByRegex().noEmpty()?.forEach { e -> excludeGroupByRegex(e) }
it.module().noEmpty()?.forEachParams(size = 2) { e -> excludeModule(e[0], e[1]) }
it.moduleByRegex().noEmpty()?.forEachParams(size = 2) { e -> excludeModuleByRegex(e[0], e[1]) }
it.version().noEmpty()?.forEachParams(size = 3) { e -> excludeVersion(e[0], e[1], e[2]) }
it.versionByRegex().noEmpty()?.forEachParams(size = 3) { e -> excludeVersionByRegex(e[0], e[1], e[2]) }
}
document.content.include.also {
it.group().noEmpty()?.forEach { e -> includeGroup(e) }
it.groupAndSubgroups().noEmpty()?.forEach { e -> includeGroupAndSubgroups(e) }
it.groupByRegex().noEmpty()?.forEach { e -> includeGroupByRegex(e) }
it.module().noEmpty()?.forEachParams(size = 2) { e -> includeModule(e[0], e[1]) }
it.moduleByRegex().noEmpty()?.forEachParams(size = 2) { e -> includeModuleByRegex(e[0], e[1]) }
it.version().noEmpty()?.forEachParams(size = 3) { e -> includeVersion(e[0], e[1], e[2]) }
it.versionByRegex().noEmpty()?.forEachParams(size = 3) { e -> includeVersionByRegex(e[0], e[1], e[2]) }
}
}
if (document.nodeType != RepositoryDocument.RepositoryType.MAVEN_LOCAL)
(document.url.noBlank()?.let {
/** JCenter 已经终止服务 - 不确定其镜像源是否也会同时关闭 */
if (it == AdditionalRepositories.ALIYUN_JCENTER_MIRROR)
SLog.warn("JCenter has shut down, and its mirror server may stop soon, please transfer to other repositories")
URI.create(it)
} ?: document.path.noBlank()?.toFile()?.toURI())
?.also { uri -> if (this is UrlArtifactRepository) this.url = uri }
}
}

View File

@@ -0,0 +1,82 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/16.
*/
package com.highcapable.sweetdependency.manager.const
/**
* 附加常用第三方存储库
*/
internal object AdditionalRepositories {
/** 中央存储库 (分流) */
const val MAVEN_CENTRAL_BRANCH = "https://repo1.maven.org/maven2"
/** JitPack */
const val JITPACK = "https://www.jitpack.io"
/** OSS Release */
const val SONATYPE_OSS_RELEASES = "https://s01.oss.sonatype.org/content/repositories/releases"
/** OSS Snapshots */
const val SONATYPE_OSS_SNAPSHOTS = "https://s01.oss.sonatype.org/content/repositories/snapshots"
/** 阿里云 Google 存储库镜像 */
const val ALIYUN_GOOGLE_MIRROR = "https://maven.aliyun.com/repository/google"
/** 阿里云中央存储库镜像 */
const val ALIYUN_MAVEN_CENTRAL_MIRROR = "https://maven.aliyun.com/repository/central"
/** 阿里云公共存储库镜像 */
const val ALIYUN_MAVEN_PUBLIC_MIRROR = "https://maven.aliyun.com/repository/public"
/** 阿里云 JCenter 镜像 */
const val ALIYUN_JCENTER_MIRROR = "https://maven.aliyun.com/nexus/content/repositories/jcenter"
/**
* 存储库简洁名称定义类
*/
internal object Name {
/** 中央存储库 (分流) */
const val MAVEN_CENTRAL_BRANCH = "maven-central-branch"
/** JitPack */
const val JITPACK = "jit-pack"
/** OSS Release */
const val SONATYPE_OSS_RELEASES = "sonatype-oss-releases"
/** OSS Snapshots */
const val SONATYPE_OSS_SNAPSHOTS = "sonatype-oss-snapshots"
/** 阿里云 Google 存储库镜像 */
const val ALIYUN_GOOGLE_MIRROR = "aliyun-google-mirror"
/** 阿里云中央存储库镜像 */
const val ALIYUN_MAVEN_CENTRAL_MIRROR = "aliyun-maven-central-mirror"
/** 阿里云公共存储库镜像 */
const val ALIYUN_MAVEN_PUBLIC_MIRROR = "aliyun-maven-public-mirror"
/** 阿里云 JCenter 镜像 */
const val ALIYUN_JCENTER_MIRROR = "aliyun-jcenter-mirror"
}
}

View File

@@ -0,0 +1,64 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/18.
*/
package com.highcapable.sweetdependency.manager.const
/**
* 内置存储库
*/
internal object InternalRepositories {
/** 本地 Maven 存储库相对路径 */
const val MAVEN_LOCAL_RELATIVE_PATH = ".m2/repository"
/** Google Maven */
const val GOOGLE = "https://dl.google.com/dl/android/maven2"
/** 中央存储库 */
const val MAVEN_CENTRAL = "https://repo.maven.apache.org/maven2"
/** Gradle Plugin 存储库 */
const val GRADLE_PLUGIN_PORTAL = "https://plugins.gradle.org/m2"
/**
* 存储库简洁名称定义类
*/
internal object Name {
/** Google Maven */
const val GOOGLE = "google"
/** 中央存储库 */
const val MAVEN_CENTRAL = "maven-central"
/** 本地 Maven 存储库 */
const val MAVEN_LOCAL = "maven-local"
/** Maven 存储库 */
const val MAVEN = "maven"
/** Ivy 存储库 */
const val IVY = "ivy"
/** Gradle Plugin 存储库 */
const val GRADLE_PLUGIN_PORTAL = "gradle-plugin-portal"
}
}

View File

@@ -0,0 +1,150 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/25.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.manager.content
import com.highcapable.sweetdependency.document.DependencyDocument
import com.highcapable.sweetdependency.document.factory.DependenciesCondition
import com.highcapable.sweetdependency.document.factory.DependencyMap
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.utils.filter
/**
* 已添加的依赖管理类
*/
internal object Dependencies {
/** 当前已添加的全部插件依赖数组 */
private val pluginEntries = mutableMapOf<DependencyName, DependencyDocument>()
/** 当前已添加的全部库依赖数组 */
private val libraryEntries = mutableMapOf<DependencyName, DependencyDocument>()
/** 标识当前是否为已过期状态 */
private var isMarkedOutdate = true
/**
* 获取当前过期状态
* @return [Boolean]
*/
internal val isOutdate get() = isMarkedOutdate
/**
* 刷新当前缓存数据状态
* @param isOutdate 是否标识为已过期
*/
internal fun refreshState(isOutdate: Boolean) {
isMarkedOutdate = isOutdate
}
/**
* 获取当前全部数组
* @return [DependencyMap]
*/
internal fun all() = (pluginEntries + libraryEntries).toMutableMap()
/**
* 获取当前插件依赖数组
* @return [DependencyMap]
*/
internal fun plugins() = pluginEntries
/**
* 获取当前库依赖数组
* @return [DependencyMap]
*/
internal fun libraries() = libraryEntries
/**
* 当前是否存在依赖
* @return [Boolean]
*/
internal fun isEmpty() = all().isEmpty()
/**
* 当前是否不存在依赖
* @return [Boolean]
*/
internal fun isNotEmpty() = isEmpty().not()
/**
* 查找是否存在指定的依赖
* @param condition 条件方法体
* @return [Boolean]
*/
internal inline fun hasAll(condition: DependenciesCondition) = findAll { key, value -> condition(key, value) }.isNotEmpty()
/**
* 查找是否存在指定的插件依赖
* @param condition 条件方法体
* @return [Boolean]
*/
internal inline fun hasPlugin(condition: DependenciesCondition) = findPlugins { key, value -> condition(key, value) }.isNotEmpty()
/**
* 查找是否存在指定的库依赖
* @param condition 条件方法体
* @return [Boolean]
*/
internal inline fun hasLibrary(condition: DependenciesCondition) = findLibraries { key, value -> condition(key, value) }.isNotEmpty()
/**
* 查找指定条件的依赖数组
* @param condition 条件方法体
* @return [DependencyMap]
*/
internal inline fun findAll(condition: DependenciesCondition) = all().filter { condition(it.key, it.value) }
/**
* 查找指定条件的插件依赖数组
* @param condition 条件方法体
* @return [DependencyMap]
*/
internal inline fun findPlugins(condition: DependenciesCondition) = plugins().filter { condition(it.key, it.value) }
/**
* 查找指定条件的库依赖数组
* @param condition 条件方法体
* @return [DependencyMap]
*/
internal inline fun findLibraries(condition: DependenciesCondition) = libraries().filter { condition(it.key, it.value) }
/**
* 生成依赖数组
* @param plugins 插件依赖数组
* @param libraries 依赖数组
*/
internal fun generate(plugins: DependencyMap, libraries: DependencyMap) {
if (plugins == plugins() && libraries == libraries()) return refreshState(isOutdate = false)
resetData()
plugins().putAll(plugins)
libraries().putAll(libraries)
}
/** 重置 (清空) 当前依赖数组 */
private fun resetData() {
pluginEntries.clear()
libraryEntries.clear()
refreshState(isOutdate = true)
}
}

View File

@@ -0,0 +1,93 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/16.
*/
package com.highcapable.sweetdependency.manager.content
import com.highcapable.sweetdependency.document.RepositoryDocument
import com.highcapable.sweetdependency.document.factory.RepositoryList
import com.highcapable.sweetdependency.manager.const.AdditionalRepositories
import com.highcapable.sweetdependency.manager.const.InternalRepositories
import com.highcapable.sweetdependency.utils.parseFileSeparator
/**
* 已添加的存储库管理类
*/
internal object Repositories {
/** 默认本地 Maven 存储库路径 */
internal val defaultMavenLocalPath by lazy {
"${System.getProperty("user.home")}/${InternalRepositories.MAVEN_LOCAL_RELATIVE_PATH}".parseFileSeparator()
}
/** 默认版本过滤器排除列表数组 */
internal val defaultVersionFilterExclusionList = arrayOf("-beta", "-alpha", "-dev", "-canary", "-pre", "-rc", "-ga", "-snapshot")
/** 当前已添加的全部存储库数组 */
private val entries = mutableListOf<RepositoryDocument>()
/**
* 获取当前存储库数组
* @return [MutableList]<[RepositoryDocument]>
*/
internal fun all() = entries
/**
* 当前是否存在存储库
* @return [Boolean]
*/
internal fun isEmpty() = all().isEmpty()
/**
* 当前是否不存在存储库
* @return [Boolean]
*/
internal fun isNotEmpty() = isEmpty().not()
/**
* 生成存储库数组
* @param repositories 存储库数组
*/
internal fun generate(repositories: RepositoryList) {
if (repositories == all()) return
resetData()
all().addAll(repositories)
}
/**
* 查找可用的存储库名 URL 地址
* @param name 存储库名
* @return [String]
*/
internal fun findAdditional(name: String) = when (name) {
AdditionalRepositories.Name.MAVEN_CENTRAL_BRANCH -> AdditionalRepositories.MAVEN_CENTRAL_BRANCH
AdditionalRepositories.Name.JITPACK -> AdditionalRepositories.JITPACK
AdditionalRepositories.Name.SONATYPE_OSS_RELEASES -> AdditionalRepositories.SONATYPE_OSS_RELEASES
AdditionalRepositories.Name.SONATYPE_OSS_SNAPSHOTS -> AdditionalRepositories.SONATYPE_OSS_SNAPSHOTS
AdditionalRepositories.Name.ALIYUN_GOOGLE_MIRROR -> AdditionalRepositories.ALIYUN_GOOGLE_MIRROR
AdditionalRepositories.Name.ALIYUN_MAVEN_CENTRAL_MIRROR -> AdditionalRepositories.ALIYUN_MAVEN_CENTRAL_MIRROR
AdditionalRepositories.Name.ALIYUN_MAVEN_PUBLIC_MIRROR -> AdditionalRepositories.ALIYUN_MAVEN_PUBLIC_MIRROR
AdditionalRepositories.Name.ALIYUN_JCENTER_MIRROR -> AdditionalRepositories.ALIYUN_JCENTER_MIRROR
else -> ""
}
/** 重置 (清空) 当前存储库数组 */
private fun resetData() = entries.clear()
}

View File

@@ -0,0 +1,61 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/21.
*/
package com.highcapable.sweetdependency.manager.helper
import com.highcapable.sweetdependency.environment.Environment
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SLog
import java.text.SimpleDateFormat
import java.util.*
/**
* 依赖自动装配日志工具类
*/
internal object DependencyAutowireLogHelper {
/** 当前日志文件名 */
private const val LOG_FILE_NAME = "dependencies-autowire.log"
/**
* 当前日志文件
* @return [String]
*/
internal val logFile get() = Environment.memoryDir(LOG_FILE_NAME)
/**
* 记录当前依赖改变
* @param dependencyName 依赖名称
* @param fromVersion 起始版本
* @param toVersion 最终版本
*/
internal fun record(dependencyName: DependencyName, fromVersion: DependencyVersion, toVersion: DependencyVersion) {
if (SweetDependencyConfigs.configs.isEnableDependenciesAutowireLog.not()) return
val versionInfo = if (fromVersion.isAutowire)
"autowire version \"$toVersion\""
else "update version \"$fromVersion\" -> \"$toVersion\""
logFile.runCatching {
appendText("[${SimpleDateFormat.getDateTimeInstance().format(Date())}] ${dependencyName.description} $versionInfo\n")
}.onFailure { SLog.error("Failed to written log file \"$logFile\"\n$it") }
}
}

View File

@@ -0,0 +1,190 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/13.
*/
package com.highcapable.sweetdependency.manager.helper
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.environment.Environment
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
import com.highcapable.sweetdependency.gradle.delegate.ProjectTransaction
import com.highcapable.sweetdependency.gradle.delegate.entity.ExternalDependencyDelegate
import com.highcapable.sweetdependency.gradle.factory.addDependencyToBuildScript
import com.highcapable.sweetdependency.gradle.factory.applyPlugin
import com.highcapable.sweetdependency.gradle.factory.getOrCreate
import com.highcapable.sweetdependency.gradle.factory.loadBuildScriptClass
import com.highcapable.sweetdependency.manager.GradleTaskManager
import com.highcapable.sweetdependency.manager.content.Dependencies
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.generator.LibrariesAccessorsGenerator
import com.highcapable.sweetdependency.utils.camelcase
import com.highcapable.sweetdependency.utils.code.entity.MavenPomData
import com.highcapable.sweetdependency.utils.code.factory.compile
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.isEmpty
import com.highcapable.sweetdependency.utils.isValidZip
import com.highcapable.sweetdependency.utils.single
import com.highcapable.sweetdependency.utils.toAbsoluteFilePaths
import com.highcapable.sweetdependency.utils.toFile
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.plugins.ExtensionAware
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependencySpec
/**
* 依赖部署工具类
*/
internal object DependencyDeployHelper {
/** 库依赖可访问 [Class] 标识名称 */
private const val ACCESSORS_NAME = "dependencies-accessors"
/** 库依赖可访问 [Class] 生成目录 */
private val accessorsDir = Environment.memoryDir(ACCESSORS_NAME)
/** 库依赖可访问 [Class] 虚拟依赖数据 */
private val accessorsPomData = MavenPomData(SweetDependencyProperties.PROJECT_GROUP_NAME, ACCESSORS_NAME, SweetDependency.VERSION)
/** 库依赖可访问 [Class] 生成实例 */
private val accessorsGenerator = LibrariesAccessorsGenerator()
/**
* 生成 Version Catalogs
*
* 由于 Gradle API 限制 - 无法针对插件依赖进行自定义 - 所以间接使用 Version Catalogs 生成
* @param settings 当前设置
*/
internal fun generateVersionCatalogs(settings: Settings) {
val pluginsNamespace = SweetDependencyConfigs.document.preferences().dependenciesNamespace.plugins()
runCatching {
settings.dependencyResolutionManagement.versionCatalogs.create(pluginsNamespace) {
Dependencies.plugins().forEach { (dependencyName, artifact) ->
if (artifact.version().isNoSpecific) return@forEach SLog.warn(
"""
You must specific a version for plugin "$dependencyName" or use Gradle's internal plugin function instead it
This problem came from the version catalogs rules, so will not generate "$dependencyName"
You can also use "autowire("$dependencyName")" to solve this problem
You will see this warning every time, because we don't recommend declaring plugins without version
""".trimIndent()
)
if (artifact.version().isAutowire) SError.make(
"""
This plugin "$dependencyName" is not autowired and cannot be generate
You can try the following solutions to resolve this problem:
1. Manually re-run Gradle Sync (make sure "autowire-on-sync-mode" not be "OFF")
2. Manually run "${GradleTaskManager.AUTOWIRE_PLUGINS_TASK_NAME}" task and re-run Gradle Sync
3. Fill an existing version for plugin "$dependencyName" and re-run Gradle Sync
If you get this error again after doing the above, maybe the currently set repositories cannot find this plugin
""".trimIndent()
)
val deployedName = dependencyName.ambiguousName(symbol = "-", isReplaceFirstChar = true, isLowerCase = false)
plugin(deployedName, dependencyName.current).version(artifact.version().deployed)
artifact.versions().forEach { (name, version) ->
plugin("$deployedName-${name.camelcase()}", dependencyName.current).version(version.deployed)
if (artifact.alias.isNotBlank())
plugin("${artifact.alias}-${name.camelcase()}", dependencyName.current).version(version.deployed)
}
if (artifact.alias.isNotBlank()) plugin(artifact.alias, dependencyName.current).version(artifact.version().deployed)
}
}
}.onFailure {
when (it) {
is InvalidUserDataException -> SError.make("Illegal name called in Gradle version catalogs", it)
else -> throw it
}
}
}
/**
* 处理库依赖可访问 [Class] 装载
* @param rootProject 当前根项目
*/
internal fun resolveAccessors(rootProject: Project) {
if (Dependencies.isOutdate || accessorsDir.isEmpty())
accessorsGenerator.build().compile(accessorsPomData, accessorsDir.absolutePath, accessorsGenerator.compileStubFiles)
rootProject.addDependencyToBuildScript(accessorsDir.absolutePath, accessorsPomData)
}
/**
* 部署库依赖可访问 [Class]
* @param project 当前项目
* @param extension 当前扩展实例
*/
internal fun deployAccessors(project: Project, extension: ExtensionAware) =
accessorsGenerator.librariesClasses.forEach { (name, className) ->
extension.getOrCreate(name, project.loadBuildScriptClass(className))
}
/**
* 处理自动装配的插件依赖
* @param spec 当前插件依赖声明对象
* @param params 当前参数数组
* @return [PluginDependencySpec]
*/
internal fun resolveAutowire(spec: PluginDependenciesSpec, params: Array<out Any>): PluginDependencySpec {
if (params.isEmpty()) SError.make("The autowire function need a param to resolve plugin")
if (params.size > 2) SError.make("The autowire function currently does not support more than 2 params of plugin")
return when (params[0]) {
is String -> {
val entry = Dependencies.findPlugins { key, value -> params[0] == key.current || params[0] == value.alias }.single()
?: SError.make("Failed to resolve plugin \"${params[0]}\", also tried alias")
val version = if (params.size == 2)
entry.value.versions()[params[1]] ?: SError.make("Failed to resolve plugin \"${params[0]}\" with version alias \"${params[1]}\"")
else entry.value.version()
spec.applyPlugin(entry.key.current, version.deployed)
}
else -> spec.applyPlugin(params[0])
}
}
/**
* 处理自动装配的依赖
* @param project 当前项目 - 默认为 [ProjectTransaction.current]
* @param params 当前参数数组
* @return [Any]
*/
internal fun resolveAutowire(project: Project = ProjectTransaction.current, params: Array<out String>): Any {
if (params.isEmpty()) SError.make("The autowire function need a param to resolve library")
return if (params[0].let { it.contains("/").not() && it.contains("\\").not() && it.startsWith("(").not() && it.endsWith(")").not() }) {
if (params.size > 2) SError.make("The autowire function currently does not support more than 2 params of external dependency")
val entry = Dependencies.findLibraries { key, value -> params[0] == key.current || params[0] == value.alias }.single()
?: SError.make("Failed to resolve library \"${params[0]}\", also tried alias")
val version = if (params.size == 2)
entry.value.versions()[params[1]] ?: SError.make("Failed to resolve library \"${params[0]}\" with version alias \"${params[1]}\"")
else entry.value.version()
ExternalDependencyDelegate(entry.key.groupId, entry.key.artifactId, version.deployed)
} else mutableListOf<String>().let {
params.forEach { param ->
val relativePath = if (param.startsWith("(") && param.endsWith(")")) param.replace("(", "").replace(")", "") else param
it.addAll(relativePath.toAbsoluteFilePaths(project.projectDir.absolutePath).onEach { path ->
if (path.toFile().isValidZip().not()) SError.make(
"""
Invalid library at file path $path
The file collection dependency needs to be a valid zip package
""".trimIndent()
)
})
}; project.files(it.toTypedArray())
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/6.
*/
package com.highcapable.sweetdependency.manager.maven
import com.highcapable.sweetdependency.document.RepositoryDocument
import com.highcapable.sweetdependency.gradle.entity.DependencyName
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.manager.maven.entity.MavenMetadata
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.executeFileBody
import com.highcapable.sweetdependency.utils.executeUrlBody
import com.highcapable.sweetdependency.utils.noEmpty
import org.xml.sax.InputSource
import java.io.StringReader
import javax.xml.parsers.DocumentBuilderFactory
/**
* Maven 解析器工具类
*/
internal object MavenParser {
/** 依赖配置文件名 */
private const val METADATA_FILE_NAME = "maven-metadata.xml"
/** 依赖配置文件名 (本地) */
private const val METADATA_LOCAL_FILE_NAME = "maven-metadata-local.xml"
/**
* 通过依赖全称使用指定存储库得到 [MavenMetadata] 实体
* @param dependencyName 依赖名称
* @param repo 当前存储库实体
* @param currentVersion 当前依赖版本
* @return [MavenMetadata]
*/
internal fun acquire(dependencyName: DependencyName, repo: RepositoryDocument, currentVersion: DependencyVersion): MavenMetadata {
val headerUrlOrPath = "${repo.url.ifBlank { repo.path }}/${dependencyName.urlName}/"
val isIncludeScope = repo.isIncludeScope(dependencyName.type == DependencyName.Type.PLUGIN)
/** 离线模式下不会自动装配、更新在线依赖 */
if (isIncludeScope && GradleHelper.isOfflineMode) return MavenMetadata()
return when {
repo.url.isNotBlank() -> "$headerUrlOrPath$METADATA_FILE_NAME".executeUrlBody(repo.credentials.username, repo.credentials.password)
repo.path.isNotBlank() -> "$headerUrlOrPath$METADATA_LOCAL_FILE_NAME".executeFileBody()
else -> SError.make("Could not resolve this repository \"${repo.nodeName}\"")
}.trim().toMetadata(currentVersion)
}
/**
* 解析 [METADATA_FILE_NAME]、[METADATA_LOCAL_FILE_NAME] 内容到 [MavenMetadata] 实体
* @param currentVersion 当前依赖版本
* @return [MavenMetadata]
*/
private fun String.toMetadata(currentVersion: DependencyVersion) = runCatching {
if ((contains("<metadata ") || contains("<metadata>")).not() || endsWith("</metadata>").not()) return@runCatching MavenMetadata()
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(InputSource(StringReader(this))).let { document ->
val lastUpdated = document.getElementsByTagName("lastUpdated").item(0)?.textContent?.toLongOrNull() ?: 0L
val versionNodeList = document.getElementsByTagName("version")
val versions = mutableListOf<DependencyVersion>()
for (i in 0..versionNodeList.length) versionNodeList.item(i)?.textContent?.also { versions.add(currentVersion.clone(it)) }
MavenMetadata(versions.noEmpty()?.reversed()?.toMutableList() ?: mutableListOf(), lastUpdated)
}
}.getOrNull() ?: MavenMetadata()
}

View File

@@ -0,0 +1,34 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/6.
*/
package com.highcapable.sweetdependency.manager.maven.entity
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
/**
* Maven Metadata 实体
* @param versions 版本数组
* @param lastUpdated 最后更新时间戳
*/
internal data class MavenMetadata(
internal var versions: MutableList<DependencyVersion> = mutableListOf(),
internal var lastUpdated: Long = 0L
)

View File

@@ -0,0 +1,110 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/1.
*/
package com.highcapable.sweetdependency.manager.transaction
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.DependencyDocument
import com.highcapable.sweetdependency.document.RootConfigDocument
import com.highcapable.sweetdependency.gradle.entity.DependencyVersion
import com.highcapable.sweetdependency.gradle.factory.fullName
import com.highcapable.sweetdependency.gradle.factory.libraries
import com.highcapable.sweetdependency.gradle.factory.plugins
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.gradle.wrapper.type.LibraryDependencyType
import com.highcapable.sweetdependency.manager.GradleTaskManager
import com.highcapable.sweetdependency.manager.content.Dependencies
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.parseFileSeparator
import com.highcapable.sweetdependency.utils.toFile
import com.highcapable.sweetdependency.utils.yaml.Yaml
/**
* 依赖迁移模版管理类
*/
internal object DependencyMigrationTemplateTransaction {
/** 模板文件头部内容 */
private const val TEMPLATE_FILE_HEADER_CONTENT = """
# SweetDependency project configuration template file
# Template files are automatically generated using Gradle task "${GradleTaskManager.CREATE_DEPENDENCIES_MIGRATION_TEMPLATE_TASK_NAME}"
# The automatically generated configuration is determined according to your project
# Please adjust these contents at any time in actual use, the generated content is for reference only, and its availability is unknown
# You can copy the content of the corresponding node in the template file to the project configuration file, and then delete this file
# You can visit ${SweetDependency.PROJECT_URL} for more help
#
# SweetDependency 项目配置模板文件
# 模版文件是使用 Gradle Task "${GradleTaskManager.CREATE_DEPENDENCIES_MIGRATION_TEMPLATE_TASK_NAME}" 自动生成的
# 自动生成的配置根据你的项目决定,请在实际使用中随时调整这些内容,生成的内容仅供参考,其可用性未知
# 你可以复制模板文件中对应节点的内容到项目配置文件,然后删除此文件
# 你可以前往 ${SweetDependency.PROJECT_URL} 以获得更多帮助
"""
/** 模板文件扩展名 */
private const val TEMPLATE_FILE_EXT_NAME = "template.yaml"
/** 模板文件头部内容 */
private val templateFileHeaderContent = TEMPLATE_FILE_HEADER_CONTENT.trimIndent()
/** 排除的部分内置插件名称前缀数组 */
private val exclusionPluginPrefixs = arrayOf("org.gradle", "com.android.internal")
/** 生成模板使用的文档实例 */
private val document = RootConfigDocument()
/** 创建模版 */
internal fun createTemplate() {
SLog.info("Starting analyze projects dependencies structure", SLog.ANLZE)
GradleHelper.allProjects.forEach { subProject ->
subProject.plugins().onEach {
if (exclusionPluginPrefixs.any { prefix -> it.id.startsWith(prefix) }) return@onEach
if (Dependencies.hasPlugin { key, _ -> key.current == it.id }) return@onEach
if (document.plugins == null) document.plugins = mutableMapOf()
val declareDocument = DependencyDocument(version = DependencyVersion.AUTOWIRE_VERSION_NAME)
document.plugins?.set(it.id, declareDocument)
}.apply { if (isNotEmpty()) SLog.info("Found $size plugins in project \"${subProject.fullName}\"", SLog.LINK) }
subProject.libraries().onEach {
if (Dependencies.hasLibrary { key, _ -> key.current == it.toString() }) return@onEach
if (document.libraries == null) document.libraries = mutableMapOf()
if (it.type == LibraryDependencyType.EXTERNAL) document.libraries?.also { entities ->
if (entities[it.groupId] == null) entities[it.groupId] = mutableMapOf()
val declareDocument = DependencyDocument(version = it.version.existed)
entities[it.groupId]?.set(it.artifactId, declareDocument)
}
}.apply { if (isNotEmpty()) SLog.info("Found $size libraries in project \"${subProject.fullName}\"", SLog.LINK) }
}; saveTemplateFile()
}
/** 保存模版到文件 */
private fun saveTemplateFile() {
if (document.plugins?.isEmpty() == true) document.plugins = null
if (document.libraries?.isEmpty() == true) document.libraries = null
if (document.plugins?.isNotEmpty() == true || document.libraries?.isNotEmpty() == true) {
val templateFilePath = SweetDependencyConfigs.configs.configFilePath
.let { it.toFile().let { e -> "${e.parent}/${e.name.split(".")[0]}.$TEMPLATE_FILE_EXT_NAME" } }.parseFileSeparator()
Yaml.parseToFile(document, templateFilePath) { "$templateFileHeaderContent\n\n${replace("\"", "")}" }
SLog.info("Template file is created at $templateFilePath", SLog.DONE)
document.plugins?.clear()
document.libraries?.clear()
} else SLog.info("No suitable dependencies can be found in all projects to create template file, nothing to do", SLog.IGNORE)
}
}

View File

@@ -0,0 +1,261 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/8.
*/
package com.highcapable.sweetdependency.manager.transaction
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.factory.DependencyMap
import com.highcapable.sweetdependency.environment.Environment
import com.highcapable.sweetdependency.gradle.factory.fullName
import com.highcapable.sweetdependency.gradle.factory.libraries
import com.highcapable.sweetdependency.gradle.factory.plugins
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.gradle.wrapper.type.LibraryDependencyType
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.noBlank
import com.highcapable.sweetdependency.utils.noEmpty
/**
* 运行时调试管理类
*/
internal object RuntimeDebugTransaction {
private val configs get() = SweetDependencyConfigs.configs
private val preferences get() = SweetDependencyConfigs.document.preferences()
private val repositories get() = SweetDependencyConfigs.document.repositories()
private val plugins get() = SweetDependencyConfigs.document.plugins(duplicate = true)
private val libraries get() = SweetDependencyConfigs.document.libraries(duplicate = true)
private val vfExclusionList get() = preferences.versionFilter.exclusionList().all()
/** 存储库内存数组 */
private val repositoriesMap = mutableMapOf<String, Any>()
/** 插件依赖内存数组 */
private val pluginsMap = mutableMapOf<String, Any>()
/** 依赖内存数组 */
private val librariesMap = mutableMapOf<String, Any>()
/** 版本过滤器内存数组 */
private val versionFilterMap = mutableMapOf<String, Any>()
/** 项目插件依赖内存数组 */
private val projectPluginsMap = mutableMapOf<String, Any>()
/** 项目库依赖内存数组 */
private val projectLibrariesMap = mutableMapOf<String, Any>()
/** 写出调试信息 */
internal fun dump() {
configureMemoryData()
log(
"""
+--------------------------------------+
| SWEET DEPENDENCY MEMORY DATA DUMP |
+--------------------------------------+
"""
)
log(
"System Environment" value Environment.systemInfo,
"Character Encoding" value Environment.characterEncoding,
"Java Version" value Environment.javaVersion,
"Gradle Version" value GradleHelper.version,
"Plugin Version" value SweetDependency.VERSION,
"Plugin Configuration" to mapOf(
"configFileName" with "(path) ${configs.configFilePath}" to mapOf(
"preferences" to mapOf(
"autowire-on-sync-mode" value preferences.autowireOnSyncMode,
"repositories-mode" value preferences.repositoriesMode,
"dependencies-namespace" to mapOf(
"plugins" value preferences.dependenciesNamespace.plugins().ifBlank { NONE },
"libraries" value preferences.dependenciesNamespace.libraries().ifBlank { NONE }
),
"version-filter" with (if (vfExclusionList.isEmpty()) "(disabled)" else "") to versionFilterMap
),
"repositories" to repositoriesMap,
"plugins" to pluginsMap,
"libraries" to librariesMap
),
"isEnableDependenciesAutowireLog" value configs.isEnableDependenciesAutowireLog,
"isEnableVerboseMode" value configs.isEnableVerboseMode
),
"Project Dependencies" to mapOf(
"Plugins" with (if (projectPluginsMap.isEmpty()) "(load failed)" else "") to projectPluginsMap,
"Libraries" with (if (projectLibrariesMap.isEmpty()) "(load failed)" else "") to projectLibrariesMap
)
)
log(
"""
All debug information has been dumped, if your project is not working properly, please give us feedback on this report
For details, please visit: ${SweetDependency.PROJECT_URL}
"""
)
}
/** 配置内存数据 */
private fun configureMemoryData() {
if (repositoriesMap.isNotEmpty() &&
pluginsMap.isNotEmpty() &&
librariesMap.isNotEmpty() &&
versionFilterMap.isNotEmpty() &&
projectLibrariesMap.isNotEmpty()
) return
repositoriesMap.clear()
pluginsMap.clear()
librariesMap.clear()
versionFilterMap.clear()
projectLibrariesMap.clear()
repositories.forEach { repo ->
val hasCredentials = repo.credentials.let { it.username.isNotBlank() || it.password.isNotBlank() }
val repoMap = mutableMapOf<String, Any>(
"enable" value repo.isEnable,
"url" value repo.url.ifBlank { NONE },
"path" value repo.path.ifBlank { NONE }
)
if (hasCredentials) repoMap["credentials"] = mapOf(
"username" value repo.credentials.username,
"password" value repo.credentials.password
)
repositoriesMap[repo.nodeName] = repoMap
}
plugins.resolveDependencies(pluginsMap)
libraries.resolveDependencies(librariesMap)
if (vfExclusionList.isNotEmpty()) versionFilterMap["exclusionList"] = mutableMapOf<String, Any>()
vfExclusionList.forEach { versionFilterMap["exclusionList"]?.addAsMap(it) }
GradleHelper.allProjects.forEach { subProject ->
projectPluginsMap[subProject.fullName] = mutableMapOf<String, Any>()
projectLibrariesMap[subProject.fullName] = mutableMapOf<String, Any>()
subProject.plugins().forEach { projectPluginsMap[subProject.fullName]?.addAsMap(it.id) }
subProject.libraries().forEach {
val prefix = "(${it.configurationName})"
when (it.type) {
LibraryDependencyType.EXTERNAL, LibraryDependencyType.EXTERNAL_DELEGATE -> {
val suffix = it.version.deployed.noBlank()?.let { e -> ":$e" } ?: ""
projectLibrariesMap[subProject.fullName]?.addAsMap("$prefix ${it.groupId}:${it.artifactId}$suffix")
}
LibraryDependencyType.PROJECT -> projectLibrariesMap[subProject.fullName]?.addAsMap("$prefix (project) ${it.project?.fullName}")
LibraryDependencyType.FILES -> {
val filesMap = mutableMapOf<String, String>()
it.files?.noEmpty()?.forEach { e -> filesMap.addAsMap(e.absolutePath) }?.also {
projectLibrariesMap[subProject.fullName] = mapOf("$prefix (files)" to filesMap)
} ?: projectLibrariesMap[subProject.fullName]?.addAsMap("$prefix (files) not found or empty folder")
}
LibraryDependencyType.OTHERS -> projectLibrariesMap[subProject.fullName]?.addAsMap("$prefix unknown type dependency")
}
}
}
}
/**
* 处理依赖数组
* @param dependenciesMap 依赖内存数组
*/
private fun DependencyMap.resolveDependencies(dependenciesMap: MutableMap<String, Any>) =
forEach { (dependencyName, artifact) ->
val repoMap = mutableMapOf<String, Any>()
val childVersionFilterMap = mutableMapOf<String, Any>()
val childVfExclusionList = artifact.versionFilter?.exclusionList()?.all()
if (childVfExclusionList?.isNotEmpty() == true) childVersionFilterMap["exclusionList"] = mutableMapOf<String, Any>()
childVfExclusionList?.forEach { childVersionFilterMap["exclusionList"]?.addAsMap(it) }
artifact.repositories().forEach { repoMap.addAsMap(it.nodeName) }
dependenciesMap[dependencyName.current] = mapOf(
"alias" value artifact.alias.ifBlank { NONE },
"version" value artifact.version().let { if (it.isNoSpecific) "(no specific)" else it.current },
"auto-update" value artifact.isAutoUpdate,
"version-filter" with (if (childVfExclusionList?.isEmpty() == true) "(disabled)" else "") to childVersionFilterMap,
"repositories" to repoMap,
)
}
/**
* 生成单边 [Pair]
* @param value 键值内容
* @return [Pair]<[String], [String]>
*/
private infix fun String.value(value: Any) = Pair(with(value), "")
/**
* 生成冒号键值对字符串
* @param value 键值内容
* @return [String]
*/
private infix fun String.with(value: Any) = if (value != NONE) "$this${if (value.toString().isBlank()) "" else ": $value"}" else ""
/**
* 任意类型转换为 [MutableMap] 并设置空键值内容
* @param key 键值名称
*/
private fun Any.addAsMap(key: String) {
@Suppress("UNCHECKED_CAST")
(this as MutableMap<String, Any>)[key] = ""
}
/**
* 创建 [MutableMap]
* @param pairs 键值对数组
* @return [MutableMap]<[String], [Any]>
*/
private fun mapOf(vararg pairs: Pair<String, Any>) = mutableMapOf(*pairs)
/**
* 根据 [Map] 生成键值对树图形字符串
* @return [String]
*/
private fun Map<*, *>.genMapTree(): String {
/**
* 生成子项目
* @param prefix 前缀
* @return [String]
*/
fun Map<*, *>.genChild(prefix: String = ""): String {
val currentMap = filterKeys { it.toString().isNotBlank() }.filterValues { it !is Map<*, *> || it.isNotEmpty() }
val builder = StringBuilder()
currentMap.keys.forEachIndexed { index, key ->
val value = currentMap[key]
val isLast = index == currentMap.keys.size - 1
val branch = if (isLast) "└─ " else "├─ "
val newPrefix = if (isLast) "$prefix " else "$prefix"
builder.append("$prefix$branch$key\n")
if (value is Map<*, *>) builder.append(value.genChild(newPrefix))
}; return builder.toString()
}; return "${SweetDependency.TAG}\n${genChild()}"
}
/**
* 打印日志
* @param pairs 键值对数组
*/
private fun log(vararg pairs: Pair<String, Any>) = log(mapOf(*pairs).genMapTree())
/**
* 打印日志
* @param any 任意内容
*/
private fun log(any: Any) = SLog.info(any.toString().trimIndent(), noTag = true)
/** 标识当前值为空 */
private const val NONE = "/*-none-*/"
}

View File

@@ -0,0 +1,69 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/19.
*/
package com.highcapable.sweetdependency.plugin
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.gradle.delegate.ProjectTransaction
import com.highcapable.sweetdependency.gradle.factory.getOrCreate
import com.highcapable.sweetdependency.gradle.proxy.IGradleLifecycle
import com.highcapable.sweetdependency.manager.GradleTaskManager
import com.highcapable.sweetdependency.plugin.extension.dsl.configure.SweetDependencyConfigureExtension
import com.highcapable.sweetdependency.plugin.impl.SweetDependencyExtensionImpl
import com.highcapable.sweetdependency.utils.debug.SError
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
/**
* [SweetDependency] 插件扩展类
*/
internal class SweetDependencyExtension internal constructor() : IGradleLifecycle {
/** 当前配置方法体实例 */
private var configure: SweetDependencyConfigureExtension? = null
/** 当前扩展实现实例 */
private var impl: SweetDependencyExtensionImpl? = null
/** 当前项目事务实例 */
private var transaction: ProjectTransaction? = null
override fun onSettingsLoaded(settings: Settings) {
configure = settings.getOrCreate<SweetDependencyConfigureExtension>(SweetDependencyConfigureExtension.NAME)
}
override fun onSettingsEvaluate(settings: Settings) {
impl = SweetDependencyExtensionImpl()
impl?.onInitialization(settings, configure?.build() ?: SError.make("Settings lifecycle is broken"))
}
override fun onProjectLoaded(project: Project, isRoot: Boolean) {
ProjectTransaction.current = project
ProjectTransaction.isRoot = isRoot
if (transaction == null) transaction = ProjectTransaction()
if (isRoot) GradleTaskManager.register(project)
transaction?.also { impl?.onTransaction(it) }
}
override fun onProjectEvaluate(project: Project, isRoot: Boolean) {
transaction?.evaluateCallbacks?.forEach { it(project, isRoot) }
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/16.
*/
@file:Suppress("unused")
package com.highcapable.sweetdependency.plugin
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.gradle.delegate.GradleDelegate
import com.highcapable.sweetdependency.utils.debug.SError
import org.gradle.api.Plugin
import org.gradle.api.initialization.Settings
import org.gradle.api.plugins.ExtensionAware
/**
* [SweetDependency] 插件定义类
*/
class SweetDependencyPlugin<T : ExtensionAware> internal constructor() : Plugin<T> {
override fun apply(target: T) = when (target) {
is Settings -> GradleDelegate.create<SweetDependencyExtension>(target)
else -> SError.make("${SweetDependency.TAG} can only applied in settings.gradle/settings.gradle.kts, but current is $target")
}
}

View File

@@ -0,0 +1,78 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.config.content
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.RootConfigDocument
import com.highcapable.sweetdependency.document.mapping.RootConfigDocumentMapping
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
import com.highcapable.sweetdependency.plugin.config.factory.build
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.yaml.factory.YamlException
import kotlin.properties.Delegates
/**
* [SweetDependency] 配置类实现类
*/
internal object SweetDependencyConfigs {
/** 当前配置 */
internal var configs by Delegates.notNull<ISweetDependencyConfigs>()
/** 当前文档实体 */
internal var document by Delegates.notNull<RootConfigDocument>()
/** 当前文档测绘实例 */
internal var documentMapping by Delegates.notNull<RootConfigDocumentMapping>()
/**
* 插件启用后执行
* @param block 方法体
*/
internal inline fun withPluginEnable(block: () -> Unit) {
if (configs.isEnable) block() else SLog.warn("${SweetDependency.TAG} is disabled (won't do anything)", noRepeat = true)
}
/**
* 初始化配置 (从文件)
* @param configs 当前配置
* @param isThrowOnError 是否在发生错误的时候抛出异常 - 默认是
* @throws SweetDependencyUnresolvedException 如果设置了 [isThrowOnError] 且发生错误
*/
internal fun initialize(configs: ISweetDependencyConfigs, isThrowOnError: Boolean = true) {
this.configs = configs
runCatching {
configs.build().also {
document = it.first
documentMapping = it.second
}
}.onFailure {
if (isThrowOnError) when (it) {
is YamlException -> SError.make("Failed to parse config file: ${configs.configFilePath}\nPlease check if there are syntax errors", it)
is SweetDependencyUnresolvedException -> throw it
else -> SError.make("Failed to load config file: ${configs.configFilePath}", it)
}
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/21.
*/
package com.highcapable.sweetdependency.plugin.config.factory
import com.highcapable.sweetdependency.document.RootConfigDocument
import com.highcapable.sweetdependency.document.mapping.RootConfigDocumentMapping
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.toFile
import com.highcapable.sweetdependency.utils.yaml.Yaml
import java.io.File
/**
* 获取并解析配置文件 [RootConfigDocument] 实体和 [RootConfigDocumentMapping]
* @return [Pair]<[RootConfigDocument], [RootConfigDocumentMapping]>
*/
internal fun ISweetDependencyConfigs.build() = configFilePath.loadOrCreateEmpty() to RootConfigDocumentMapping(this)
/**
* 通过字符串路径获取或创建配置文件 [RootConfigDocument] 实体
* @return [RootConfigDocument]
*/
private fun String.loadOrCreateEmpty(): RootConfigDocument {
toFile().apply {
if (name.endsWith(".yaml").not() && name.endsWith(".yml").not())
SError.make("Config file name must be end with \".yaml\" or \".yml\"")
}.createTemplateFileOrNot()
return Yaml.loadFromFile<RootConfigDocument>(path = this)
}
/** 自动创建模版配置文件 */
private fun File.createTemplateFileOrNot() {
fun createTemplateFile() {
writeText(RootConfigDocument.defaultContent)
SLog.info("Automatically created config file: $absolutePath")
}
runCatching {
when {
exists().not() && parentFile.exists().not() -> {
parentFile.mkdirs()
createTemplateFile()
}
exists().not() -> createTemplateFile()
exists() && isDirectory -> SError.make("Tries to create file path is a directory")
}
}.onFailure { SError.make("Could not automatically created config file: $absolutePath", it) }
}

View File

@@ -0,0 +1,52 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/18.
*/
package com.highcapable.sweetdependency.plugin.config.proxy
import com.highcapable.sweetdependency.SweetDependency
/**
* [SweetDependency] 配置类接口类
*/
internal interface ISweetDependencyConfigs {
companion object {
/**
* 默认的配置文件名称
*
* "sweet-dependency-config.yaml"
*/
internal const val DEFAULT_CONFIG_FILE_NAME = "sweet-dependency-config.yaml"
}
/** 是否启用插件 */
val isEnable: Boolean
/** [SweetDependency] 的配置文件路径 */
val configFilePath: String
/** 是否启用依赖自动装配日志 */
val isEnableDependenciesAutowireLog: Boolean
/** 是否启用详细模式 */
val isEnableVerboseMode: Boolean
}

View File

@@ -0,0 +1,27 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/19.
*/
package com.highcapable.sweetdependency.plugin.extension.accessors.proxy
/**
* 扩展可访问 [Class] 定义空间接口
*/
internal interface IExtensionAccessors

View File

@@ -0,0 +1,89 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/26.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.plugin.extension.dsl.configure
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.environment.Environment
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
/**
* [SweetDependency] 配置方法体实现类
*/
open class SweetDependencyConfigureExtension internal constructor() {
internal companion object {
/** [SweetDependencyConfigureExtension] 扩展名称 */
internal const val NAME = "sweetDependency"
}
/**
* 是否启用插件
*
* 默认启用 - 如果你想关闭插件 - 在这里设置就可以了
*/
var isEnable = true
@JvmName("enable") set
/**
* [SweetDependency] 配置文件名称
*
* 默认为 [ISweetDependencyConfigs.DEFAULT_CONFIG_FILE_NAME]
*/
var configFileName = ISweetDependencyConfigs.DEFAULT_CONFIG_FILE_NAME
@JvmName("configFileName") set
/**
* 是否启用依赖自动装配日志
*
* 此功能默认启用 - 会在当前根项目 (Root Project) 的 build 目录下创建日志文件
*/
var isEnableDependenciesAutowireLog = true
@JvmName("enableDependenciesAutowireLog") set
/**
* 是否启用详细模式
*
* 此功能默认启用 - 关闭后 [SweetDependency] 将会在非必要情况下保持安静 (省略非必要日志)
*/
var isEnableVerboseMode = true
@JvmName("enableVerboseMode") set
/**
* 构造 [ISweetDependencyConfigs]
* @return [ISweetDependencyConfigs]
*/
internal fun build(): ISweetDependencyConfigs {
val currentEnable = isEnable
val currentConfigFilePath = Environment.resourcesDir(configFileName).absolutePath
val currentEnableDependenciesAutowireLog = isEnableDependenciesAutowireLog
val currentEnableVerboseMode = isEnableVerboseMode
return object : ISweetDependencyConfigs {
override val isEnable get() = currentEnable
override val configFilePath get() = currentConfigFilePath
override val isEnableDependenciesAutowireLog get() = currentEnableDependenciesAutowireLog
override val isEnableVerboseMode get() = currentEnableVerboseMode
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/26.
*/
package com.highcapable.sweetdependency.plugin.extension.dsl.manager
import com.highcapable.sweetdependency.manager.helper.DependencyDeployHelper
import org.gradle.api.Project
/**
* 依赖扩展功能配置方法体实现类
* @param project 当前项目
*/
open class SweetDependencyAutowireExtension internal constructor(private val project: Project) {
internal companion object {
/** [SweetDependencyAutowireExtension] 扩展名称 */
internal const val NAME = "sweet"
}
/**
* 自动装配依赖
* @param params 参数数组
* @return [Any]
*/
fun autowire(vararg params: String) = DependencyDeployHelper.resolveAutowire(project, params)
}

View File

@@ -0,0 +1,433 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/13.
*/
package com.highcapable.sweetdependency.plugin.generator
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.DependencyDocument
import com.highcapable.sweetdependency.document.factory.splitToDependencyGenerateNames
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
import com.highcapable.sweetdependency.gradle.delegate.entity.ExternalDependencyDelegate
import com.highcapable.sweetdependency.gradle.entity.ExternalDependency
import com.highcapable.sweetdependency.manager.content.Dependencies
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.extension.accessors.proxy.IExtensionAccessors
import com.highcapable.sweetdependency.utils.camelcase
import com.highcapable.sweetdependency.utils.capitalize
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.firstNumberToLetter
import com.highcapable.sweetdependency.utils.toNonJavaName
import com.highcapable.sweetdependency.utils.uncapitalize
import com.highcapable.sweetdependency.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
/**
* 库依赖可访问 [Class] 生成实现类
*/
internal class LibrariesAccessorsGenerator {
private companion object {
/** 生成的 [Class] 所在包名 */
private const val ACCESSORS_PACKAGE_NAME = "${SweetDependencyProperties.PROJECT_GROUP_NAME}.plugin.extension.accessors.generated"
/** 生成的 [Class] 后缀名 */
private const val CLASS_SUFFIX_NAME = "Accessors"
/** 生成的首位 [Class] 名称 */
private const val TOP_CLASS_NAME = "Libraries$CLASS_SUFFIX_NAME"
/** 标识首位生成的 [Class] TAG */
private const val TOP_SUCCESSIVE_NAME = "_top_successive_name"
}
/** 生成的依赖库 [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 = mutableListOf<Pair<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 子类名 - 默认空
* @return [String]
*/
private fun createAccessorsName(name: String = "") =
"$ACCESSORS_PACKAGE_NAME.$TOP_CLASS_NAME${if (name.isNotBlank()) "\$${name.capitalized()}" else ""}"
/**
* 创建通用构建器描述类
* @param name 名称
* @param accessors 接续对象 - 没有默认值
* @param isInner 是否为内部类 - 默认是
* @return [TypeSpec.Builder]
*/
private fun createClassSpec(name: String, accessors: Any = Any(), isInner: Boolean = true) =
TypeSpec.classBuilder(if (isInner) name.capitalized() else name).apply {
if (isInner) {
val actual = when (accessors) {
is ExternalDependency -> "\"$accessors\" library"
else -> "\"$accessors\" accessors"
}; addJavadoc("The $actual")
addSuperinterface(IExtensionAccessors::class.java)
addModifiers(Modifier.PUBLIC, Modifier.STATIC)
} else {
addJavadoc(
"""
This class is generated by ${SweetDependency.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
<br/>
The content here is automatically generated according to the dependencies of your projects
<br/>
You can visit <a href="${SweetDependency.PROJECT_URL}">here</a> for more help
""".trimIndent()
)
addModifiers(Modifier.PUBLIC)
}
}
/**
* 创建通用构造方法构建器描述类
* @return [MethodSpec.Builder]
*/
private fun createConstructorSpec() = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
/**
* 向通用构建器描述类添加变量
* @param accessors 接续对象
* @param className 类名
* @return [TypeSpec.Builder]
*/
private fun TypeSpec.Builder.addSuccessiveField(accessors: Any, className: String) = addField(
FieldSpec.builder(className.capitalized().asClassType(), className.uncapitalized(), Modifier.PRIVATE, Modifier.FINAL)
.apply {
val actual = when (accessors) {
is ExternalDependency -> "\"$accessors\" library"
else -> "\"$accessors\" accessors"
}; addJavadoc("Create the $actual")
}.build()
)
/**
* 向通用构建器描述类添加方法
* @param accessors 接续对象
* @param methodName 方法名
* @param className 类名
* @return [TypeSpec.Builder]
*/
private fun TypeSpec.Builder.addSuccessiveMethod(accessors: Any, methodName: String, className: String) =
addMethod(
MethodSpec.methodBuilder("get${methodName.capitalize()}")
.apply {
val actual = when (accessors) {
is ExternalDependency -> "\"$accessors\" library"
else -> "\"$accessors\" accessors"
}; addJavadoc("Resolve the $actual")
}.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(className.capitalized().asClassType())
.addStatement("return ${className.uncapitalized()}")
.build()
)
/**
* 向通用构建器描述类添加依赖描述器
* @param dependency 外部存储库依赖实体
* @param artifact 依赖文档实体
* @return [TypeSpec.Builder]
*/
private fun TypeSpec.Builder.addLibraryClass(dependency: ExternalDependency, artifact: DependencyDocument) = apply {
superclass(ExternalDependencyDelegate::class.java)
artifact.versions().forEach { (alias, _) ->
addField(
FieldSpec.builder(ExternalDependencyDelegate::class.java, alias.camelcase(), Modifier.PRIVATE, Modifier.FINAL)
.addJavadoc("Create the \"$dependency\" version alias \"$alias\"")
.build()
)
addMethod(
MethodSpec.methodBuilder("get${alias.uppercamelcase()}")
.addJavadoc("Resolve the \"$dependency\" version alias \"$alias\"")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(ExternalDependencyDelegate::class.java)
.addStatement("return ${alias.camelcase()}")
.build()
)
}
}
/**
* 向通用构造方法构建器描述类添加变量实例化语句
* @param className 类名
* @return [MethodSpec.Builder]
*/
private fun MethodSpec.Builder.addSuccessiveStatement(className: String) =
addStatement("${className.uncapitalized()} = new ${className.capitalized()}()")
/**
* 向通用构造方法构建器描述类添加依赖描述器
* @param dependency 外部存储库依赖实体
* @param artifact 依赖文档实体
* @return [MethodSpec.Builder]
*/
private fun MethodSpec.Builder.addLibraryStatement(dependency: ExternalDependency, artifact: DependencyDocument) = apply {
addStatement("super(\"${dependency.groupId}\", \"${dependency.artifactId}\", \"${dependency.version.deployed}\")")
artifact.versions().forEach { (alias, version) ->
addStatement(
"${alias.camelcase()} = new ${ExternalDependencyDelegate::class.java.simpleName}" +
"(\"${dependency.groupId}\", \"${dependency.artifactId}\", \"$version\")"
)
}
}
/**
* 获取、创建通用构建器描述类
* @param name 名称
* @param accessors 接续对象
* @return [TypeSpec.Builder]
*/
private fun getOrCreateClassSpec(name: String, accessors: Any) =
classSpecs[name] ?: createClassSpec(name, accessors).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 dependency 外部存储库依赖实体
* @param artifact 依赖文档实体
*/
private fun parseTypeSpec(successiveName: String, dependency: ExternalDependency, artifact: DependencyDocument) {
/**
* 获取生成的依赖库连续名称重复次数
* @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 = splitToDependencyGenerateNames()
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
}
val successiveNames = successiveName.parseSuccessiveNames()
successiveNames.forEachIndexed { index, (accessorsName, className, methodName) ->
val nextItem = successiveNames.getOrNull(index + 1)
val nextAccessorsName = nextItem?.first ?: ""
val nextClassName = nextItem?.second ?: ""
val nextMethodName = nextItem?.third ?: ""
val isPreLastIndex = index == successiveNames.lastIndex - 1
val nextAccessors: Any = if (isPreLastIndex) dependency else nextAccessorsName
if (index == successiveNames.lastIndex) {
getOrCreateClassSpec(className, dependency)?.addLibraryClass(dependency, artifact)
getOrCreateConstructorSpec(className)?.addLibraryStatement(dependency, artifact)
} else {
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)
memoryExtensionClasses.add(methodName.uncapitalize() to createAccessorsName(className))
}
noRepeated(className, nextMethodName, nextClassName) {
getOrCreateClassSpec(className, accessorsName)
.addSuccessiveField(nextAccessors, nextClassName)
.addSuccessiveMethod(nextAccessors, nextMethodName, nextClassName)
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")
}
/** 创建首位构建器 */
private fun createTopClassSpec() {
classSpecs[TOP_SUCCESSIVE_NAME] = createClassSpec(TOP_CLASS_NAME, isInner = false)
constructorSpecs[TOP_SUCCESSIVE_NAME] = createConstructorSpec()
}
/** 清空所有已生成的数据 */
private fun clearGeneratedData() {
classSpecs.clear()
constructorSpecs.clear()
preAddConstructorSpecNames.clear()
memoryExtensionClasses.clear()
grandSuccessiveNames.clear()
grandSuccessiveDuplicateIndexs.clear()
usedSuccessiveTags.clear()
}
/**
* 生成 [JavaFile]
* @return [JavaFile]
* @throws SweetDependencyUnresolvedException 如果生成失败
*/
internal fun build() = runCatching {
clearGeneratedData()
createTopClassSpec()
Dependencies.libraries().forEach { (dependencyName, artifact) ->
val dependency = ExternalDependency(dependencyName, artifact.version())
parseTypeSpec(dependencyName.current, dependency, artifact)
if (artifact.alias.isNotBlank()) parseTypeSpec(artifact.alias, dependency, artifact)
releaseParseTypeSpec()
}; buildTypeSpec().createJavaFile(ACCESSORS_PACKAGE_NAME)
}.getOrElse { SError.make("Failed to generated accessors classes, please checking your config file", 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)
val externalDependencyDelegateFile =
TypeSpec.classBuilder(ExternalDependencyDelegate::class.java.simpleName)
.addModifiers(Modifier.PUBLIC)
.addMethod(
MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(String::class.java, "groupId")
.addParameter(String::class.java, "artifactId")
.addParameter(String::class.java, "version")
.build()
).build().createJavaFile(ExternalDependencyDelegate::class.java.packageName)
stubFiles.add(iExtensionAccessorsFile)
stubFiles.add(externalDependencyDelegateFile)
return stubFiles
}
/**
* 获取扩展功能预置 [Class] 数组 (依赖)
*
* 需要调用 [build] 生成后才可以使用 - 否则可能会返回空数组
* @return [List]<[Pair]<[String], [String]>>
*/
internal val librariesClasses get() =
SweetDependencyConfigs.document.preferences().dependenciesNamespace.libraries().let { namespace ->
if (namespace.isNotBlank()) listOf(namespace to createAccessorsName())
else memoryExtensionClasses
}
}

View File

@@ -0,0 +1,65 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/7/30.
*/
package com.highcapable.sweetdependency.plugin.helper
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.generated.SweetDependencyProperties
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.utils.debug.SLog
import com.highcapable.sweetdependency.utils.executeUrlBody
/**
* 插件自身检查更新工具类
*/
internal object PluginUpdateHelper {
private const val REPO_NAME = SweetDependencyProperties.PROJECT_NAME
private const val AUTHOR_NAME = SweetDependencyProperties.PROJECT_DEVELOPER_NAME
/** 检查更新 URL 地址 */
private const val RELEASE_URL = "https://github.com/$AUTHOR_NAME/$REPO_NAME"
/** 检查更新 */
internal fun checkingForUpdate() {
if (GradleHelper.isOfflineMode) return
val latestVersion = RELEASE_URL.executeUrlBody(isShowFailure = false).findVersionName()
if (latestVersion.isNotBlank() && latestVersion != SweetDependency.VERSION)
SLog.note(
"""
Plugin update is available, the current version is ${SweetDependency.VERSION}, please update to $latestVersion
You can modify your plugin version in your project's settings.gradle / settings.gradle.kts
plugins {
id("${SweetDependencyProperties.PROJECT_GROUP_NAME}") version "$latestVersion"
...
}
For more information, you can visit ${SweetDependency.PROJECT_URL}
""".trimIndent(), SLog.UP
)
}
/**
* 解析 JSON 并查找字符串版本 "name"
* @return [String]
*/
private fun String.findVersionName() =
runCatching { trim().split("href=\"/$AUTHOR_NAME/$REPO_NAME/releases/tag/")[1].split("\"")[0] }.getOrNull() ?: ""
}

View File

@@ -0,0 +1,111 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/27.
*/
package com.highcapable.sweetdependency.plugin.impl
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.document.PreferencesDocument
import com.highcapable.sweetdependency.document.factory.toUpdateMode
import com.highcapable.sweetdependency.environment.Environment
import com.highcapable.sweetdependency.gradle.delegate.ProjectTransaction
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.manager.RepositoryManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.helper.PluginUpdateHelper
import com.highcapable.sweetdependency.plugin.impl.base.BaseExtensionImpl
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import org.gradle.api.initialization.Settings
/**
* [SweetDependency] 扩展实现类
*/
internal class SweetDependencyExtensionImpl : BaseExtensionImpl() {
private companion object {
/** 插件是否已经装载 - 在 Gradle 守护程序启动后进程不会被结束 */
private var isPluginLoaded = false
}
/**
* 检查兼容性
*
* 目前仅支持 Gradle 7.x.x and 8.x.x 版本
*/
private fun checkingCompatibility() {
Environment.characterEncoding?.also {
if (it.lowercase() != "utf-8") SLog.warn(
"""
!!! WARNING !!!
The current character encoding is not UTF-8, it is currently $it
This will cause some characters cannot be displayed, please change the console character encoding
""".trimIndent(), noTag = true
)
}
if (GradleHelper.version.let { it.startsWith("7.") || it.startsWith("8.") }.not()) SError.make(
"${SweetDependency.TAG} ${SweetDependency.VERSION} " +
"does not support Gradle ${GradleHelper.version}, please update Gradle or plugin version"
)
}
override fun onInitialization(settings: Settings, configs: ISweetDependencyConfigs) {
checkingCompatibility()
configureProject(configs)
SweetDependencyConfigs.initialize(configs)
SweetDependencyConfigs.withPluginEnable {
configureRepositoriesAndDependencies(settings) { autowireOnSyncMode ->
autowireOnSyncMode.toUpdateMode()?.also { DependencyManager.autowireAndUpdate(it) }
}; PluginUpdateHelper.checkingForUpdate()
}
}
override fun onTransaction(transaction: ProjectTransaction) {
if (transaction.isRoot) DependencyManager.resolve(transaction.current)
transaction.evaluation { project, isRoot -> if (isRoot) DependencyManager.deploy(project) }
}
/**
* 配置项目
* @param configs 当前配置
*/
private fun configureProject(configs: ISweetDependencyConfigs) {
SLog.isVerboseMode = configs.isEnableVerboseMode
if (isPluginLoaded.not() || GradleHelper.isSyncMode.not()) SLog.verbose(SweetDependency.bannerContent, noTag = true)
if (isPluginLoaded) return
isPluginLoaded = true
SLog.verbose("Welcome to ${SweetDependency.TAG} ${SweetDependency.VERSION}! Using Gradle ${GradleHelper.version}")
}
/**
* 配置存储库和依赖
* @param settings 当前设置
* @param autowire 自回调动装配方法体
*/
private inline fun configureRepositoriesAndDependencies(settings: Settings, autowire: (PreferencesDocument.AutowireOnSyncMode) -> Unit) {
RepositoryManager.generateAndApply(settings)
DependencyManager.generateAndApply()
if (GradleHelper.isSyncMode) autowire(SweetDependencyConfigs.document.preferences().autowireOnSyncMode)
DependencyManager.generateAndApply(settings)
}
}

View File

@@ -0,0 +1,45 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/27.
*/
package com.highcapable.sweetdependency.plugin.impl.base
import com.highcapable.sweetdependency.gradle.delegate.ProjectTransaction
import com.highcapable.sweetdependency.plugin.config.proxy.ISweetDependencyConfigs
import org.gradle.api.initialization.Settings
/**
* 扩展父类实现类
*/
internal abstract class BaseExtensionImpl internal constructor() {
/**
* 当初始化时回调
* @param settings 当前设置
* @param configs 当前配置
*/
internal abstract fun onInitialization(settings: Settings, configs: ISweetDependencyConfigs)
/**
* 当开始事务时回调
* @param transaction 当前实例
*/
internal abstract fun onTransaction(transaction: ProjectTransaction)
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配 Gradle Task
*/
internal class AutowireDependenciesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.ALL,
DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配 Gradle Task
*/
internal class AutowireLibrariesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.LIBRARIES,
DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配 Gradle Task
*/
internal class AutowirePluginsTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.PLUGINS,
DependencyUpdateMode.UpdateType.ONLY_AUTOWIRE
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,34 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.manager.transaction.DependencyMigrationTemplateTransaction
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 创建依赖迁移模板 Gradle Task
*/
internal class CreateDependenciesMigrationTemplateTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable { DependencyMigrationTemplateTransaction.createTemplate() }
}

View File

@@ -0,0 +1,34 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.manager.transaction.RuntimeDebugTransaction
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 调试 Gradle Task
*/
internal class SweetDependencyDebugTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable { RuntimeDebugTransaction.dump() }
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/14.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (全部) Gradle Task
*/
internal class UpdateAllDependenciesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.ALL,
DependencyUpdateMode.UpdateType.UPDATE_ALL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (全部) Gradle Task
*/
internal class UpdateAllLibrariesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.LIBRARIES,
DependencyUpdateMode.UpdateType.UPDATE_ALL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (全部) Gradle Task
*/
internal class UpdateAllPluginsTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.PLUGINS,
DependencyUpdateMode.UpdateType.UPDATE_ALL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (可选) Gradle Task
*/
internal class UpdateOptionalDependenciesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.ALL,
DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (可选) Gradle Task
*/
internal class UpdateOptionalLibrariesTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.LIBRARIES,
DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,42 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/16.
*/
package com.highcapable.sweetdependency.plugin.task
import com.highcapable.sweetdependency.gradle.entity.DependencyUpdateMode
import com.highcapable.sweetdependency.manager.DependencyManager
import com.highcapable.sweetdependency.plugin.config.content.SweetDependencyConfigs
import com.highcapable.sweetdependency.plugin.task.base.BaseTask
/**
* 依赖自动装配、更新 (可选) Gradle Task
*/
internal class UpdateOptionalPluginsTask : BaseTask() {
override fun onTransaction() = SweetDependencyConfigs.withPluginEnable {
DependencyManager.autowireAndUpdate(
DependencyUpdateMode(
DependencyUpdateMode.DependencyType.PLUGINS,
DependencyUpdateMode.UpdateType.UPDATE_OPTIONAL
), isRunningOnSync = false
)
}
}

View File

@@ -0,0 +1,31 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/29.
*/
package com.highcapable.sweetdependency.plugin.task.base
/**
* Gradle Task 父类实现类
*/
internal abstract class BaseTask internal constructor() {
/** 当开始事务时回调 */
internal abstract fun onTransaction()
}

View File

@@ -0,0 +1,136 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
@file:Suppress("unused")
package com.highcapable.sweetdependency.utils
import com.highcapable.sweetdependency.utils.debug.SError
import java.io.File
import java.nio.file.Paths
import java.util.zip.ZipFile
/**
* 字符串路径转换为文件
*
* 自动调用 [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("\\", "/")
/**
* 字符串文件路径转换到相对文件路径
* @param basePath 基于路径
* @param rootPath 根路径 - 不填将不校验完整路径
* @return [String]
*/
internal fun String.toRelativeFilePath(basePath: String, rootPath: String = "") =
parseFileSeparator().runCatching {
if (rootPath.isNotBlank() && contains(rootPath).not()) return this
return Paths.get(basePath).relativize(Paths.get(this)).toString()
}.getOrNull() ?: parseFileSeparator()
/**
* 字符串文件路径转换到绝对文件路径
* @param basePath 基于路径
* @return [String]
*/
internal fun String.toAbsoluteFilePath(basePath: String) =
parseFileSeparator().runCatching {
if (Paths.get(this).isAbsolute) return this
Paths.get(basePath).resolve(Paths.get(this)).normalize().toString()
}.getOrNull() ?: parseFileSeparator()
/**
* 字符串文件路径转换到绝对文件路径数组
* @param basePath 基于路径
* @return [MutableList]<[String]>
*/
internal fun String.toAbsoluteFilePaths(basePath: String) =
toAbsoluteFilePath(basePath).let { path ->
mutableListOf<String>().apply {
when {
path.toFile().let { it.exists() && it.isFile } -> add(path)
path.toFile().let { it.exists() && it.isDirectory } -> SError.make("The file path $path is a directory")
else -> {
/**
* 是否匹配文件扩展名
* @param condition 条件
* @return [Boolean]
*/
fun String.isMatch(condition: String) =
condition.let { if (it == "*") "*.*" else it }.replace(".", "\\.").replace("*", ".*").toRegex().matches(this)
val condition = path.split(File.separator)
if (path.contains(File.separator) && condition[condition.lastIndex].contains("*"))
path.toFile().parentFile?.listFiles()?.forEach { if (it.name.isMatch(condition[condition.lastIndex])) add(it.absolutePath) }
else SError.make("Could not resolve file path $path")
}
}
}
}
/**
* 检查文件是否为合法的压缩包文件
*
* - 如果不是文件 (可能是目录) - 返回 true
* - 如果文件不存在 - 返回 false
* @return [Boolean]
*/
internal fun File.isValidZip(): Boolean {
if (isFile.not()) return true
if (exists().not()) return false
return runCatching { ZipFile(this).use {}; true }.getOrNull() ?: false
}
/**
* 检查目录是否为空
*
* - 如果不是目录 (可能是文件) - 返回 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()
}
}
/**
* 获取当前文件内容的字符串内容 (同步)
* @return [String]
*/
internal fun String.executeFileBody() = runCatching { toFile().readText() }.getOrNull() ?: ""

View File

@@ -0,0 +1,58 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/15.
*/
package com.highcapable.sweetdependency.utils
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.debug.SLog
import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.concurrent.TimeUnit
/**
* 获取当前 URL 地址的请求体字符串内容 (GET) (同步)
* @param username 用户名
* @param password 密码
* @param isShowFailure 是否显示错误 - 默认是
* @return [String]
*/
internal fun String.executeUrlBody(username: String = "", password: String = "", isShowFailure: Boolean = true) = runCatching {
OkHttpClient()
.newBuilder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.authenticator { _, response ->
if (response.code == 400 || response.code == 401)
response.request.newBuilder()
.header("Authorization", Credentials.basic(username, password))
.build()
else null
}.build().newCall(
Request.Builder().url(when {
startsWith("https://") -> "https://" + replace("https://", "").replace("//", "/")
startsWith("http://") -> "http://" + replace("http://", "").replace("//", "/")
else -> SError.make("Invalid URL: $this")
}).get().build()
).execute().let {
if (it.code == 200 || it.code == 404) it.body?.string() ?: ""
else SError.make("Request failed with code ${it.code}")
}
}.onFailure { if (isShowFailure) SLog.error("Failed to connect to $this\n$it") }.getOrNull() ?: ""

View File

@@ -0,0 +1,47 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/7/16.
*/
package com.highcapable.sweetdependency.utils
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
* 创建当前线程池服务
* @return [ExecutorService]
*/
private val currentThreadPool get() = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
/**
* 启动 [Thread] 延迟等待 [block] 的结果 [T]
* @param delayMs 延迟毫秒 - 默认 1 ms
* @param block 方法块
* @return [T]
*/
internal inline fun <T> T.await(delayMs: Long = 1, crossinline block: (T) -> Unit): T {
currentThreadPool.apply {
execute {
if (delayMs > 0) Thread.sleep(delayMs)
block(this@await)
shutdown()
}
}; return this
}

View File

@@ -0,0 +1,31 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/19.
*/
package com.highcapable.sweetdependency.utils
import java.io.PrintWriter
import java.io.StringWriter
/**
* 写出异常堆栈到字符串
* @return [String]
*/
internal fun Throwable.dumpToString() = StringWriter().apply { printStackTrace(PrintWriter(this).apply { flush() }) }.toString()

View File

@@ -0,0 +1,150 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/14.
*/
package com.highcapable.sweetdependency.utils
/**
* 允许 [MutableList] 进行 [orEmpty] 操作后返回 [MutableList]
* @return [MutableList]<[T]>
*/
internal fun <T> MutableList<T>?.orEmpty() = this ?: emptyList<T>().toMutableList()
/**
* 允许 [MutableList] 进行 [filter] 操作后返回 [MutableList]
* @param predicate 方法体
* @return [MutableList]<[T]>
*/
internal inline fun <T> MutableList<out T>.filter(predicate: (T) -> Boolean) = filterTo(mutableListOf(), predicate).toMutableList()
/**
* 允许 [MutableMap] 进行 [filter] 操作后返回 [MutableMap]
* @param predicate 方法体
* @return [MutableMap]<[K], [V]>
*/
internal inline fun <K, V> MutableMap<out K, V>.filter(predicate: (Map.Entry<K, V>) -> Boolean) = filterTo(mutableMapOf(), predicate).toMutableMap()
/**
* 获取 [MutableMap] 第一位元素 (数组为空返回 null)
* @return [MutableMap.MutableEntry]<[K], [V]> or null
*/
internal fun <K, V> MutableMap<K, V>.single() = entries.firstOrNull()
/**
* 当数组不为空时返回非空
* @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 [Boolean]
*/
internal fun List<*>.hasDuplicate() = distinct().size != size
/**
* 查找数组中的重复元素
* @return [List]<[T]>
*/
internal inline fun <reified T> List<T>.findDuplicates() = distinct().filter { e -> count { it == e } > 1 }.distinct()
/**
* 字符串数组转换为内容字符串
* @return [String]
*/
internal fun List<String>.joinToContent() = joinToString("\n").trim()
/**
* 空格字符串数组转换为 [MutableList]
* @return [MutableList]<[String]>
*/
internal fun String.toSpaceList() = when {
contains(" ") -> replace("\\s+".toRegex(), " ").split(" ").toMutableList()
isNotBlank() -> mutableListOf(this)
else -> mutableListOf()
}
/**
* 下划线、分隔线、点、空格命名字符串转小驼峰命名字符串
* @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]) }

View File

@@ -0,0 +1,193 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6.
*/
package com.highcapable.sweetdependency.utils.code
import com.highcapable.sweetdependency.plugin.SweetDependencyExtension
import com.highcapable.sweetdependency.utils.code.entity.MavenPomData
import com.highcapable.sweetdependency.utils.debug.SError
import com.highcapable.sweetdependency.utils.deleteEmptyRecursively
import com.highcapable.sweetdependency.utils.parseFileSeparator
import com.highcapable.sweetdependency.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 SweetDependencyExtension 如果编译失败
*/
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 SweetDependencyExtension 如果编译输出目录不存在
*/
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) } }
}
}

View File

@@ -0,0 +1,30 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/24.
*/
package com.highcapable.sweetdependency.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)

View File

@@ -0,0 +1,84 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6.
*/
@file:Suppress("unused")
package com.highcapable.sweetdependency.utils.code.factory
import com.highcapable.sweetdependency.plugin.SweetDependencyExtension
import com.highcapable.sweetdependency.utils.code.CodeCompiler
import com.highcapable.sweetdependency.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 SweetDependencyExtension 如果编译失败
*/
@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 SweetDependencyExtension 如果编译失败
*/
@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 SweetDependencyExtension 如果编译失败
*/
@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 SweetDependencyExtension 如果编译失败
*/
@JvmName("compileWithJavaFileObject")
internal fun List<JavaFileObject>.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List<JavaFileObject> = mutableListOf()) =
CodeCompiler.compile(pomData, outputDirPath, files = this, compileOnlyFiles)

View File

@@ -0,0 +1,38 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/31.
*/
package com.highcapable.sweetdependency.utils.debug
import com.highcapable.sweetdependency.exception.SweetDependencyUnresolvedException
/**
* 全局异常管理类
*/
internal object SError {
/**
* 抛出异常
* @param msg 消息内容
* @throws e 异常内容 - 默认空
* @throws SweetDependencyUnresolvedException
*/
internal fun make(msg: String, e: Throwable? = null): Nothing = throw SweetDependencyUnresolvedException(msg, e)
}

View File

@@ -0,0 +1,131 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/25.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.sweetdependency.utils.debug
import com.highcapable.sweetdependency.SweetDependency
import com.highcapable.sweetdependency.utils.await
import org.apache.log4j.Logger
/**
* 全局 Log 管理类
*/
internal object SLog {
internal const val DONE = ""
internal const val IGNORE = ""
internal const val ERROR = ""
internal const val WARN = "⚠️"
internal const val LINK = "➡️"
internal const val WIRE = "⚙️"
internal const val UP = "⬆️"
internal const val ROTATE = "\uD83D\uDD04"
internal const val ANLZE = "\uD83D\uDD0D"
internal const val STRNG = "\uD83D\uDCAA"
/** 当前日志输出对象 */
private val logger = Logger.getLogger(SLog::class.java)
/** 短时间内不重复的日志内容 */
private var noRepeatInThisTimeContent = ""
/** 是否启用详细模式 */
internal var isVerboseMode = true
/**
* 打印 Verbose 级别 Log (无颜色)
* @param msg 消息内容
* @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
* @param noTag 无标签 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
internal fun verbose(msg: Any, symbol: String = "", noTag: Boolean = false, noRepeat: Boolean = false) {
if (isVerboseMode) info(msg, symbol, noTag, noRepeat)
}
/**
* 打印 Info (提醒) 级别 Log (绿色)
* @param msg 消息内容
* @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
* @param noTag 无标签 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
internal fun note(msg: Any, symbol: String = "", noTag: Boolean = false, noRepeat: Boolean = false) =
log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "38;5;10", noRepeat = noRepeat)
/**
* 打印 Info 级别 Log (无颜色)
* @param msg 消息内容
* @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
* @param noTag 无标签 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
internal fun info(msg: Any, symbol: String = "", noTag: Boolean = false, noRepeat: Boolean = false) =
log(if (noTag) msg else msg.createSymbolMsg(symbol), noRepeat = noRepeat)
/**
* 打印 Warn 级别 Log (黄色)
* @param msg 消息内容
* @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [WARN]
* @param noTag 无标签 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
internal fun warn(msg: Any, symbol: String = WARN, noTag: Boolean = false, noRepeat: Boolean = false) =
log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "33", noRepeat = noRepeat)
/**
* 打印 Error 级别 Log (红色)
* @param msg 消息内容
* @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [ERROR]
* @param noTag 无标签 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
internal fun error(msg: Any, symbol: String = ERROR, noTag: Boolean = false, noRepeat: Boolean = false) =
log(if (noTag) msg else msg.createSymbolMsg(symbol), isError = true, noRepeat = noRepeat)
/**
* 创建符号消息内容
* @param symbol 前缀符号
* @return [String]
*/
private fun Any.createSymbolMsg(symbol: String) =
if (symbol.isNotBlank()) "[${SweetDependency.TAG}] $symbol $this" else "[${SweetDependency.TAG}] $this"
/**
* 打印 Log
* @param msg 消息内容
* @param color 颜色代码 - 默认无颜色
* @param isError 是否强制为错误日志 - 默认否
* @param noRepeat 短时间内不重复输出 - 默认否
*/
private fun log(msg: Any, color: String = "0", isError: Boolean = false, noRepeat: Boolean = false) {
if (noRepeat && noRepeatInThisTimeContent == msg.toString()) return
noRepeatInThisTimeContent = msg.toString()
when {
isError -> logger.error(msg)
color != "0" -> println("\u001B[${color}m$msg\u001B[0m")
else -> println(msg)
}
if (noRepeat) await(1500) { noRepeatInThisTimeContent = "" } else noRepeatInThisTimeContent = ""
}
}

View File

@@ -0,0 +1,117 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/17.
*/
@file:Suppress("unused")
package com.highcapable.sweetdependency.utils.yaml
import com.charleskorn.kaml.YamlConfiguration
import com.charleskorn.kaml.yamlMap
import com.highcapable.sweetdependency.gradle.helper.GradleHelper
import com.highcapable.sweetdependency.utils.hasInterpolation
import com.highcapable.sweetdependency.utils.replaceInterpolation
import com.highcapable.sweetdependency.utils.toFile
import com.highcapable.sweetdependency.utils.yaml.factory.YamlMapEntries
import com.highcapable.sweetdependency.utils.yaml.proxy.IYamlDocument
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.serializer
import com.charleskorn.kaml.Yaml as Kaml
/**
* YAML 文档处理类
*/
internal object Yaml {
/**
* 获取 [Kaml] 对象
* @return [Kaml]
*/
private val kaml by lazy { Kaml(configuration = YamlConfiguration(encodeDefaults = false)) }
/**
* 从文件解析到 [IYamlDocument]
* @param path 文件路径
* @return [T]
*/
internal inline fun <reified T : IYamlDocument> loadFromFile(path: String) = loadFromString<T>(path.toFile().readText())
/**
* 从字符串解析到 [IYamlDocument]
* @param string 字符串
* @return [T]
*/
internal inline fun <reified T : IYamlDocument> loadFromString(string: String) = kaml.decodeFromString<T>(string.flattened())
/**
* 从文件解析到 [YamlMapEntries]
* @param path 文件路径
* @return [YamlMapEntries]
*/
internal fun loadFromFileAsNode(path: String) = loadFromStringAsNode(path.toFile().readText())
/**
* 从字符串解析到 [YamlMapEntries]
* @param string 字符串
* @return [YamlMapEntries]
*/
internal fun loadFromStringAsNode(string: String) = kaml.parseToYamlNode(string.flattened()).yamlMap.entries
/**
* 序列化 [IYamlDocument] 到文件
* @param path 文件路径
* @param formatter 回调字符串格式化方式
*/
internal inline fun <reified T : IYamlDocument> parseToFile(doc: T, path: String, formatter: String.() -> String = { this }) =
path.toFile().writeText(kaml.encodeToString(serializer(), doc).let(formatter))
/**
* 字符串平坦化处理
*
* - 去除字符串中以 # 开头的注释行并去除空行
* - 调用 [interpFromEnv] 解析可被插值的字符串
* @return [String]
*/
private fun String.flattened() = trimIndent()
.replace("(^|\\s)#.*".toRegex(), "")
.replace("(?m)^\\s*$(\\n|\\r\\n?)".toRegex(), "")
.let { if (it.hasInterpolation()) it.interpFromEnv() else it }
.trim()
/**
* 将系统属性资源值插入到当前字符串中
*
* 形如:${...}
*
* 会按照以下顺序进行查找 ↓
*
* - 项目 properties
* - 用户 properties
* - 系统 properties
* - 系统环境变量
* @return [String]
*/
private fun String.interpFromEnv() = replaceInterpolation { key ->
GradleHelper.projectProperties?.get(key)?.toString()
?: GradleHelper.userProperties?.get(key)?.toString()
?: runCatching { System.getProperties()[key]?.toString() }.getOrNull()
?: runCatching { System.getenv(key) }.getOrNull() ?: ""
}
}

View File

@@ -0,0 +1,46 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/19.
*/
package com.highcapable.sweetdependency.utils.yaml.factory
import com.charleskorn.kaml.YamlException
import com.charleskorn.kaml.YamlMap
import com.charleskorn.kaml.YamlNode
import com.charleskorn.kaml.YamlScalar
/** YAML 异常类型定义 */
internal typealias YamlException = YamlException
/** YAML 节点数组类型定义 */
internal typealias YamlMapEntries = Map<YamlScalar, YamlNode>
/**
* 转换为 YAML 节点数组
* @return [YamlMapEntries] or null
*/
internal fun YamlNode.asMap() = (this as? YamlMap)?.entries
/**
* 获取 YAML 节点是否存在
* @param key 节点名称
* @return [Boolean]
*/
internal fun YamlMapEntries.isKeyExist(key: String) = keys.singleOrNull { it.content == key } != null

View File

@@ -0,0 +1,29 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/5/20.
*/
package com.highcapable.sweetdependency.utils.yaml.proxy
import java.io.Serializable
/**
* YAML 文档接口
*/
internal interface IYamlDocument : Serializable

View File

@@ -0,0 +1,51 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/25.
*/
@file:Suppress("unused", "UnusedReceiverParameter")
package org.gradle.kotlin.dsl
import com.highcapable.sweetdependency.manager.helper.DependencyDeployHelper
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependencySpec
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
/**
* 自动装配插件依赖
* @param params 参数数组
* @return [PluginDependencySpec]
*/
fun PluginDependenciesSpec.autowire(vararg params: Any) = DependencyDeployHelper.resolveAutowire(spec = this, params)
/**
* 自动装配依赖
* @param params 参数数组
* @return [Any]
*/
fun DependencyHandler.autowire(vararg params: String) = DependencyDeployHelper.resolveAutowire(params = params)
/**
* 自动装配依赖
* @param params 参数数组
* @return [Any]
*/
fun KotlinDependencyHandler.autowire(vararg params: String) = DependencyDeployHelper.resolveAutowire(project, params)

View File

@@ -0,0 +1,51 @@
/*
* SweetDependency - An easy autowire and manage dependencies Gradle plugin
* Copyright (C) 2019-2023 HighCapable
* https://github.com/HighCapable/SweetDependency
*
* 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/6/3.
*/
@file:Suppress("unused")
package org.gradle.kotlin.dsl
import com.highcapable.sweetdependency.gradle.factory.configure
import com.highcapable.sweetdependency.gradle.factory.get
import com.highcapable.sweetdependency.plugin.extension.dsl.configure.SweetDependencyConfigureExtension
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 [SweetDependencyConfigureExtension] extension.
* @return [SweetDependencyConfigureExtension]
*/
val Settings.sweetDependency get() = get<SweetDependencyConfigureExtension>(SweetDependencyConfigureExtension.NAME)
/**
* Configures the [SweetDependencyConfigureExtension] extension.
* @param configure
*/
fun Settings.sweetDependency(configure: Action<SweetDependencyConfigureExtension>) = configure(SweetDependencyConfigureExtension.NAME, configure)