[KLIB] ABI reader: Refactor manifest reading test
^KT-54402
This commit is contained in:
committed by
Space Team
parent
ac3c000ab1
commit
a95f963ad3
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.library.abi
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil.createTempDirectory
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
|
||||
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter
|
||||
import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.konan.file.File as KFile
|
||||
|
||||
@OptIn(ExperimentalLibraryAbiReader::class)
|
||||
class ManifestReadingTest {
|
||||
private lateinit var buildDir: File
|
||||
|
||||
@BeforeEach
|
||||
fun setUp(testInfo: TestInfo) {
|
||||
buildDir = createTempDirectory(testInfo.testClass.get().simpleName, testInfo.testMethod.get().name)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
buildDir.deleteRecursively()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testManifestReading() {
|
||||
val testData = mapOf(
|
||||
"sample-library-1" to LibraryManifest(
|
||||
platform = BuiltInsPlatform.JS.name,
|
||||
nativeTargets = emptyList(),
|
||||
compilerVersion = "1.23.45",
|
||||
abiVersion = "2.34.56",
|
||||
libraryVersion = "3.45.67",
|
||||
irProviderName = "test_ir_provider_123"
|
||||
),
|
||||
"sample-library-2" to LibraryManifest(
|
||||
platform = BuiltInsPlatform.NATIVE.name,
|
||||
nativeTargets = listOf("ios_arm64", "ios_simulator_arm64", "macos_arm64", "macos_x64"),
|
||||
compilerVersion = null,
|
||||
abiVersion = null,
|
||||
libraryVersion = null,
|
||||
irProviderName = null
|
||||
),
|
||||
)
|
||||
|
||||
testData.forEach { (libraryName, originalManifest) ->
|
||||
val libraryFile = createEmptyLibraryWithSpecificManifest(libraryName, originalManifest)
|
||||
val readManifest = LibraryAbiReader.readAbiInfo(libraryFile).manifest
|
||||
|
||||
assertTrue(originalManifest !== readManifest) { "Library name: $libraryName" }
|
||||
assertEquals(originalManifest, readManifest) { "Library name: $libraryName" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEmptyLibraryWithSpecificManifest(libraryName: String, libraryManifest: LibraryManifest): File {
|
||||
val libraryVersioning = KotlinLibraryVersioning(
|
||||
libraryVersion = libraryManifest.libraryVersion,
|
||||
compilerVersion = libraryManifest.compilerVersion,
|
||||
abiVersion = libraryManifest.abiVersion?.parseKotlinAbiVersion(),
|
||||
metadataVersion = null
|
||||
)
|
||||
val builtInsPlatform = libraryManifest.platform?.let(BuiltInsPlatform::parseFromString)
|
||||
?: error("Unknown platform: ${libraryManifest.platform}")
|
||||
|
||||
val libraryFile = buildDir.resolve("$libraryName.klib")
|
||||
val libraryKFile = KFile(libraryFile.absolutePath)
|
||||
|
||||
val libraryLayout = KotlinLibraryLayoutForWriter(libraryKFile, libraryKFile)
|
||||
val library = KotlinLibraryWriterImpl(
|
||||
moduleName = libraryName,
|
||||
versions = libraryVersioning,
|
||||
builtInsPlatform = builtInsPlatform,
|
||||
nativeTargets = libraryManifest.nativeTargets,
|
||||
nopack = true,
|
||||
shortName = libraryName,
|
||||
layout = libraryLayout
|
||||
)
|
||||
libraryManifest.irProviderName?.let { irProviderName ->
|
||||
library.addManifestAddend(
|
||||
Properties().apply {
|
||||
this[KLIB_PROPERTY_IR_PROVIDER] = irProviderName
|
||||
}
|
||||
)
|
||||
}
|
||||
library.addIr(SerializedIrModule(files = emptyList())) // Empty library.
|
||||
library.commit()
|
||||
|
||||
return libraryFile
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.library.abi
|
||||
|
||||
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
import java.io.File
|
||||
|
||||
@OptIn(ExperimentalLibraryAbiReader::class)
|
||||
class ManifestRenderingTest {
|
||||
private lateinit var buildDir: File
|
||||
private lateinit var sourceFile: File
|
||||
|
||||
@BeforeEach
|
||||
fun setUp(testInfo: TestInfo) {
|
||||
buildDir = setUpBuildDir(testInfo)
|
||||
sourceFile = buildDir.resolve("source.kt").apply { createNewFile() } // Just an empty file.
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renderWithManifest() {
|
||||
val customManifest = LibraryManifest(
|
||||
platform = BuiltInsPlatform.JS.name,
|
||||
nativeTargets = listOf("ios_arm64", "ios_simulator_arm64", "macos_arm64", "macos_x64"),
|
||||
compilerVersion = "1.23.45",
|
||||
abiVersion = "2.34.56",
|
||||
libraryVersion = "3.45.67",
|
||||
irProviderName = "test_ir_provider_123"
|
||||
)
|
||||
|
||||
val libraryFile = buildLibrary(sourceFile, libraryName = "sample-library", buildDir)
|
||||
patchManifest(libraryFile, customManifest)
|
||||
|
||||
val readManifest = LibraryAbiReader.readAbiInfo(libraryFile).manifest
|
||||
assertTrue(customManifest !== readManifest)
|
||||
assertEquals(customManifest, readManifest)
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.library.abi
|
||||
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.SingleRootFileViewProvider
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||
import org.jetbrains.kotlin.cli.js.klib.compileModuleToAnalyzedFirWithPsi
|
||||
import org.jetbrains.kotlin.cli.js.klib.serializeFirKlib
|
||||
import org.jetbrains.kotlin.cli.js.klib.transformFirToIr
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.Companion.createForParallelTests
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
||||
import org.jetbrains.kotlin.ir.backend.js.MainModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.konan.file.ZipFileSystemCacheableAccessor
|
||||
import org.jetbrains.kotlin.konan.file.unzipTo
|
||||
import org.jetbrains.kotlin.konan.file.zipDirAs
|
||||
import org.jetbrains.kotlin.konan.properties.saveToFile
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.utils.TestDisposable
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.TestInfo
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.nio.charset.Charset
|
||||
import org.jetbrains.kotlin.konan.file.File as KFile
|
||||
|
||||
private val TestInfo.className: String get() = testClass.orElseGet { fail("Can't get test class name") }.simpleName
|
||||
private val TestInfo.methodName: String get() = testMethod.orElseGet { fail("Can't get test method name") }.name
|
||||
|
||||
internal fun setUpBuildDir(testInfo: TestInfo): File {
|
||||
val projectBuildDir = System.getenv(ENV_VAR_PROJECT_BUILD_DIR)
|
||||
?.let { File(it) }
|
||||
?: fail<Nothing>("$ENV_VAR_PROJECT_BUILD_DIR environment variable not specified")
|
||||
|
||||
assertTrue(projectBuildDir.isDirectory) { "Project build dir does not exist: $projectBuildDir" }
|
||||
|
||||
return projectBuildDir.resolve("t").resolve("${testInfo.className}.${testInfo.methodName}").apply {
|
||||
deleteRecursively()
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun buildLibrary(sourceFile: File, libraryName: String, buildDir: File): File {
|
||||
val configuration = CompilerConfiguration()
|
||||
val environment = createForParallelTests(TestDisposable(), configuration, EnvironmentConfigFiles.JS_CONFIG_FILES)
|
||||
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, libraryName)
|
||||
configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
|
||||
configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
|
||||
configuration.put(JSConfigurationKeys.ZIP_FILE_SYSTEM_ACCESSOR, ZipFileSystemCacheableAccessor(2))
|
||||
|
||||
val psiManager = PsiManager.getInstance(environment.project)
|
||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
|
||||
|
||||
val virtualFile = fileSystem.findFileByIoFile(sourceFile) ?: error("VirtualFile for $sourceFile not found")
|
||||
val ktFiles = SingleRootFileViewProvider(psiManager, virtualFile).allFiles.filterIsInstance<KtFile>()
|
||||
|
||||
val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter()
|
||||
|
||||
val jsStdlib = File("libraries/stdlib/js-ir/build/classes/kotlin/js/main").absoluteFile
|
||||
val dependencies = listOf(jsStdlib.absolutePath)
|
||||
|
||||
val moduleStructure = ModulesStructure(
|
||||
project = environment.project,
|
||||
mainModule = MainModule.SourceFiles(ktFiles),
|
||||
compilerConfiguration = configuration,
|
||||
dependencies = dependencies,
|
||||
friendDependenciesPaths = emptyList()
|
||||
)
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true)
|
||||
|
||||
val analyzedOutput = compileModuleToAnalyzedFirWithPsi(
|
||||
moduleStructure = moduleStructure,
|
||||
ktFiles = ktFiles,
|
||||
libraries = dependencies,
|
||||
friendLibraries = emptyList(),
|
||||
diagnosticsReporter = diagnosticsReporter,
|
||||
incrementalDataProvider = null,
|
||||
lookupTracker = null,
|
||||
useWasmPlatform = false
|
||||
)
|
||||
|
||||
val fir2IrActualizedResult = transformFirToIr(moduleStructure, analyzedOutput.output, diagnosticsReporter)
|
||||
|
||||
if (analyzedOutput.reportCompilationErrors(moduleStructure, diagnosticsReporter, messageCollector)) {
|
||||
val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
|
||||
fail<Nothing>("The following errors occurred compiling test:\n$messages")
|
||||
}
|
||||
|
||||
val libraryDir = KFile(buildDir.resolve(libraryName).absolutePath)
|
||||
val libraryFile = KFile(libraryDir.absolutePath + ".klib")
|
||||
|
||||
serializeFirKlib(
|
||||
moduleStructure = moduleStructure,
|
||||
firOutputs = analyzedOutput.output,
|
||||
fir2IrActualizedResult = fir2IrActualizedResult,
|
||||
outputKlibPath = libraryDir.absolutePath,
|
||||
messageCollector = messageCollector,
|
||||
diagnosticsReporter = diagnosticsReporter,
|
||||
jsOutputName = libraryName,
|
||||
useWasmPlatform = false
|
||||
)
|
||||
|
||||
if (messageCollector.hasErrors()) {
|
||||
val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
|
||||
fail<Nothing>("The following errors occurred serializing test klib:\n$messages")
|
||||
}
|
||||
|
||||
libraryDir.zipDirAs(libraryFile)
|
||||
libraryDir.deleteRecursively()
|
||||
|
||||
return File(libraryFile.absolutePath)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLibraryAbiReader::class)
|
||||
internal fun patchManifest(libraryFile: File, customManifest: LibraryManifest) {
|
||||
val libraryKFile = KFile(libraryFile.absolutePath)
|
||||
val manifestProperties = resolveSingleFileKlib(libraryKFile).manifestProperties
|
||||
|
||||
fun set(name: String, value: String?) {
|
||||
assertTrue(!value.isNullOrBlank()) { "$name has invalid value: [$value]" }
|
||||
manifestProperties[name] = value!!
|
||||
}
|
||||
|
||||
set(KLIB_PROPERTY_BUILTINS_PLATFORM, customManifest.platform)
|
||||
set(KLIB_PROPERTY_NATIVE_TARGETS, customManifest.nativeTargets.joinToString(" "))
|
||||
set(KLIB_PROPERTY_COMPILER_VERSION, customManifest.compilerVersion)
|
||||
set(KLIB_PROPERTY_ABI_VERSION, customManifest.abiVersion)
|
||||
set(KLIB_PROPERTY_LIBRARY_VERSION, customManifest.libraryVersion)
|
||||
set(KLIB_PROPERTY_IR_PROVIDER, customManifest.irProviderName)
|
||||
|
||||
assertEquals(libraryFile.extension, "klib")
|
||||
val libraryDir = libraryFile.resolveSibling(libraryFile.nameWithoutExtension)
|
||||
val libraryKDir = KFile(libraryDir.absolutePath)
|
||||
libraryKFile.unzipTo(libraryKDir)
|
||||
libraryKFile.deleteRecursively()
|
||||
|
||||
libraryDir.walkTopDown()
|
||||
.filter { it.isFile && it.name == "manifest" }
|
||||
.forEach { manifestProperties.saveToFile(KFile(it.absolutePath)) }
|
||||
|
||||
libraryKDir.zipDirAs(libraryKFile)
|
||||
}
|
||||
|
||||
private const val ENV_VAR_PROJECT_BUILD_DIR = "PROJECT_BUILD_DIR"
|
||||
Reference in New Issue
Block a user