Skip to content

Commit b1b107f

Browse files
authored
dataconnect: fix build on macos and windows (#6350)
1 parent 3f3b688 commit b1b107f

File tree

7 files changed

+423
-133
lines changed

7 files changed

+423
-133
lines changed

firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutable.kt

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,11 @@
1515
*/
1616
package com.google.firebase.dataconnect.gradle.plugin
1717

18-
import java.io.InputStream
19-
import kotlinx.serialization.ExperimentalSerializationApi
20-
import kotlinx.serialization.json.Json
21-
import kotlinx.serialization.json.decodeFromStream
22-
2318
sealed interface DataConnectExecutable {
2419

2520
data class File(val file: java.io.File) : DataConnectExecutable
2621

2722
data class RegularFile(val file: org.gradle.api.file.RegularFile) : DataConnectExecutable
2823

29-
data class Version(val version: String) : DataConnectExecutable {
30-
companion object {
31-
val default: Version
32-
get() = Version(VersionsJson.load().default)
33-
}
34-
}
35-
36-
@OptIn(ExperimentalSerializationApi::class)
37-
object VersionsJson {
38-
39-
const val RESOURCE_PATH =
40-
"com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableVersions.json"
41-
42-
fun load(): Root = openFile().use { Json.decodeFromStream<Root>(it) }
43-
44-
private fun openFile(): InputStream =
45-
this::class.java.classLoader.getResourceAsStream(RESOURCE_PATH)
46-
?: throw DataConnectGradleException("antkaw2gjp", "resource not found: $RESOURCE_PATH")
47-
48-
@kotlinx.serialization.Serializable
49-
data class Root(
50-
val default: String,
51-
val versions: Map<String, VerificationInfo>,
52-
)
53-
54-
@kotlinx.serialization.Serializable
55-
data class VerificationInfo(val size: Long, val sha512DigestHex: String)
56-
}
24+
data class Version(val version: String) : DataConnectExecutable
5725
}

firebase-dataconnect/gradleplugin/plugin/src/main/kotlin/com/google/firebase/dataconnect/gradle/plugin/DataConnectExecutableDownloadTask.kt

Lines changed: 105 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ import java.util.regex.Pattern
2323
import kotlin.time.Duration.Companion.seconds
2424
import kotlin.time.DurationUnit
2525
import kotlin.time.toDuration
26-
import kotlinx.serialization.encodeToString
27-
import kotlinx.serialization.json.Json
2826
import org.gradle.api.DefaultTask
27+
import org.gradle.api.Task
2928
import org.gradle.api.file.DirectoryProperty
3029
import org.gradle.api.file.RegularFileProperty
3130
import org.gradle.api.provider.Property
@@ -42,6 +41,8 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
4241

4342
@get:Input @get:Optional abstract val version: Property<String>
4443

44+
@get:Input @get:Optional abstract val operatingSystem: Property<OperatingSystem>
45+
4546
@get:Internal abstract val buildDirectory: DirectoryProperty
4647

4748
@get:OutputFile abstract val outputFile: RegularFileProperty
@@ -50,11 +51,13 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
5051
fun run() {
5152
val inputFile: File? = inputFile.orNull?.asFile
5253
val version: String? = version.orNull
54+
val operatingSystem: OperatingSystem = operatingSystem.get()
5355
val buildDirectory: File = buildDirectory.get().asFile
5456
val outputFile: File = outputFile.get().asFile
5557

5658
logger.info("inputFile: {}", inputFile)
5759
logger.info("version: {}", version)
60+
logger.info("operatingSystem: {}", operatingSystem)
5861
logger.info("buildDirectory: {}", buildDirectory)
5962
logger.info("outputFile: {}", outputFile)
6063

@@ -71,8 +74,8 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
7174
} else if (inputFile !== null) {
7275
runWithFile(inputFile = inputFile, outputFile = outputFile)
7376
} else if (version !== null) {
74-
runWithVersion(version = version, outputFile = outputFile)
75-
verifyOutputFile(outputFile, version)
77+
downloadDataConnectExecutable(version, operatingSystem, outputFile)
78+
verifyOutputFile(outputFile, operatingSystem, version)
7679
} else {
7780
throw DataConnectGradleException(
7881
"chc94cq7vx",
@@ -82,66 +85,73 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
8285
}
8386
}
8487

85-
private fun verifyOutputFile(outputFile: File, version: String) {
88+
private fun verifyOutputFile(
89+
outputFile: File,
90+
operatingSystem: OperatingSystem,
91+
version: String
92+
) {
8693
logger.info("Verifying file size and SHA512 digest of file: {}", outputFile)
8794
val fileInfo = FileInfo.forFile(outputFile)
8895

89-
val verificationInfoJsonString =
90-
jsonPrettyPrint.encodeToString(
91-
DataConnectExecutable.VersionsJson.VerificationInfo(
92-
size = fileInfo.sizeInBytes,
93-
sha512DigestHex = fileInfo.sha512DigestHex,
94-
)
95-
)
96+
val allVersions = DataConnectExecutableVersionsRegistry.load().versions
97+
val allVersionNames =
98+
allVersions
99+
.asSequence()
100+
.filter { it.os == operatingSystem }
101+
.map { it.version }
102+
.distinct()
103+
.sorted()
104+
.joinToString(", ")
105+
val applicableVersions =
106+
allVersions.filter { it.version == version && it.os == operatingSystem }
96107

97-
val verificationInfoByVersion = DataConnectExecutable.VersionsJson.load().versions
98-
val verificationInfo = verificationInfoByVersion[version]
99-
if (verificationInfo === null) {
108+
if (applicableVersions.isEmpty()) {
100109
val message =
101-
"verification information for ${outputFile.absolutePath}" +
102-
" (version $version) is not known; known versions are: " +
103-
verificationInfoByVersion.keys.sorted().joinToString(", ")
110+
"verification information for Data Connect toolkit executable" +
111+
" version $version for $operatingSystem is not known;" +
112+
" known versions for $operatingSystem are: $allVersionNames" +
113+
" (loaded from ${DataConnectExecutableVersionsRegistry.PATH})"
104114
logger.error("ERROR: $message")
105-
logger.error(
106-
"To update ${DataConnectExecutable.VersionsJson.RESOURCE_PATH} with" +
107-
" information about this version, add this JSON blob: $verificationInfoJsonString"
108-
)
109115
throw DataConnectGradleException("ym8assbfgw", message)
116+
} else if (applicableVersions.size > 1) {
117+
val message =
118+
"INTERNAL ERROR: ${applicableVersions.size} verification information records for" +
119+
" Data Connect toolkit executable version $version for $operatingSystem were found in" +
120+
" ${DataConnectExecutableVersionsRegistry.PATH}, but expected exactly 1"
121+
logger.error("ERROR: $message")
122+
throw DataConnectGradleException("zyw5xrky6e", message)
110123
}
111124

125+
val versionInfo = applicableVersions.single()
112126
val verificationErrors = mutableListOf<String>()
113-
if (fileInfo.sizeInBytes != verificationInfo.size) {
127+
if (fileInfo.sizeInBytes != versionInfo.size) {
114128
logger.error(
115129
"ERROR: File ${outputFile.absolutePath} has an unexpected size (in bytes): actual is " +
116130
fileInfo.sizeInBytes.toStringWithThousandsSeparator() +
117131
" but expected " +
118-
verificationInfo.size.toStringWithThousandsSeparator()
132+
versionInfo.size.toStringWithThousandsSeparator()
119133
)
120134
verificationErrors.add("file size mismatch")
121135
}
122-
if (fileInfo.sha512DigestHex != verificationInfo.sha512DigestHex) {
136+
if (fileInfo.sha512DigestHex != versionInfo.sha512DigestHex) {
123137
logger.error(
124138
"ERROR: File ${outputFile.absolutePath} has an unexpected SHA512 digest:" +
125139
" actual is ${fileInfo.sha512DigestHex}" +
126-
" but expected ${verificationInfo.sha512DigestHex}"
140+
" but expected ${versionInfo.sha512DigestHex}"
127141
)
128142
verificationErrors.add("SHA512 digest mismatch")
129143
}
130144

131-
if (verificationErrors.isEmpty()) {
132-
logger.info("Verifying file size and SHA512 digest succeeded")
133-
return
145+
if (verificationErrors.isNotEmpty()) {
146+
val errorMessage =
147+
"Verification of ${outputFile.absolutePath}" +
148+
" (version=${versionInfo.version} os=${versionInfo.os}) failed:" +
149+
" ${verificationErrors.joinToString(", ")}"
150+
logger.error(errorMessage)
151+
throw DataConnectGradleException("x9dfwhjr9c", errorMessage)
134152
}
135153

136-
logger.error(
137-
"To update ${DataConnectExecutable.VersionsJson.RESOURCE_PATH} with" +
138-
" information about this version, add this JSON blob: $verificationInfoJsonString"
139-
)
140-
141-
throw DataConnectGradleException(
142-
"x9dfwhjr9c",
143-
"Verification of ${outputFile.absolutePath} failed: ${verificationErrors.joinToString(", ")}"
144-
)
154+
logger.info("Verifying file size and SHA512 digest succeeded")
145155
}
146156

147157
data class FileInfo(val sizeInBytes: Long, val sha512DigestHex: String) {
@@ -181,62 +191,73 @@ abstract class DataConnectExecutableDownloadTask : DefaultTask() {
181191
}
182192
}
183193

184-
private fun runWithVersion(version: String, outputFile: File) {
185-
val fileName = "dataconnect-emulator-linux-v$version"
186-
val url = URL("https://storage.googleapis.com/firemat-preview-drop/emulator/$fileName")
194+
companion object {
195+
fun Task.downloadDataConnectExecutable(
196+
version: String,
197+
operatingSystem: OperatingSystem,
198+
outputFile: File
199+
) {
200+
val osName =
201+
when (operatingSystem) {
202+
OperatingSystem.Windows -> "windows"
203+
OperatingSystem.MacOS -> "macos"
204+
OperatingSystem.Linux -> "linux"
205+
}
206+
val downloadFileName = "dataconnect-emulator-$osName-v$version"
207+
val url =
208+
URL("https://storage.googleapis.com/firemat-preview-drop/emulator/$downloadFileName")
187209

188-
logger.info("Downloading {} to {}", url, outputFile)
189-
project.mkdir(outputFile.parentFile)
210+
logger.info("Downloading {} to {}", url, outputFile)
211+
project.mkdir(outputFile.parentFile)
190212

191-
val connection = url.openConnection() as HttpURLConnection
192-
connection.requestMethod = "GET"
213+
val connection = url.openConnection() as HttpURLConnection
214+
connection.requestMethod = "GET"
193215

194-
val responseCode = connection.responseCode
195-
if (responseCode != HttpURLConnection.HTTP_OK) {
196-
throw DataConnectGradleException(
197-
"n3mj6ahxwt",
198-
"Downloading Data Connect executable from $url failed with HTTP response code" +
199-
" $responseCode: ${connection.responseMessage}" +
200-
" (expected HTTP response code ${HttpURLConnection.HTTP_OK})"
201-
)
202-
}
203-
204-
val startTime = System.nanoTime()
205-
val debouncer = Debouncer(5.seconds)
206-
outputFile.outputStream().use { oStream ->
207-
var downloadByteCount: Long = 0
208-
fun logDownloadedBytes() {
209-
val elapsedTime = (System.nanoTime() - startTime).toDuration(DurationUnit.NANOSECONDS)
210-
logger.info(
211-
"Downloaded {} bytes in {}",
212-
downloadByteCount.toStringWithThousandsSeparator(),
213-
elapsedTime
216+
val responseCode = connection.responseCode
217+
if (responseCode != HttpURLConnection.HTTP_OK) {
218+
throw DataConnectGradleException(
219+
"n3mj6ahxwt",
220+
"Downloading Data Connect executable from $url failed with HTTP response code" +
221+
" $responseCode: ${connection.responseMessage}" +
222+
" (expected HTTP response code ${HttpURLConnection.HTTP_OK})"
214223
)
215224
}
216-
connection.inputStream.use { iStream ->
217-
val buffer = ByteArray(8192)
218-
while (true) {
219-
val readCount = iStream.read(buffer)
220-
if (readCount < 0) {
221-
break
225+
226+
val startTime = System.nanoTime()
227+
val debouncer = Debouncer(5.seconds)
228+
outputFile.outputStream().use { oStream ->
229+
var downloadByteCount: Long = 0
230+
fun logDownloadedBytes() {
231+
val elapsedTime = (System.nanoTime() - startTime).toDuration(DurationUnit.NANOSECONDS)
232+
logger.info(
233+
"Downloaded {} bytes in {}",
234+
downloadByteCount.toStringWithThousandsSeparator(),
235+
elapsedTime
236+
)
237+
}
238+
connection.inputStream.use { iStream ->
239+
val buffer = ByteArray(8192)
240+
while (true) {
241+
val readCount = iStream.read(buffer)
242+
if (readCount < 0) {
243+
break
244+
}
245+
downloadByteCount += readCount
246+
debouncer.maybeRun(::logDownloadedBytes)
247+
oStream.write(buffer, 0, readCount)
222248
}
223-
downloadByteCount += readCount
224-
debouncer.maybeRun(::logDownloadedBytes)
225-
oStream.write(buffer, 0, readCount)
226249
}
250+
logDownloadedBytes()
227251
}
228-
logDownloadedBytes()
229-
}
230252

231-
project.exec { execSpec ->
232-
execSpec.run {
233-
executable = "chmod"
234-
args = listOf("a+x", outputFile.absolutePath)
253+
if (operatingSystem != OperatingSystem.Windows) {
254+
project.exec { execSpec ->
255+
execSpec.run {
256+
executable = "chmod"
257+
args = listOf("a+x", outputFile.absolutePath)
258+
}
259+
}
235260
}
236261
}
237262
}
238-
239-
private companion object {
240-
val jsonPrettyPrint = Json { prettyPrint = true }
241-
}
242263
}

0 commit comments

Comments
 (0)