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,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-*/"
}