1
+ /*
2
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
3
+ *
4
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
5
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
6
+ *
7
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
8
+ */
9
+
10
+ package net.mamoe.mirai.console.gradle
11
+
12
+ import org.gradle.api.DefaultTask
13
+ import org.gradle.api.artifacts.ExternalModuleDependency
14
+ import org.gradle.api.artifacts.ResolvedArtifact
15
+ import org.gradle.api.artifacts.ResolvedDependency
16
+ import org.gradle.api.attributes.AttributeContainer
17
+ import org.gradle.api.capabilities.Capability
18
+ import org.gradle.api.file.DuplicatesStrategy
19
+ import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration
20
+ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor
21
+ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
22
+ import org.gradle.api.internal.file.FileCollectionInternal
23
+ import org.gradle.api.internal.file.FileCollectionStructureVisitor
24
+ import org.gradle.api.tasks.TaskAction
25
+ import org.gradle.internal.DisplayName
26
+ import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
27
+ import org.gradle.jvm.tasks.Jar
28
+ import org.gradle.kotlin.dsl.create
29
+ import org.gradle.kotlin.dsl.get
30
+ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
31
+ import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
32
+ import java.io.File
33
+ import javax.inject.Inject
34
+
35
+ public open class BuildMiraiPluginV2 : Jar () {
36
+
37
+ // @get:Internal
38
+ private lateinit var metadataTask: GenMetadataTask
39
+
40
+ public open class GenMetadataTask
41
+ @Inject internal constructor (
42
+ @JvmField internal val orgTask: BuildMiraiPluginV2 ,
43
+ ) : DefaultTask () {
44
+ @TaskAction
45
+ internal fun run () {
46
+ val runtime = mutableSetOf<String >()
47
+ val api = mutableSetOf<String >()
48
+ val linkedDependencies = mutableSetOf (
49
+ " net.mamoe:mirai-core-api" ,
50
+ " net.mamoe:mirai-core-api-jvm" ,
51
+ " net.mamoe:mirai-core-api-android" ,
52
+ " net.mamoe:mirai-core" ,
53
+ " net.mamoe:mirai-core-jvm" ,
54
+ " net.mamoe:mirai-core-android" ,
55
+ " net.mamoe:mirai-core-utils" ,
56
+ " net.mamoe:mirai-core-utils-jvm" ,
57
+ " net.mamoe:mirai-core-utils-android" ,
58
+ " net.mamoe:mirai-console" ,
59
+ " net.mamoe:mirai-console-terminal" ,
60
+ )
61
+ val linkToApi = mutableSetOf<String >()
62
+ val shadowedFiles = mutableSetOf<File >()
63
+
64
+ // TODO: check dependencies
65
+ project.configurations.findByName(" apiElements" )?.allDependencies?.forEach { dep ->
66
+ if (dep is ExternalModuleDependency ) {
67
+ val artId = " ${dep.group} :${dep.name} "
68
+ linkedDependencies.add(artId)
69
+ linkToApi.add(artId)
70
+ }
71
+ }
72
+ project.configurations.findByName(" implementation" )?.allDependencies?.forEach { dep ->
73
+ if (dep is ExternalModuleDependency ) {
74
+ linkedDependencies.add(" ${dep.group} :${dep.name} " )
75
+ }
76
+ }
77
+
78
+ fun ResolvedDependency.depId (): String = " $moduleGroup :$moduleName "
79
+
80
+ val runtimeClasspath = project.configurations[" runtimeClasspath" ].resolvedConfiguration
81
+ fun markAsResolved (resolvedDependency : ResolvedDependency ) {
82
+ val depId = resolvedDependency.depId()
83
+ linkedDependencies.add(depId)
84
+ resolvedDependency.children.forEach { markAsResolved(it) }
85
+ }
86
+
87
+ fun linkDependencyTo (resolvedDependency : ResolvedDependency , dependencies : MutableCollection <String >) {
88
+ dependencies.add(resolvedDependency.module.toString())
89
+ resolvedDependency.children.forEach { linkDependencyTo(it, dependencies) }
90
+ }
91
+
92
+ fun resolveDependency (resolvedDependency : ResolvedDependency ) {
93
+ val depId = resolvedDependency.depId()
94
+ if (depId in linkedDependencies) {
95
+ markAsResolved(resolvedDependency)
96
+ linkDependencyTo(resolvedDependency, runtime)
97
+ if (depId in linkToApi) {
98
+ linkDependencyTo(resolvedDependency, api)
99
+ }
100
+ return
101
+ }
102
+ }
103
+ runtimeClasspath.firstLevelModuleDependencies.forEach { resolveDependency(it) }
104
+
105
+ logger.info { " linkedDependencies: $linkedDependencies " }
106
+ logger.info { " linkToAPi : $linkToApi " }
107
+ logger.info { " api : $api " }
108
+ logger.info { " runtime : $runtime " }
109
+
110
+ val lenientConfiguration = runtimeClasspath.lenientConfiguration
111
+ if (lenientConfiguration is DefaultLenientConfiguration ) {
112
+ val resolvedArtifacts = mutableSetOf<ResolvedArtifact >()
113
+ lenientConfiguration.select().visitArtifacts(object : ArtifactVisitor {
114
+ override fun prepareForVisit (source : FileCollectionInternal .Source ): FileCollectionStructureVisitor .VisitType {
115
+ return FileCollectionStructureVisitor .VisitType .Visit
116
+ }
117
+
118
+ override fun visitArtifact (
119
+ variantName : DisplayName ,
120
+ variantAttributes : AttributeContainer ,
121
+ capabilities : MutableList <out Capability >,
122
+ artifact : ResolvableArtifact
123
+ ) {
124
+ resolvedArtifacts.add(artifact.toPublicView())
125
+ }
126
+
127
+ override fun requireArtifactFiles (): Boolean = false
128
+ override fun visitFailure (failure : Throwable ) {}
129
+ }, false )
130
+ resolvedArtifacts
131
+ } else {
132
+ runtimeClasspath.resolvedArtifacts
133
+ }.forEach { artifact ->
134
+ val artId = artifact.id
135
+ if (artId is ModuleComponentArtifactIdentifier ) {
136
+ val cid = artId.componentIdentifier
137
+ if (" ${cid.group} :${cid.module} " in linkedDependencies) {
138
+ return @forEach
139
+ }
140
+ }
141
+ logger.info { " `- $artId - ${artId.javaClass} " }
142
+ shadowedFiles.add(artifact.file)
143
+ }
144
+
145
+ shadowedFiles.forEach { file ->
146
+ if (file.isDirectory) {
147
+ orgTask.from(file)
148
+ } else if (file.extension == " jar" ) {
149
+ orgTask.from(project.zipTree(file))
150
+ } else {
151
+ orgTask.from(file)
152
+ }
153
+ }
154
+
155
+ temporaryDir.also {
156
+ it.mkdirs()
157
+ }.let { tmpDir ->
158
+ tmpDir.resolve(" api.txt" ).writeText(api.sorted().joinToString(" \n " ))
159
+ tmpDir.resolve(" runtime.txt" ).writeText(runtime.sorted().joinToString(" \n " ))
160
+ orgTask.from(tmpDir.resolve(" api.txt" )) { copy ->
161
+ copy.into(" META-INF/mirai-console-plugin" )
162
+ copy.rename { " dependencies-shared.txt" }
163
+ }
164
+ orgTask.from(tmpDir.resolve(" runtime.txt" )) { copy ->
165
+ copy.into(" META-INF/mirai-console-plugin" )
166
+ copy.rename { " dependencies-private.txt" }
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ @Suppress(" RedundantLambdaArrow" , " RemoveExplicitTypeArguments" )
173
+ internal fun init (target : KotlinTarget ) {
174
+ metadataTask = project.tasks.create<GenMetadataTask >(name + " GenMetadata" , this )
175
+ dependsOn(metadataTask)
176
+ archiveExtension.set(" mirai.jar" )
177
+ duplicatesStrategy = DuplicatesStrategy .WARN
178
+
179
+ val compilations = target.compilations.filter { it.name == KotlinCompilation .MAIN_COMPILATION_NAME }
180
+ compilations.forEach {
181
+ dependsOn(it.compileKotlinTask)
182
+ from(it.output.allOutputs)
183
+ metadataTask.dependsOn(it.compileKotlinTask)
184
+ }
185
+ exclude { elm ->
186
+ elm.path.startsWith(" META-INF/" ) && elm.name.endsWith(" .sf" , ignoreCase = true )
187
+ }
188
+ }
189
+ }
0 commit comments