You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

123 lines
4.3 KiB

/*
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package org.jetbrains.compose.desktop.application.internal.files
import org.gradle.process.ExecOperations
import org.jetbrains.compose.desktop.application.internal.MacUtils
import org.jetbrains.compose.desktop.application.internal.isJarFile
import org.jetbrains.compose.desktop.application.internal.validation.ValidatedMacOSSigningSettings
import java.io.*
import java.util.regex.Pattern
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
internal class MacJarSignFileCopyingProcessor(
private val tempDir: File,
private val execOperations: ExecOperations,
private val signing: ValidatedMacOSSigningSettings
) : FileCopyingProcessor {
private val signKey: String
init {
val certificates = ByteArrayOutputStream().use { baos ->
PrintStream(baos).use { ps ->
execOperations.exec { exec ->
exec.executable = MacUtils.security.absolutePath
val args = arrayListOf("find-certificate", "-a", "-c", signing.fullDeveloperID)
signing.keychain?.let { args.add(it.absolutePath) }
exec.args(*args.toTypedArray())
exec.standardOutput = ps
}
}
baos.toString()
}
val regex = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\"")
val m = regex.matcher(certificates)
if (!m.find()) {
val keychainPath = signing.keychain?.absolutePath
error(
"Could not find certificate for '${signing.identity}'" +
" in keychain [${keychainPath.orEmpty()}]"
)
}
signKey = m.group(1)
if (m.find()) error("Multiple matching certificates are found for '${signing.fullDeveloperID}'. " +
"Please specify keychain containing unique matching certificate.")
}
override fun copy(source: File, target: File) {
if (!source.isJarFile) {
SimpleFileCopyingProcessor.copy(source, target)
return
}
if (target.exists()) target.delete()
ZipInputStream(FileInputStream(source).buffered()).use { zin ->
ZipOutputStream(FileOutputStream(target).buffered()).use { zout ->
copyAndSignNativeLibs(zin, zout)
}
}
}
private fun copyAndSignNativeLibs(zin: ZipInputStream, zout: ZipOutputStream) {
for (sourceEntry in generateSequence { zin.nextEntry }) {
if (!sourceEntry.name.endsWith(".dylib")) {
zout.putNextEntry(ZipEntry(sourceEntry))
zin.copyTo(zout)
} else {
val unpackedDylibFile = tempDir.resolve(sourceEntry.name.substringAfterLast("/"))
try {
unpackedDylibFile.outputStream().buffered().use {
zin.copyTo(it)
}
signDylib(unpackedDylibFile)
val targetEntry = ZipEntry(sourceEntry.name).apply {
comment = sourceEntry.comment
extra = sourceEntry.extra
method = sourceEntry.method
size = unpackedDylibFile.length()
}
zout.putNextEntry(targetEntry)
unpackedDylibFile.inputStream().buffered().use {
it.copyTo(zout)
}
} finally {
unpackedDylibFile.delete()
}
}
zout.closeEntry()
}
}
private fun signDylib(dylibFile: File) {
val args = arrayListOf(
"-vvvv",
"--timestamp",
"--options", "runtime",
"--force",
"--prefix", signing.prefix,
"--sign", signKey
)
signing.keychain?.let {
args.add("--keychain")
args.add(it.absolutePath)
}
args.add(dylibFile.absolutePath)
execOperations.exec { exec ->
exec.executable = MacUtils.codesign.absolutePath
exec.args(*args.toTypedArray())
}.assertNormalExitValue()
}
}