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,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)