From 6dbc4ff30385f7ddc452cfa907cd8c89b2ff5d9c Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Thu, 26 Jan 2017 11:47:53 +0300 Subject: [PATCH] Introduce JvmBuildMetaInfo to hold last build settings --- .../kotlin/build/JvmBuildMetaInfo.kt | 78 +++++++++++++++++++ .../kotlin/build/serializationUtils.kt | 77 ++++++++++++++++++ .../kotlin/build/JvmBuildMetaInfoTest.kt | 76 ++++++++++++++++++ .../kotlin/build/SerializationUtilsTest.kt | 55 +++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 build-common/src/org/jetbrains/kotlin/build/JvmBuildMetaInfo.kt create mode 100644 build-common/src/org/jetbrains/kotlin/build/serializationUtils.kt create mode 100644 build-common/test/org/jetbrains/kotlin/build/JvmBuildMetaInfoTest.kt create mode 100644 build-common/test/org/jetbrains/kotlin/build/SerializationUtilsTest.kt diff --git a/build-common/src/org/jetbrains/kotlin/build/JvmBuildMetaInfo.kt b/build-common/src/org/jetbrains/kotlin/build/JvmBuildMetaInfo.kt new file mode 100644 index 00000000000..9cc42a6db32 --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/build/JvmBuildMetaInfo.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.build + +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion +import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver +import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion +import org.jetbrains.kotlin.build.deserializeFromPlainText +import org.jetbrains.kotlin.build.serializeToPlainText + +/** + * If you want to add a new field, check its type is supported by [serializeToPlainText], [deserializeFromPlainText] + */ +data class JvmBuildMetaInfo( + val isEAP: Boolean, + val compilerBuildVersion: String, + val languageVersionString: String, + val apiVersionString: String, + val coroutinesEnable: Boolean, + val coroutinesWarn: Boolean, + val coroutinesError: Boolean, + val multiplatformEnable: Boolean, + val metadataVersionMajor: Int, + val metadataVersionMinor: Int, + val metadataVersionPatch: Int, + val bytecodeVersionMajor: Int, + val bytecodeVersionMinor: Int, + val bytecodeVersionPatch: Int, + val ownVersion: Int = JvmBuildMetaInfo.OWN_VERSION, + val coroutinesVersion: Int = JvmBuildMetaInfo.COROUTINES_VERSION, + val multiplatformVersion: Int = JvmBuildMetaInfo.MULTIPLATFORM_VERSION +) { + companion object { + const val OWN_VERSION: Int = 0 + const val COROUTINES_VERSION: Int = 0 + const val MULTIPLATFORM_VERSION: Int = 0 + + fun serializeToString(info: JvmBuildMetaInfo): String = + serializeToPlainText(info) + + fun deserializeFromString(str: String): JvmBuildMetaInfo? = + deserializeFromPlainText(str) + } +} + +fun JvmBuildMetaInfo(args: CommonCompilerArguments): JvmBuildMetaInfo = + JvmBuildMetaInfo(isEAP = DeserializedDescriptorResolver.IS_PRE_RELEASE, + compilerBuildVersion = KotlinCompilerVersion.VERSION, + languageVersionString = args.languageVersion ?: LanguageVersion.LATEST.versionString, + apiVersionString = args.apiVersion ?: ApiVersion.LATEST.versionString, + coroutinesEnable = args.coroutinesEnable, + coroutinesWarn = args.coroutinesWarn, + coroutinesError = args.coroutinesError, + multiplatformEnable = args.multiPlatform, + metadataVersionMajor = JvmMetadataVersion.INSTANCE.major, + metadataVersionMinor = JvmMetadataVersion.INSTANCE.minor, + metadataVersionPatch = JvmMetadataVersion.INSTANCE.patch, + bytecodeVersionMajor = JvmBytecodeBinaryVersion.INSTANCE.major, + bytecodeVersionMinor = JvmBytecodeBinaryVersion.INSTANCE.minor, + bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch) \ No newline at end of file diff --git a/build-common/src/org/jetbrains/kotlin/build/serializationUtils.kt b/build-common/src/org/jetbrains/kotlin/build/serializationUtils.kt new file mode 100644 index 00000000000..de65f211c99 --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/build/serializationUtils.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.build + +import kotlin.reflect.KClass +import kotlin.reflect.KParameter +import kotlin.reflect.full.createType +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor + +internal inline fun serializeToPlainText(instance: T): String { + val lines = ArrayList() + for (property in T::class.memberProperties) { + val value = property.get(instance) + if (value != null) { + lines.add("${property.name}=$value") + } + } + return lines.joinToString("\n") +} + +internal inline fun deserializeFromPlainText(str: String): T? { + val args = ArrayList() + val properties = str + .split("\n") + .filter(String::isNotBlank) + .associate { it.substringBefore("=") to it.substringAfter("=") } + + val primaryConstructor = T::class.primaryConstructor + ?: throw IllegalStateException("Class ${T::class.java} does not have primary constructor") + val params = primaryConstructor.parameters + val sortedBy = params.sortedBy { it.index } + for (param in sortedBy) { + val argumentString = properties[param.name] + + if (argumentString == null) { + if (param.type.isMarkedNullable) { + args.add(null) + continue + } + else { + return null + } + } + + val argument: Any? = when { + param.isTypeOrNullableType(Int::class) -> argumentString.toInt() + param.isTypeOrNullableType(Boolean::class) -> argumentString.toBoolean() + param.isTypeOrNullableType(String::class) -> argumentString + else -> throw IllegalStateException("Unexpected property type: ${param.type}") + } + + args.add(argument) + } + + return primaryConstructor.call(*args.toTypedArray()) +} + +@PublishedApi +internal fun KParameter.isTypeOrNullableType(klass: KClass): Boolean = + this.type == klass.createType(nullable = true) || this.type == klass.createType(nullable = false) + + diff --git a/build-common/test/org/jetbrains/kotlin/build/JvmBuildMetaInfoTest.kt b/build-common/test/org/jetbrains/kotlin/build/JvmBuildMetaInfoTest.kt new file mode 100644 index 00000000000..19af4183acc --- /dev/null +++ b/build-common/test/org/jetbrains/kotlin/build/JvmBuildMetaInfoTest.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.build + +import junit.framework.TestCase +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.junit.Assert.assertNotEquals +import org.junit.Test + +class JvmBuildMetaInfoTest : TestCase() { + @Test + fun testSerialization() { + val args = K2JVMCompilerArguments() + val info = JvmBuildMetaInfo(args) + val actual = JvmBuildMetaInfo.serializeToString(info) + val expectedTempalte = +"""apiVersionString=1.1 +bytecodeVersionMajor=1 +bytecodeVersionMinor=0 +bytecodeVersionPatch=1 +compilerBuildVersion=@snapshot@ +coroutinesEnable=false +coroutinesError=false +coroutinesVersion=0 +coroutinesWarn=false +isEAP=@isEAP@ +languageVersionString=1.1 +metadataVersionMajor=1 +metadataVersionMinor=1 +metadataVersionPatch=3 +multiplatformEnable=false +multiplatformVersion=0 +ownVersion=0""" + val expected = expectedTempalte.replace("@snapshot@", KotlinCompilerVersion.VERSION) + .replace("@isEAP@", KotlinCompilerVersion.IS_PRE_RELEASE.toString()) + assertEquals(expected, actual) + } + + @Test + fun testSerializationDeserialization() { + val args = K2JVMCompilerArguments() + val info = JvmBuildMetaInfo(args) + val serialized = JvmBuildMetaInfo.serializeToString(info) + val deserialized = JvmBuildMetaInfo.deserializeFromString(serialized) + assertEquals(info, deserialized) + } + + @Test + fun testEquals() { + val args1 = K2JVMCompilerArguments() + args1.coroutinesEnable = true + val info1 = JvmBuildMetaInfo(args1) + + val args2 = K2JVMCompilerArguments() + args2.coroutinesEnable = false + val info2 = JvmBuildMetaInfo(args2) + + assertNotEquals(info1, info2) + assertEquals(info1, info2.copy(coroutinesEnable = true)) + } +} \ No newline at end of file diff --git a/build-common/test/org/jetbrains/kotlin/build/SerializationUtilsTest.kt b/build-common/test/org/jetbrains/kotlin/build/SerializationUtilsTest.kt new file mode 100644 index 00000000000..6b083f064e0 --- /dev/null +++ b/build-common/test/org/jetbrains/kotlin/build/SerializationUtilsTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.build + +import junit.framework.TestCase +import org.junit.Test + +class SerializationUtilsTest : TestCase() { + data class TestPropertyTypes( + val intNull: Int?, + val int: Int, + val stringNull: String?, + val string: String, + val boolNull: Boolean?, + val bool: Boolean + ) + + @Test + fun testPropertyTypes() { + val instance1 = TestPropertyTypes(null, 1, null, "abc", null, false) + val deserialized1 = deserializeFromPlainText(serializeToPlainText(instance1)) + assertEquals(instance1, deserialized1) + + val instance2 = TestPropertyTypes(1, 2, "abc", "xyz", true, false) + val deserialized2 = deserializeFromPlainText(serializeToPlainText(instance2)) + assertEquals(instance2, deserialized2) + } + + data class TestAddedField1(val x: Int) + data class TestAddedField2(val x: Int, val y: Int?) + + @Test + fun testAddedField() { + val testAddedField1 = TestAddedField1(1) + val serialized = serializeToPlainText(testAddedField1) + val deserialized = deserializeFromPlainText(serialized) + + assertEquals(TestAddedField2(1, null), deserialized) + } +} +