Alexey Tsvetkov
4 years ago
committed by
GitHub
12 changed files with 303 additions and 20 deletions
@ -0,0 +1,39 @@ |
|||||||
|
package org.jetbrains.compose.desktop.application.internal |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import org.gradle.api.provider.Provider |
||||||
|
import org.jetbrains.compose.desktop.application.dsl.Application |
||||||
|
import org.jetbrains.compose.desktop.application.dsl.NativeDistributions |
||||||
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat |
||||||
|
|
||||||
|
internal fun packageVersionFor( |
||||||
|
project: Project, |
||||||
|
app: Application, |
||||||
|
targetFormat: TargetFormat |
||||||
|
): Provider<String?> = |
||||||
|
project.provider { |
||||||
|
app.nativeDistributions.packageVersionFor(targetFormat) |
||||||
|
?: project.version.toString().takeIf { it != "unspecified" } |
||||||
|
} |
||||||
|
|
||||||
|
private fun NativeDistributions.packageVersionFor( |
||||||
|
targetFormat: TargetFormat |
||||||
|
): String? { |
||||||
|
val formatSpecificVersion: String? = when (targetFormat) { |
||||||
|
TargetFormat.AppImage -> null |
||||||
|
TargetFormat.Deb -> linux.debPackageVersion |
||||||
|
TargetFormat.Rpm -> linux.rpmPackageVersion |
||||||
|
TargetFormat.Dmg -> macOS.dmgPackageVersion |
||||||
|
TargetFormat.Pkg -> macOS.pkgPackageVersion |
||||||
|
TargetFormat.Exe -> windows.exePackageVersion |
||||||
|
TargetFormat.Msi -> windows.msiPackageVersion |
||||||
|
} |
||||||
|
val osSpecificVersion: String? = when (targetFormat.targetOS) { |
||||||
|
OS.Linux -> linux.packageVersion |
||||||
|
OS.MacOS -> macOS.packageVersion |
||||||
|
OS.Windows -> windows.packageVersion |
||||||
|
} |
||||||
|
return formatSpecificVersion |
||||||
|
?: osSpecificVersion |
||||||
|
?: packageVersion |
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
package org.jetbrains.compose.desktop.application.internal.validation |
||||||
|
|
||||||
|
import org.gradle.api.GradleException |
||||||
|
import org.gradle.api.Project |
||||||
|
import org.jetbrains.compose.desktop.application.dsl.Application |
||||||
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat |
||||||
|
import org.jetbrains.compose.desktop.application.internal.OS |
||||||
|
import org.jetbrains.compose.desktop.application.internal.packageVersionFor |
||||||
|
|
||||||
|
internal fun Project.validatePackageVersions(app: Application) { |
||||||
|
val errors = ErrorsCollector() |
||||||
|
|
||||||
|
for (targetFormat in app.nativeDistributions.targetFormats) { |
||||||
|
val packageVersion = packageVersionFor(project, app, targetFormat).orNull |
||||||
|
if (packageVersion == null) { |
||||||
|
errors.addError(targetFormat, "no version was specified") |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
val versionChecker: VersionChecker? = when (targetFormat) { |
||||||
|
TargetFormat.AppImage -> null |
||||||
|
TargetFormat.Deb -> DebVersionChecker |
||||||
|
TargetFormat.Rpm -> RpmVersionChecker |
||||||
|
TargetFormat.Msi, TargetFormat.Exe -> WindowsVersionChecker |
||||||
|
TargetFormat.Dmg, TargetFormat.Pkg -> MacVersionChecker |
||||||
|
} |
||||||
|
|
||||||
|
versionChecker?.apply { |
||||||
|
if (!isValid(packageVersion)) { |
||||||
|
errors.addError( |
||||||
|
targetFormat, |
||||||
|
"'$packageVersion' is not a valid version", |
||||||
|
correctFormat = correctFormat |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (errors.errors.isNotEmpty()) { |
||||||
|
throw GradleException(errors.errors.joinToString("\n")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class ErrorsCollector { |
||||||
|
private val myErrors = arrayListOf<String>() |
||||||
|
|
||||||
|
val errors: List<String> |
||||||
|
get() = myErrors |
||||||
|
|
||||||
|
fun addError( |
||||||
|
targetFormat: TargetFormat, |
||||||
|
error: String, |
||||||
|
correctFormat: String? = null |
||||||
|
) { |
||||||
|
val msg = buildString { |
||||||
|
appendln("* Illegal version for '$targetFormat': $error.") |
||||||
|
if (correctFormat != null) { |
||||||
|
appendln(" * Correct format: $correctFormat") |
||||||
|
} |
||||||
|
appendln(" * You can specify the correct version using DSL properties: " + |
||||||
|
dslPropertiesFor(targetFormat).joinToString(", ") |
||||||
|
) |
||||||
|
} |
||||||
|
myErrors.add(msg) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun dslPropertiesFor( |
||||||
|
targetFormat: TargetFormat |
||||||
|
): List<String> { |
||||||
|
val nativeDistributions = "nativeDistributions" |
||||||
|
val linux = "$nativeDistributions.linux" |
||||||
|
val macOS = "$nativeDistributions.macOS" |
||||||
|
val windows = "$nativeDistributions.windows" |
||||||
|
val packageVersion = "packageVersion" |
||||||
|
|
||||||
|
val formatSpecificProperty: String? = when (targetFormat) { |
||||||
|
TargetFormat.AppImage -> null |
||||||
|
TargetFormat.Deb -> "$linux.debPackageVersion" |
||||||
|
TargetFormat.Rpm -> "$linux.rpmPackageVersion" |
||||||
|
TargetFormat.Dmg -> "$macOS.dmgPackageVersion" |
||||||
|
TargetFormat.Pkg -> "$macOS.pkgPackageVersion" |
||||||
|
TargetFormat.Exe -> "$windows.exePackageVersion" |
||||||
|
TargetFormat.Msi -> "$windows.msiPackageVersion" |
||||||
|
} |
||||||
|
val osSettingsProperty: String = when (targetFormat.targetOS) { |
||||||
|
OS.Linux -> "$linux.$packageVersion" |
||||||
|
OS.MacOS -> "$macOS.$packageVersion" |
||||||
|
OS.Windows -> "$windows.$packageVersion" |
||||||
|
} |
||||||
|
val appSpecificProperty = "$nativeDistributions.$packageVersion" |
||||||
|
return listOfNotNull( |
||||||
|
formatSpecificProperty, |
||||||
|
osSettingsProperty, |
||||||
|
appSpecificProperty, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
private interface VersionChecker { |
||||||
|
val correctFormat: String |
||||||
|
fun isValid(version: String): Boolean |
||||||
|
} |
||||||
|
|
||||||
|
private object DebVersionChecker : VersionChecker { |
||||||
|
override val correctFormat = """|'[EPOCH:]UPSTREAM_VERSION[-DEBIAN_REVISION]', where: |
||||||
|
| * EPOCH is an optional non-negative integer; |
||||||
|
| * UPSTREAM_VERSION may contain only alphanumerics and the characters '.', '+', '-', '~' and must start with a digit; |
||||||
|
| * DEBIAN_REVISION is optional and may contain only alphanumerics and the characters '.', '+', '~'; |
||||||
|
| * see https://www.debian.org/doc/debian-policy/ch-controlfields.html#version for details; |
||||||
|
""".trimMargin() |
||||||
|
|
||||||
|
override fun isValid(version: String): Boolean = |
||||||
|
version.matches(debRegex) |
||||||
|
|
||||||
|
private val debRegex = ( |
||||||
|
/* EPOCH */"([0-9]+:)?" + |
||||||
|
/* UPSTREAM_VERSION */ "[0-9][0-9a-zA-Z.+\\-~]*" + |
||||||
|
/* DEBIAN_REVISION */ "(-[0-9a-zA-Z.+~]+)?").toRegex() |
||||||
|
} |
||||||
|
|
||||||
|
private object RpmVersionChecker : VersionChecker { |
||||||
|
override val correctFormat = "rpm package version must not contain a dash '-'" |
||||||
|
|
||||||
|
override fun isValid(version: String): Boolean = |
||||||
|
!version.contains("-") |
||||||
|
} |
||||||
|
|
||||||
|
private object WindowsVersionChecker : VersionChecker { |
||||||
|
override val correctFormat = """|'MAJOR.MINOR.BUILD', where: |
||||||
|
| * MAJOR is a non-negative integer with a maximum value of 255; |
||||||
|
| * MINOR is a non-negative integer with a maximum value of 255; |
||||||
|
| * BUILD is a non-negative integer with a maximum value of 65535; |
||||||
|
""".trimMargin() |
||||||
|
|
||||||
|
override fun isValid(version: String): Boolean { |
||||||
|
val parts = version.split(".").map { it.toIntOrNull() } |
||||||
|
if (parts.size != 3) return false |
||||||
|
|
||||||
|
return parts[0].isIntInRange(0, 255) |
||||||
|
&& parts[1].isIntInRange(0, 255) |
||||||
|
&& parts[2].isIntInRange(0, 65535) |
||||||
|
} |
||||||
|
|
||||||
|
private fun Int?.isIntInRange(min: Int, max: Int) = |
||||||
|
this != null && this >= min && this <= max |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private object MacVersionChecker : VersionChecker { |
||||||
|
override val correctFormat = """|'MAJOR[.MINOR][.PATCH]', where: |
||||||
|
| * MAJOR is an integer > 0; |
||||||
|
| * MINOR is an optional non-negative integer; |
||||||
|
| * PATCH is an optional non-negative integer; |
||||||
|
""".trimMargin() |
||||||
|
|
||||||
|
override fun isValid(version: String): Boolean { |
||||||
|
val parts = version.split(".").map { it.toIntOrNull() } |
||||||
|
|
||||||
|
return parts.isNotEmpty() |
||||||
|
&& parts.size <= 3 |
||||||
|
&& parts.all { it != null && it >= 0 } |
||||||
|
&& (parts.first() ?: 0) > 0 |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue