1
1
package io.bazel.kotlin.generate
2
2
3
3
import io.bazel.kotlin.generate.WriteKotlincCapabilities.KotlincCapabilities.Companion.asCapabilities
4
- import io.bazel.kotlin.generate.WriteKotlincCapabilities.KotlincCapability
5
4
import org.jetbrains.kotlin.cli.common.arguments.Argument
6
5
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
7
6
import org.jetbrains.kotlin.config.LanguageVersion
@@ -12,19 +11,75 @@ import java.nio.file.Files
12
11
import java.time.Year
13
12
import kotlin.io.path.exists
14
13
import kotlin.io.path.writeText
14
+ import kotlin.jvm.java
15
+ import kotlin.math.max
16
+ import kotlin.streams.asSequence
17
+ import kotlin.streams.toList
15
18
16
19
/* *
17
20
* Generates a list of kotlinc flags from the K2JVMCompilerArguments on the classpath.
18
21
*/
19
22
object WriteKotlincCapabilities {
20
23
24
+ @JvmStatic
25
+ fun main (vararg args : String ) {
26
+ // TODO: Replace with a real option parser
27
+ val options = args.asSequence()
28
+ .flatMap { t -> t.split(" =" , limit = 1 ) }
29
+ .chunked(2 )
30
+ .fold(mutableMapOf<String , MutableList <String >>()) { m, (key, value) ->
31
+ m.apply {
32
+ computeIfAbsent(key) { mutableListOf () }.add(value)
33
+ }
34
+ }
35
+
36
+ val envPattern = Regex (" \\ $\\ {(\\ w+)}" )
37
+ val capabilitiesDirectory = options[" --out" ]
38
+ ?.first()
39
+ ?.let { env ->
40
+ envPattern.replace(env) {
41
+ System .getenv(it.groups[1 ]?.value)
42
+ }
43
+ }
44
+ ?.run (FileSystems .getDefault()::getPath)
45
+ ?.apply {
46
+ if (! parent.exists()) {
47
+ Files .createDirectories(parent)
48
+ }
49
+ }
50
+ ? : error(" --out is required" )
51
+
52
+ capabilitiesDirectory.resolve(capabilitiesName).writeText(
53
+ getArguments(K2JVMCompilerArguments ::class .java)
54
+ .filterNot(KotlincCapability ::shouldSuppress)
55
+ .asCapabilities()
56
+ .asCapabilitiesBzl()
57
+ .toString(),
58
+ StandardCharsets .UTF_8 ,
59
+ )
60
+
61
+ capabilitiesDirectory.resolve(" templates.bzl" ).writeText(
62
+ BzlDoc {
63
+ assignment(
64
+ " TEMPLATES" ,
65
+ list(
66
+ * Files .list(capabilitiesDirectory)
67
+ .filter { it.fileName.toString().startsWith(" capabilities_" ) }
68
+ .map { " Label(${it.fileName.bzlQuote()} )" }
69
+ .sorted()
70
+ .toArray(::arrayOfNulls),
71
+ ),
72
+ )
73
+ }.toString(),
74
+ )
75
+ }
76
+
21
77
/* * Options that are either confusing, useless, or unexpected to be set outside the worker. */
22
78
private val suppressedFlags = setOf (
23
79
" -P" ,
24
80
" -X" ,
25
81
" -Xbuild-file" ,
26
82
" -Xcompiler-plugin" ,
27
- " -Xcompiler-plugin" ,
28
83
" -Xdump-declarations-to" ,
29
84
" -Xdump-directory" ,
30
85
" -Xdump-fqname" ,
@@ -45,54 +100,109 @@ object WriteKotlincCapabilities {
45
100
" -script-templates" ,
46
101
)
47
102
48
- @JvmStatic
49
- fun main (vararg args : String ) {
50
- // TODO: Replace with a real option parser
51
- val options = args.asSequence()
52
- .flatMap { t -> t.split(" =" , limit = 1 ) }
53
- .chunked(2 )
54
- .fold(mutableMapOf<String , MutableList <String >>()) { m, (key, value) ->
55
- m.apply {
56
- computeIfAbsent(key) { mutableListOf () }.add(value)
57
- }
58
- }
59
-
60
- val instance = K2JVMCompilerArguments ()
103
+ fun String.increment () = " $this "
104
+ fun String.decrement () = substring(0 , (length - 2 ).coerceAtLeast(0 ))
61
105
62
- val capabilitiesName = LanguageVersion .LATEST_STABLE .run {
106
+ val capabilitiesName: String by lazy {
107
+ LanguageVersion .LATEST_STABLE .run {
63
108
" capabilities_${major} .${minor} .bzl.com_github_jetbrains_kotlin.bazel"
64
109
}
110
+ }
65
111
66
- val envPattern = Regex (" \\ $\\ {(\\ w+)}" )
67
- val capabilitiesDirectory = options[" --out" ]
68
- ?.first()
69
- ?.let { env ->
70
- envPattern.replace(env) {
71
- System .getenv(it.groups[1 ]?.value)
112
+ private class BzlDoc {
113
+ private val HEADER = Comment (
114
+ """
115
+ # Copyright ${Year .now()} The Bazel Authors. All rights reserved.
116
+ #
117
+ # Licensed under the Apache License, Version 2.0 (the "License");
118
+ # you may not use this file except in compliance with the License.
119
+ # You may obtain a copy of the License at
120
+ #
121
+ # http://www.apache.org/licenses/LICENSE-2.0
122
+ #
123
+ # Unless required by applicable law or agreed to in writing, software
124
+ # distributed under the License is distributed on an "AS IS" BASIS,
125
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
126
+ # See the License for the specific language governing permissions and
127
+ # limitations under the License.
128
+
129
+ # DO NOT EDIT: generated by bazel run //src/main/kotlin/io/bazel/kotlin/generate:kotlin_release_options
130
+ """ .trimIndent(),
131
+ )
132
+
133
+ val contents: MutableList <Block > = mutableListOf ()
134
+
135
+ constructor (statements: BzlDoc .() -> Unit ) {
136
+ statement(HEADER )
137
+ apply (statements)
138
+ }
139
+
140
+ fun statement (vararg statements : Block ) {
141
+ contents.addAll(statements)
142
+ }
143
+
144
+ class Indent (val spaces : Int = 0 ) {
145
+ fun increment () = Indent (spaces+ 2 )
146
+ fun decrement () = Indent (max(spaces - 2 , 0 ))
147
+ override fun toString () = " " .repeat(spaces)
148
+ operator fun plus (s : String? ) = toString() + s
149
+ }
150
+
151
+ fun interface Block {
152
+ fun asString (indent : Indent ): String?
153
+ fun asString () = asString(Indent ())
154
+ }
155
+
156
+ fun interface ValueBlock : Block {
157
+ fun asString (indent : Indent , map : (String ) -> String ): String?
158
+ override fun asString (indent : Indent ) = asString(indent.increment()) { it }
159
+ }
160
+
161
+ class Comment (val contents : String ) : Block {
162
+ override fun asString (indent : Indent ): String? = indent + contents
163
+ }
164
+
165
+ override fun toString () = contents.mapNotNull { it.asString() }.joinToString(" \n " )
166
+
167
+ fun assignment (key : String , value : ValueBlock ) {
168
+ statement(
169
+ Block { indent ->
170
+ indent + value.asString(indent.increment()) { " $key = $it " }
171
+ },
172
+ )
173
+ }
174
+
175
+ fun struct (vararg properties : Pair <String , String ?>) = ValueBlock { indent, format ->
176
+ properties
177
+ .mapNotNull { (key, value) ->
178
+ value?.let { " $indent$key = $it " }
72
179
}
73
- }
74
- ? : error(" --out is required" )
180
+ .joinToString(" ,\n " , prefix = " struct(\n " , postfix = " \n ${indent.decrement()} )" )
181
+ .run (format)
182
+ }
75
183
76
- FileSystems .getDefault()
77
- .getPath( " $capabilitiesDirectory / $capabilitiesName " )
78
- . apply {
79
- if ( ! parent.exists ()) {
80
- Files .createDirectories(parent)
184
+ fun dict ( vararg properties : Pair < String , ValueBlock >) = ValueBlock { indent, format ->
185
+ properties
186
+ .mapNotNull { (key, value) ->
187
+ value.asString(indent.increment ())
188
+ ?. let { " $indent${key.bzlQuote()} : $it " }
81
189
}
82
- writeText(
83
- getArguments(K2JVMCompilerArguments ::class .java)
84
- .filterNot(KotlincCapability ::shouldSuppress)
85
- .asCapabilities()
86
- .asCapabilitiesBzl(),
87
- StandardCharsets .UTF_8
88
- )
89
- }
90
- .let {
91
- println (" Wrote to $it " )
92
- }
190
+ .joinToString(" ,\n " , prefix = " {\n " , postfix = " \n ${indent.decrement()} }" )
191
+ .run (format)
192
+ }
193
+
194
+ fun list (vararg items : String ) = ValueBlock { indent, format ->
195
+ items
196
+ .joinToString(
197
+ separator = " ,\n " ,
198
+ prefix = " [\n " ,
199
+ postfix = " \n ${indent.decrement()} ]" ,
200
+ ) { " $indent$it " }
201
+ .run (format)
202
+ }
93
203
}
94
204
95
- private fun getArguments (klass : Class <* >) : Sequence <KotlincCapability > = sequence {
205
+ private fun getArguments (klass : Class <* >): Sequence <KotlincCapability > = sequence {
96
206
val instance = K2JVMCompilerArguments ()
97
207
klass.superclass?.let {
98
208
yieldAll(getArguments(it))
@@ -104,55 +214,86 @@ object WriteKotlincCapabilities {
104
214
yield (
105
215
KotlincCapability (
106
216
flag = argument.value,
107
- default = " ${ getter.invoke(instance)} " ,
108
- type = field.type.toString(),
109
- )
217
+ default = getter.invoke(instance)?.let (Any ::toString),
218
+ type = StarlarkType .mapFrom(field.type),
219
+ doc = argument.description,
220
+ ),
110
221
)
111
222
}
112
223
}
113
224
}
114
225
115
- private class KotlincCapabilities (capabilities : Iterable <KotlincCapability >) :
116
- List <KotlincCapability > by capabilities.toList() {
226
+ private class KotlincCapabilities (val capabilities : Iterable <KotlincCapability >) {
117
227
118
228
companion object {
119
229
fun Sequence<KotlincCapability>.asCapabilities () = KotlincCapabilities (sorted().toList())
120
230
}
121
231
122
- fun asCapabilitiesBzl () = HEADER + " \n " + joinToString(
123
- " ,\n " ,
124
- prefix = " KOTLIN_OPTS = [\n " ,
125
- postfix = " \n ]" ,
126
- transform = KotlincCapability ::asCapabilityFlag,
127
- )
232
+ fun asCapabilitiesBzl () = BzlDoc {
233
+ assignment(
234
+ " KOTLIN_OPTS" ,
235
+ dict(
236
+ * capabilities.map { capability ->
237
+ capability.flag to struct(
238
+ " flag" to capability.flag.bzlQuote(),
239
+ " doc" to capability.doc.bzlQuote(),
240
+ " default" to capability.defaultStarlarkValue(),
241
+ )
242
+ }.toTypedArray(),
243
+ ),
244
+ )
245
+ }
128
246
}
129
247
130
- private data class KotlincCapability (
131
- private val flag : String ,
132
- private val default : String ,
133
- private val type : String ,
248
+ data class KotlincCapability (
249
+ val flag : String ,
250
+ val doc : String ,
251
+ private val default : String? ,
252
+ private val type : StarlarkType ,
134
253
) : Comparable<KotlincCapability> {
135
254
136
- fun shouldSuppress () = flag in suppressedFlags
255
+ fun shouldSuppress () = flag in suppressedFlags
256
+
257
+ fun defaultStarlarkValue (): String? = type.convert(default)
137
258
138
- fun asCapabilityFlag () = " \" ${flag} \" "
139
259
override fun compareTo (other : KotlincCapability ): Int = flag.compareTo(other.flag)
140
260
}
141
261
262
+ sealed class StarlarkType (val attr : String ) {
142
263
143
- private val HEADER = """
144
- # Copyright ${Year .now()} The Bazel Authors. All rights reserved.
145
- #
146
- # Licensed under the Apache License, Version 2.0 (the "License");
147
- # you may not use this file except in compliance with the License.
148
- # You may obtain a copy of the License at
149
- #
150
- # http://www.apache.org/licenses/LICENSE-2.0
151
- #
152
- # Unless required by applicable law or agreed to in writing, software
153
- # distributed under the License is distributed on an "AS IS" BASIS,
154
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
155
- # See the License for the specific language governing permissions and
156
- # limitations under the License.
157
- """ .trimIndent()
264
+ class Bool () : StarlarkType(" attr.bool" ) {
265
+ override fun convert (value : String? ): String? = when (value) {
266
+ " true" -> " True"
267
+ else -> " False"
268
+ }
269
+ }
270
+
271
+ class Str () : StarlarkType(" attr.string" ) {
272
+ override fun convert (value : String? ): String? = value?.bzlQuote() ? : " None"
273
+ }
274
+
275
+ class StrList () : StarlarkType(" attr.string_list" ) {
276
+ override fun convert (value : String? ): String =
277
+ value?.let { " default = [${it.bzlQuote()} ]" } ? : " []"
278
+ }
279
+
280
+ companion object {
281
+ fun mapFrom (clazz : Class <* >) = when (clazz.canonicalName) {
282
+ java.lang.Boolean .TYPE .canonicalName, java.lang.Boolean ::class .java.canonicalName -> Bool ()
283
+ java.lang.String ::class .java.canonicalName -> Str ()
284
+ Array <String >::class .java.canonicalName -> StrList ()
285
+ else -> {
286
+ throw IllegalArgumentException (" ($clazz )is not a starlark mappable type" )
287
+ }
288
+ }
289
+ }
290
+
291
+ abstract fun convert (value : String? ): String?
292
+ }
293
+
294
+ private fun Any.bzlQuote (): String {
295
+ var asString = toString()
296
+ val quote = " \" " .repeat(if (" \n " in asString || " \" " in asString) 3 else 1 )
297
+ return quote + asString + quote
298
+ }
158
299
}
0 commit comments