build: make toolchain inputs reproducible
This commit is contained in:
+140
-23
@@ -1,5 +1,54 @@
|
||||
import java.io.FileInputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.security.MessageDigest
|
||||
import java.util.Properties
|
||||
import java.util.UUID
|
||||
|
||||
fun verifySha256(file: File, expected: String, identity: String) {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
file.inputStream().buffered().use { input ->
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
while (true) {
|
||||
val count = input.read(buffer)
|
||||
if (count < 0) break
|
||||
digest.update(buffer, 0, count)
|
||||
}
|
||||
}
|
||||
val actual = digest.digest().joinToString("") {
|
||||
(it.toInt() and 0xff).toString(16).padStart(2, '0')
|
||||
}
|
||||
if (actual != expected) {
|
||||
throw GradleException("SHA-256 mismatch for $identity: expected $expected, got $actual")
|
||||
}
|
||||
}
|
||||
|
||||
fun promoteDirectory(staging: File, destination: File) {
|
||||
val backup = File(destination.parentFile, "${destination.name}.backup-${UUID.randomUUID()}")
|
||||
val hadDestination = destination.exists()
|
||||
try {
|
||||
if (hadDestination) {
|
||||
Files.move(destination.toPath(), backup.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
}
|
||||
try {
|
||||
Files.move(staging.toPath(), destination.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
} catch (promotionFailure: Exception) {
|
||||
if (hadDestination && backup.exists()) {
|
||||
try {
|
||||
Files.move(backup.toPath(), destination.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
} catch (restoreFailure: Exception) {
|
||||
promotionFailure.addSuppressed(restoreFailure)
|
||||
}
|
||||
}
|
||||
throw promotionFailure
|
||||
}
|
||||
if (hadDestination && backup.exists() && !backup.deleteRecursively()) {
|
||||
throw GradleException("Failed to remove obsolete native artifact backup at ${backup.absolutePath}")
|
||||
}
|
||||
} finally {
|
||||
staging.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
@@ -9,17 +58,35 @@ plugins {
|
||||
}
|
||||
|
||||
val mpvVersion = "v1.0.7"
|
||||
val mpvSha256 = "d55d440e587b2a9ffb91874d93069460a987be05fe72af8394849983f0df2d7a"
|
||||
val mpvDir = layout.buildDirectory.dir("libmpv").get().asFile
|
||||
val mpvAar = "libmpv-release.aar"
|
||||
val mpvUrl = "https://github.com/edde746/libmpv-android/releases/download/$mpvVersion/$mpvAar"
|
||||
|
||||
val downloadLibmpv by tasks.registering {
|
||||
val stamp = File(mpvDir, ".version")
|
||||
outputs.upToDateWhen { stamp.exists() && stamp.readText().trim() == mpvVersion }
|
||||
val aar = File(mpvDir, mpvAar)
|
||||
val manifest = File(mpvDir, ".manifest")
|
||||
inputs.property("version", mpvVersion)
|
||||
inputs.property("sourceUrl", mpvUrl)
|
||||
inputs.property("sha256", mpvSha256)
|
||||
outputs.files(aar, manifest)
|
||||
doLast {
|
||||
mpvDir.mkdirs()
|
||||
val url = "https://github.com/edde746/libmpv-android/releases/download/$mpvVersion/$mpvAar"
|
||||
exec { commandLine("curl", "-sfL", url, "-o", File(mpvDir, mpvAar).absolutePath) }
|
||||
stamp.writeText(mpvVersion)
|
||||
mpvDir.parentFile.mkdirs()
|
||||
val staging = File(mpvDir.parentFile, "${mpvDir.name}.staging-${UUID.randomUUID()}")
|
||||
try {
|
||||
staging.mkdirs()
|
||||
val stagedAar = File(staging, mpvAar)
|
||||
try {
|
||||
exec { commandLine("curl", "-sfL", mpvUrl, "-o", stagedAar.absolutePath) }
|
||||
} catch (error: Exception) {
|
||||
throw GradleException("Failed to download $mpvAar $mpvVersion", error)
|
||||
}
|
||||
verifySha256(stagedAar, mpvSha256, "$mpvAar $mpvVersion")
|
||||
File(staging, ".manifest").writeText("version=$mpvVersion\nsha256=$mpvSha256\n")
|
||||
promoteDirectory(staging, mpvDir)
|
||||
} finally {
|
||||
staging.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,28 +117,78 @@ val extractMpvLibcxx by tasks.registering {
|
||||
|
||||
val doviVersion = "2.3.1"
|
||||
val doviDir = layout.buildDirectory.dir("libdovi").get().asFile
|
||||
val doviAbis = mapOf(
|
||||
"arm64-v8a" to "aarch64-linux-android",
|
||||
"armeabi-v7a" to "armv7-linux-androideabi",
|
||||
"x86" to "i686-linux-android",
|
||||
"x86_64" to "x86_64-linux-android"
|
||||
val doviArtifacts = mapOf(
|
||||
"arm64-v8a" to Pair(
|
||||
"aarch64-linux-android",
|
||||
"9d2983fc86f2f9e6da54c3c84ba8ea3a528690619f312ff4620198071b84e9ae"
|
||||
),
|
||||
"armeabi-v7a" to Pair(
|
||||
"armv7-linux-androideabi",
|
||||
"ed6fec8bf744e41c661b97f5fc4bf1197ebe9b09a140cbde369728e790ee3a68"
|
||||
),
|
||||
"x86" to Pair(
|
||||
"i686-linux-android",
|
||||
"50f0a5606e617dff8976b9e7930a23272f4804882a35a6f0f2b2f2d3f8ed7135"
|
||||
),
|
||||
"x86_64" to Pair(
|
||||
"x86_64-linux-android",
|
||||
"eba59678f89b792f5c6f802962e237542fe8328f6aa03a0a90ee77353dac3194"
|
||||
)
|
||||
)
|
||||
val doviBaseUrl = "https://github.com/edde746/libdovi-builds/releases/download/v$doviVersion"
|
||||
|
||||
val downloadLibdovi by tasks.registering {
|
||||
val stamp = File(doviDir, ".version")
|
||||
outputs.upToDateWhen { stamp.exists() && stamp.readText().trim() == doviVersion }
|
||||
val manifest = File(doviDir, ".manifest")
|
||||
inputs.property("version", doviVersion)
|
||||
inputs.property("baseUrl", doviBaseUrl)
|
||||
doviArtifacts.forEach { (abi, artifact) ->
|
||||
inputs.property("$abi.triple", artifact.first)
|
||||
inputs.property("$abi.sha256", artifact.second)
|
||||
inputs.property("$abi.sourceUrl", "$doviBaseUrl/libdovi-${artifact.first}.tar.gz")
|
||||
}
|
||||
outputs.files(doviArtifacts.keys.map { abi -> File(doviDir, "$abi/lib/libdovi.a") } + manifest)
|
||||
doLast {
|
||||
doviDir.mkdirs()
|
||||
val baseUrl = "https://github.com/edde746/libdovi-builds/releases/download/v$doviVersion"
|
||||
doviAbis.forEach { (abi, triple) ->
|
||||
val archive = File(doviDir, "$triple.tar.gz")
|
||||
exec { commandLine("curl", "-sfL", "$baseUrl/libdovi-$triple.tar.gz", "-o", archive.absolutePath) }
|
||||
val outDir = File(doviDir, "$abi/lib")
|
||||
outDir.mkdirs()
|
||||
exec { commandLine("tar", "-xzf", archive.absolutePath, "-C", outDir.absolutePath) }
|
||||
archive.delete()
|
||||
doviDir.parentFile.mkdirs()
|
||||
val staging = File(doviDir.parentFile, "${doviDir.name}.staging-${UUID.randomUUID()}")
|
||||
try {
|
||||
staging.mkdirs()
|
||||
val downloads = File(staging, ".downloads").apply { mkdirs() }
|
||||
doviArtifacts.forEach { (abi, artifact) ->
|
||||
val (triple, expectedSha256) = artifact
|
||||
val archiveName = "libdovi-$triple.tar.gz"
|
||||
val archive = File(downloads, archiveName)
|
||||
val sourceUrl = "$doviBaseUrl/$archiveName"
|
||||
try {
|
||||
exec { commandLine("curl", "-sfL", sourceUrl, "-o", archive.absolutePath) }
|
||||
} catch (error: Exception) {
|
||||
throw GradleException("Failed to download $archiveName v$doviVersion", error)
|
||||
}
|
||||
verifySha256(archive, expectedSha256, "$archiveName v$doviVersion")
|
||||
|
||||
val outDir = File(staging, "$abi/lib").apply { mkdirs() }
|
||||
try {
|
||||
exec { commandLine("tar", "-xzf", archive.absolutePath, "-C", outDir.absolutePath) }
|
||||
} catch (error: Exception) {
|
||||
throw GradleException("Failed to extract $archiveName", error)
|
||||
}
|
||||
if (!File(outDir, "libdovi.a").isFile) {
|
||||
throw GradleException("$archiveName did not contain the expected libdovi.a")
|
||||
}
|
||||
}
|
||||
if (!downloads.deleteRecursively()) {
|
||||
throw GradleException("Failed to clean staged libdovi archives")
|
||||
}
|
||||
val manifestText = buildString {
|
||||
append("version=$doviVersion\n")
|
||||
doviArtifacts.forEach { (abi, artifact) ->
|
||||
append("$abi=${artifact.first},${artifact.second}\n")
|
||||
}
|
||||
}
|
||||
File(staging, ".manifest").writeText(manifestText)
|
||||
promoteDirectory(staging, doviDir)
|
||||
} finally {
|
||||
staging.deleteRecursively()
|
||||
}
|
||||
stamp.writeText(doviVersion)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user