15
15
*/
16
16
package com.google.firebase.dataconnect.gradle.plugin
17
17
18
+ import com.google.firebase.dataconnect.gradle.plugin.DataConnectGenerateCodeTask.CallingConvention
18
19
import java.io.File
20
+ import javax.inject.Inject
19
21
import org.gradle.api.DefaultTask
20
- import org.gradle.api.Task
21
22
import org.gradle.api.file.DirectoryProperty
22
23
import org.gradle.api.file.RegularFileProperty
23
24
import org.gradle.api.provider.Property
25
+ import org.gradle.api.provider.Provider
24
26
import org.gradle.api.tasks.Input
25
27
import org.gradle.api.tasks.InputFile
26
28
import org.gradle.api.tasks.InputFiles
27
29
import org.gradle.api.tasks.Internal
28
30
import org.gradle.api.tasks.Optional
29
31
import org.gradle.api.tasks.OutputDirectory
30
32
import org.gradle.api.tasks.TaskAction
33
+ import org.gradle.process.ExecOperations
34
+ import org.slf4j.Logger
31
35
32
36
abstract class DataConnectGenerateCodeTask : DefaultTask () {
33
37
34
38
@get:InputFile abstract val dataConnectExecutable: RegularFileProperty
35
39
40
+ @get:Input abstract val dataConnectExecutableCallingConvention: Property <CallingConvention >
41
+
36
42
@get:Optional @get:InputFiles abstract val configDirectory: DirectoryProperty
37
43
38
44
@get:Input abstract val connectors: Property <Collection <String >>
@@ -43,16 +49,32 @@ abstract class DataConnectGenerateCodeTask : DefaultTask() {
43
49
44
50
@get:Optional @get:InputFile abstract val ktfmtJarFile: RegularFileProperty
45
51
52
+ @get:Inject abstract val execOperations: ExecOperations
53
+
54
+ /* *
55
+ * The subcommand of the Data Connect executable to use to perform code generation.
56
+ *
57
+ * In August 2025 the subcommand was changed by cl/795582011 from "gradle generate" to "sdk
58
+ * generate -platform=kotlin". The "gradle generate" command was last supported in version 2.11.0
59
+ * of the Data Connect executable.
60
+ */
61
+ enum class CallingConvention {
62
+ GRADLE ,
63
+ SDK_GENERATE ,
64
+ }
65
+
46
66
@TaskAction
47
67
fun run () {
48
68
val dataConnectExecutable: File = dataConnectExecutable.get().asFile
69
+ val dataConnectExecutableCallingConvention = dataConnectExecutableCallingConvention.get()
49
70
val configDirectory: File ? = configDirectory.orNull?.asFile
50
71
val connectors: Collection <String > = connectors.get().distinct().sorted()
51
72
val buildDirectory: File = buildDirectory.get().asFile
52
73
val outputDirectory: File = outputDirectory.get().asFile
53
74
val ktfmtJarFile: File ? = ktfmtJarFile.orNull?.asFile
54
75
55
76
logger.info(" dataConnectExecutable={}" , dataConnectExecutable.absolutePath)
77
+ logger.info(" dataConnectExecutableCallingConvention={}" , dataConnectExecutableCallingConvention)
56
78
logger.info(" configDirectory={}" , configDirectory?.absolutePath)
57
79
logger.info(" connectors={}" , connectors.joinToString(" , " ))
58
80
logger.info(" buildDirectory={}" , buildDirectory.absolutePath)
@@ -69,14 +91,26 @@ abstract class DataConnectGenerateCodeTask : DefaultTask() {
69
91
return
70
92
}
71
93
94
+ val subCommand =
95
+ when (dataConnectExecutableCallingConvention) {
96
+ CallingConvention .GRADLE -> listOf (" gradle" , " generate" )
97
+ CallingConvention .SDK_GENERATE -> listOf (" sdk" , " generate" )
98
+ }
99
+
72
100
runDataConnectExecutable(
73
101
dataConnectExecutable = dataConnectExecutable,
74
- subCommand = listOf ( " gradle " , " generate " ) ,
102
+ subCommand = subCommand ,
75
103
configDirectory = configDirectory,
76
104
) {
77
- this .connectors = connectors
105
+ when (dataConnectExecutableCallingConvention) {
106
+ CallingConvention .GRADLE -> this .connectors = connectors
107
+ CallingConvention .SDK_GENERATE -> this .connectorId = connectors
108
+ }
78
109
this .outputDirectory = outputDirectory
79
110
this .logFile = File (buildDirectory, " codegen.log.txt" )
111
+ if (dataConnectExecutableCallingConvention == CallingConvention .SDK_GENERATE ) {
112
+ this .platform = " kotlin"
113
+ }
80
114
}
81
115
82
116
if (ktfmtJarFile != = null ) {
@@ -92,7 +126,7 @@ abstract class DataConnectGenerateCodeTask : DefaultTask() {
92
126
}
93
127
}
94
128
95
- private fun Task .runKtfmt (
129
+ private fun DataConnectGenerateCodeTask .runKtfmt (
96
130
ktfmtJarFile : File ,
97
131
directory : File ,
98
132
logFile : File ,
@@ -123,3 +157,80 @@ private fun Task.runKtfmt(
123
157
}
124
158
}
125
159
}
160
+
161
+ fun DataConnectGenerateCodeTask.detectedCallingConvention (
162
+ dataConnectExecutable : RegularFileProperty = this.dataConnectExecutable,
163
+ buildDirectory : DirectoryProperty = this.buildDirectory,
164
+ execOperations : ExecOperations = this.execOperations,
165
+ logger : Logger = this.logger
166
+ ): Provider <CallingConvention > =
167
+ dataConnectExecutable.map {
168
+ determineCallingConvention(
169
+ dataConnectExecutable = it.asFile,
170
+ workDirectory = File (buildDirectory.get().asFile, " determineCallingConvention" ),
171
+ execOperations = execOperations,
172
+ logger = logger,
173
+ )
174
+ }
175
+
176
+ private fun determineCallingConvention (
177
+ dataConnectExecutable : File ,
178
+ workDirectory : File ,
179
+ execOperations : ExecOperations ,
180
+ logger : Logger ,
181
+ ): CallingConvention {
182
+ logger.info(
183
+ " Determining calling convention of Data Connect executable: {}" ,
184
+ dataConnectExecutable.absolutePath
185
+ )
186
+
187
+ val callingConventionResults =
188
+ CallingConvention .entries.map { callingConvention ->
189
+ val logFile =
190
+ File (workDirectory, " $callingConvention .log.txt" ).also { it.parentFile.mkdirs() }
191
+ logger.info(
192
+ " Testing {} for support of calling convention {} (log file: {})" ,
193
+ dataConnectExecutable.absolutePath,
194
+ callingConvention,
195
+ logFile.absolutePath
196
+ )
197
+
198
+ val exitCode: Int =
199
+ logFile.outputStream().use { logFileStream ->
200
+ execOperations
201
+ .exec { execSpec ->
202
+ execSpec.run {
203
+ executable(dataConnectExecutable)
204
+ isIgnoreExitValue = true
205
+ standardOutput = logFileStream
206
+ errorOutput = logFileStream
207
+ when (callingConvention) {
208
+ CallingConvention .GRADLE -> args(" gradle" , " help" , " generate" )
209
+ CallingConvention .SDK_GENERATE -> args(" sdk" , " help" , " generate" )
210
+ }
211
+ }
212
+ }
213
+ .exitValue
214
+ }
215
+
216
+ val callingConventionSupported = exitCode == 0
217
+ logger.info(
218
+ " Testing {} for support of calling convention {} completed: {} (exitCode={})" ,
219
+ dataConnectExecutable.absolutePath,
220
+ callingConvention,
221
+ callingConventionSupported,
222
+ exitCode
223
+ )
224
+ Pair (callingConvention, callingConventionSupported)
225
+ }
226
+
227
+ val supportedCallingConventions: List <CallingConvention > =
228
+ callingConventionResults.filter { it.second }.map { it.first }
229
+ return supportedCallingConventions.singleOrNull()
230
+ ? : throw DataConnectGradleException (
231
+ " d24j9dm3r6" ,
232
+ " could not detect calling convention of Data Connect executable ${dataConnectExecutable.absolutePath} : " +
233
+ " found ${supportedCallingConventions.size} supported calling conventions, but expected exactly 1: " +
234
+ supportedCallingConventions.joinToString(" , " )
235
+ )
236
+ }
0 commit comments