From 14eca72913a05969c81eadb17c6e2690c76a90e3 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Fri, 21 May 2021 12:03:44 +0200 Subject: [PATCH] Implement 'CommonizerTarget.fileName' and limit fileName length ^KT-46856 Fixed --- ...rchicalNativeDistributionCommonizerTask.kt | 20 ++---- .../commonizer/TargetLibrariesLayout.kt | 24 ++++++- .../CommonizerTargetFileNameTest.kt | 67 +++++++++++++++++++ 3 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 native/commonizer-api/test/org/jetbrains/kotlin/commonizer/CommonizerTargetFileNameTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/HierarchicalNativeDistributionCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/HierarchicalNativeDistributionCommonizerTask.kt index 784fc80d171..d1b9c5170ef 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/HierarchicalNativeDistributionCommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/HierarchicalNativeDistributionCommonizerTask.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.native.internal import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.tasks.* -import org.jetbrains.kotlin.commonizer.CommonizerLogLevel +import org.jetbrains.kotlin.commonizer.HierarchicalCommonizerOutputLayout.fileName import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget import org.jetbrains.kotlin.commonizer.identityString import org.jetbrains.kotlin.commonizer.isAncestorOf @@ -21,8 +21,7 @@ import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR import java.io.File -import java.nio.charset.StandardCharsets -import java.util.* +import java.net.URLEncoder internal open class HierarchicalNativeDistributionCommonizerTask : DefaultTask() { @@ -78,16 +77,11 @@ internal open class HierarchicalNativeDistributionCommonizerTask : DefaultTask() internal fun getRootOutputDirectory(target: SharedCommonizerTarget): File { val kotlinVersion = project.getKotlinPluginVersion() - val discriminator = buildString { - append(target.identityString) - append("-") - append(kotlinVersion.toLowerCase().base64) - } - return project.file(konanHome) .resolve(KONAN_DISTRIBUTION_KLIB_DIR) .resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR) - .resolve(discriminator) + .resolve(urlEncode(kotlinVersion)) + .resolve(target.fileName) } @TaskAction @@ -121,8 +115,4 @@ private fun Project.getRootCommonizerTargets(): Set { return allTargets.filter { target -> allTargets.none { otherTarget -> otherTarget isAncestorOf target } }.toSet() } -private val String.base64 - get() = base64Encoder.encodeToString(toByteArray(StandardCharsets.UTF_8)) - -private val base64Encoder = Base64.getEncoder().withoutPadding() - +private fun urlEncode(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name()) diff --git a/native/commonizer-api/src/org/jetbrains/kotlin/commonizer/TargetLibrariesLayout.kt b/native/commonizer-api/src/org/jetbrains/kotlin/commonizer/TargetLibrariesLayout.kt index 1e8a9c1efa0..d3c9110feb9 100644 --- a/native/commonizer-api/src/org/jetbrains/kotlin/commonizer/TargetLibrariesLayout.kt +++ b/native/commonizer-api/src/org/jetbrains/kotlin/commonizer/TargetLibrariesLayout.kt @@ -8,6 +8,8 @@ package org.jetbrains.kotlin.commonizer import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR import java.io.File +import java.security.MessageDigest +import java.util.* public fun interface CommonizerOutputLayout { @@ -24,7 +26,27 @@ public object NativeDistributionCommonizerOutputLayout : CommonizerOutputLayout } public object HierarchicalCommonizerOutputLayout : CommonizerOutputLayout { + internal const val maxFileNameLength = 150 + override fun getTargetDirectory(root: File, target: CommonizerTarget): File { - return root.resolve(target.identityString) + return root.resolve(target.fileName) } + + public val CommonizerTarget.fileName: String + get() { + val identityString = identityString + return if (identityString.length <= maxFileNameLength) identityString + else { + val hashSuffix = "[--$identityStringHash]" + return identityString.take(maxFileNameLength - hashSuffix.length) + hashSuffix + } + } + + private val CommonizerTarget.identityStringHash: String + get() { + val sha = MessageDigest.getInstance("SHA-1") + val base64 = Base64.getUrlEncoder() + return base64.encode(sha.digest(identityString.encodeToByteArray())).decodeToString() + } } + diff --git a/native/commonizer-api/test/org/jetbrains/kotlin/commonizer/CommonizerTargetFileNameTest.kt b/native/commonizer-api/test/org/jetbrains/kotlin/commonizer/CommonizerTargetFileNameTest.kt new file mode 100644 index 00000000000..c7b052006fd --- /dev/null +++ b/native/commonizer-api/test/org/jetbrains/kotlin/commonizer/CommonizerTargetFileNameTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer + +import org.jetbrains.kotlin.commonizer.HierarchicalCommonizerOutputLayout.fileName +import org.jetbrains.kotlin.commonizer.HierarchicalCommonizerOutputLayout.maxFileNameLength +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class CommonizerTargetFileNameTest { + + private val longCommonizerTarget = parseCommonizerTarget( + buildString { + append("(") + append( + sequence { + var i = 0 + while (true) { + yield(i.toString()) + i++ + } + }.take(maxFileNameLength).joinToString(", ") + ) + append(")") + } + ) + + @get:Rule + val temporaryFolder = TemporaryFolder() + + @Test + fun `small targets will use identityString`() { + val target = parseCommonizerTarget("((a, b), c)") + assertEquals(target.identityString, target.fileName) + } + + @OptIn(ExperimentalStdlibApi::class) + @Test + fun `longCommonizerTarget respect maximum fileName length`() { + assertTrue( + longCommonizerTarget.identityString.length > maxFileNameLength, + "Expected test target's identityString to exceed maxFileNameLength" + ) + + assertEquals( + longCommonizerTarget.fileName.length, maxFileNameLength, + "Expected test target's fileName to be exactly match the maximum" + ) + } + + @Test + fun `longCommonizerTarget fileName can create new file`() { + val longCommonizerTargetFile = temporaryFolder.root.resolve(longCommonizerTarget.fileName) + assertTrue(longCommonizerTargetFile.createNewFile(), "Expected being able to create file $longCommonizerTargetFile") + longCommonizerTargetFile.writeText(longCommonizerTarget.identityString) + assertEquals( + longCommonizerTarget, parseCommonizerTarget(longCommonizerTargetFile.readText()), + "Expected being able to read and write to $longCommonizerTargetFile" + ) + } +}