[K/N] Stand-alone Native Application wizard in IDEA

Issue #KT-27076 Fixed
This commit is contained in:
Dmitriy Dolovov
2018-09-25 17:24:59 +07:00
committed by GitHub
parent 8e455f5a14
commit 5e33860652
15 changed files with 158 additions and 81 deletions
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.ide.konan.gradle
import org.jetbrains.kotlin.idea.configuration.KotlinGradleAbstractMultiplatformModuleBuilder
import org.jetbrains.kotlin.konan.target.presetName
class KotlinGradleNativeMultiplatformModuleBuilder : KotlinGradleAbstractMultiplatformModuleBuilder() {
override fun getBuilderId() = "kotlin.gradle.multiplatform.native"
override fun getPresentableName() = "Kotlin/Native"
override fun getDescription() = "Kotlin module for native binaries"
override val notImportedCommonSourceSets = true
override fun buildMultiPlatformPart(): String {
return """
kotlin {
targets {
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
fromPreset(presets.${defaultNativeTarget.presetName}, '${defaultNativeTarget.userTargetName}')
}
}
""".trimIndent()
}
}
@@ -132,7 +132,7 @@ abstract class GradleKotlinFrameworkSupportProvider(
val isNewProject = module.project.getUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT) == true
if (isNewProject) {
ProjectCodeStyleImporter.apply(module.project, KotlinStyleGuideCodeStyle.INSTANCE)
GradlePropertiesFileUtils.addCodeStyleProperty(module.project, KotlinStyleGuideCodeStyle.CODE_STYLE_SETTING)
GradlePropertiesFileFacade.forProject(module.project).addCodeStyleProperty(KotlinStyleGuideCodeStyle.CODE_STYLE_SETTING)
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.configuration
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import org.jetbrains.plugins.gradle.model.ExternalProject
import java.io.File
import java.util.*
internal class GradlePropertiesFileFacade(private val baseDir: String) {
fun readProperty(propertyName: String): String? {
val baseVirtualDir = LocalFileSystem.getInstance().findFileByPath(baseDir) ?: return null
for (propertyFileName in GRADLE_PROPERTY_FILES) {
val propertyFile = baseVirtualDir.findChild(propertyFileName) ?: continue
Properties().also { it.load(propertyFile.inputStream) }.getProperty(propertyName)?.let {
return it
}
}
return null
}
fun addCodeStyleProperty(value: String) {
addProperty(KOTLIN_CODE_STYLE_GRADLE_SETTING, value)
}
fun addNotImportedCommonSourceSetsProperty() {
addProperty(KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING, true.toString())
}
private fun addProperty(key: String, value: String) {
val projectPropertiesFile = File(baseDir, GRADLE_PROPERTIES_FILE_NAME)
val keyValue = "$key=$value"
val updatedText = if (projectPropertiesFile.exists()) {
projectPropertiesFile.readText() + System.lineSeparator() + keyValue
} else {
keyValue
}
projectPropertiesFile.writeText(updatedText)
}
companion object {
fun forProject(project: Project) = GradlePropertiesFileFacade(ExternalSystemApiUtil.toCanonicalPath(project.basePath!!))
fun forExternalProject(externalProject: ExternalProject) = GradlePropertiesFileFacade(externalProject.projectDir.canonicalPath)
const val KOTLIN_CODE_STYLE_GRADLE_SETTING = "kotlin.code.style"
const val KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING = "kotlin.import.noCommonSourceSets"
private const val GRADLE_PROPERTIES_FILE_NAME = "gradle.properties"
private const val GRADLE_PROPERTIES_LOCAL_FILE_NAME = "local.properties"
private val GRADLE_PROPERTY_FILES = listOf(GRADLE_PROPERTIES_LOCAL_FILE_NAME, GRADLE_PROPERTIES_FILE_NAME)
}
}
@@ -1,60 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.idea.configuration
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import java.io.File
import java.util.*
internal object GradlePropertiesFileUtils {
const val KOTLIN_CODE_STYLE_GRADLE_SETTING = "kotlin.code.style"
private const val GRADLE_PROPERTIES_FILE_NAME = "gradle.properties"
private const val GRADLE_PROPERTIES_LOCAL_FILE_NAME = "local.properties"
private val gradlePropertyFiles = listOf(GRADLE_PROPERTIES_LOCAL_FILE_NAME, GRADLE_PROPERTIES_FILE_NAME)
fun readProperty(project: Project, propertyName: String): String? {
for (propertyFileName in gradlePropertyFiles) {
val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
val properties = Properties()
properties.load(propertyFile.inputStream)
properties.getProperty(propertyName)?.let {
return it
}
}
return null
}
fun addCodeStyleProperty(project: Project, value: String) {
addProperty(project, KOTLIN_CODE_STYLE_GRADLE_SETTING, value)
}
private fun addProperty(project: Project, key: String, value: String) {
val propertiesFile = projectPropertiesFile(project)
val keyValue = "$key=$value"
val updatedText = if (propertiesFile.exists()) {
propertiesFile.readText() + System.lineSeparator() + keyValue
} else {
keyValue
}
propertiesFile.writeText(updatedText)
}
private fun projectPropertiesFile(project: Project): File {
return File(getBaseDirPath(project), "gradle.properties")
}
private fun getBaseDirPath(project: Project): File {
val basePath = project.basePath!!
return File(ExternalSystemApiUtil.toCanonicalPath(basePath))
}
}
@@ -102,7 +102,7 @@ abstract class KotlinDslGradleKotlinFrameworkSupportProvider(
val isNewProject = module.project.getUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT) == true
if (isNewProject) {
ProjectCodeStyleImporter.apply(module.project, KotlinStyleGuideCodeStyle.INSTANCE)
GradlePropertiesFileUtils.addCodeStyleProperty(module.project, KotlinStyleGuideCodeStyle.CODE_STYLE_SETTING)
GradlePropertiesFileFacade.forProject(module.project).addCodeStyleProperty(KotlinStyleGuideCodeStyle.CODE_STYLE_SETTING)
}
}
@@ -14,6 +14,9 @@ import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.TargetSupportException
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl
@@ -41,6 +44,8 @@ abstract class KotlinGradleAbstractMultiplatformModuleBuilder : GradleModuleBuil
GradleKotlinMPPFrameworkSupportProvider().addSupport(builder, module, sdk = null, specifyPluginVersionIfNeeded = true)
VfsUtil.saveText(buildGradle, builder.buildConfigurationPart() + builder.buildMainPart() + buildMultiPlatformPart())
createProjectSkeleton(module, rootDir)
if (notImportedCommonSourceSets) GradlePropertiesFileFacade.forProject(module.project).addNotImportedCommonSourceSetsProperty()
} finally {
flushSettingsGradleCopy(module)
}
@@ -57,6 +62,23 @@ abstract class KotlinGradleAbstractMultiplatformModuleBuilder : GradleModuleBuil
protected open fun createProjectSkeleton(module: Module, rootDir: VirtualFile) {}
protected open val notImportedCommonSourceSets = false
protected val defaultNativeTarget by lazy {
try {
HostManager.host
} catch (e: TargetSupportException) {
KonanTarget.IOS_X64
}
}
// Examples: ios_x64 -> ios, macos_x64 -> macos, wasm32 -> wasm.
protected val KonanTarget.userTargetName: String
get() {
val index = name.indexOfAny("_0123456789".toCharArray())
return if (index > 0) name.substring(0, index) else name
}
companion object {
const val productionSuffix = "Main"
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.module.Module
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.konan.target.presetName
import java.io.BufferedWriter
class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultiplatformModuleBuilder() {
@@ -14,7 +15,7 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
private val commonName: String = "common"
private var jvmTargetName: String = "jvm"
private var jsTargetName: String = "js"
private var nativeTargetName: String = "ios"
private var nativeTargetName: String = defaultNativeTarget.userTargetName
private val commonSourceName get() = "$commonName$productionSuffix"
private val commonTestName get() = "$commonName$testSuffix"
@@ -30,7 +31,7 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
override fun getPresentableName() = "Kotlin (Multiplatform Library)"
override fun getDescription() =
"Multiplatform Gradle projects allow sharing the same Kotlin code between all three main platforms (JVM, JS, iOS)."
"Multiplatform Gradle projects allow sharing the same Kotlin code between all three main platforms (JVM, JS, Native)."
override fun createProjectSkeleton(module: Module, rootDir: VirtualFile) {
val src = rootDir.createChildDirectory(this, "src")
@@ -42,7 +43,7 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
val jsMain = src.createKotlinSampleFileWriter(jsSourceName)
val jsTest = src.createKotlinSampleFileWriter(jsTestName, fileName = "SampleTestsJS.kt")
val nativeMain = src.createKotlinSampleFileWriter(nativeSourceName)
val nativeTest = src.createKotlinSampleFileWriter(nativeTestName, fileName = "SampleTestsIOS.kt")
val nativeTest = src.createKotlinSampleFileWriter(nativeTestName, fileName = "SampleTestsNative.kt")
try {
commonMain.write(
@@ -98,7 +99,7 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
}
actual object Platform {
actual val name: String = "iOS"
actual val name: String = "Native"
}
""".trimIndent()
)
@@ -158,10 +159,10 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
import kotlin.test.Test
import kotlin.test.assertTrue
class SampleTestsIOS {
class SampleTestsNative {
@Test
fun testHello() {
assertTrue("iOS" in hello())
assertTrue("Native" in hello())
}
}
""".trimIndent()
@@ -180,7 +181,7 @@ class KotlinGradleSharedMultiplatformModuleBuilder : KotlinGradleAbstractMultipl
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
fromPreset(presets.iosX64, '$nativeTargetName')
fromPreset(presets.${defaultNativeTarget.presetName}, '${defaultNativeTarget.userTargetName}')
}
sourceSets {
$commonSourceName {
@@ -41,9 +41,8 @@ import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.gradle.ArgsInfo
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.configuration.GradlePropertiesFileFacade.Companion.KOTLIN_CODE_STYLE_GRADLE_SETTING
import org.jetbrains.kotlin.idea.facet.*
import org.jetbrains.kotlin.idea.formatter.KotlinObsoleteCodeStyle
import org.jetbrains.kotlin.idea.formatter.KotlinStyleGuideCodeStyle
import org.jetbrains.kotlin.idea.formatter.ProjectCodeStyleImporter
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
@@ -149,7 +148,7 @@ class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Vo
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
}
val codeStyleStr = GradlePropertiesFileUtils.readProperty(project, GradlePropertiesFileUtils.KOTLIN_CODE_STYLE_GRADLE_SETTING)
val codeStyleStr = GradlePropertiesFileFacade.forProject(project).readProperty(KOTLIN_CODE_STYLE_GRADLE_SETTING)
ProjectCodeStyleImporter.apply(project, codeStyleStr)
}
}
@@ -333,6 +332,6 @@ internal fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List
}
internal fun findKotlinCoroutinesProperty(project: Project): String {
return GradlePropertiesFileUtils.readProperty(project, "kotlin.coroutines")
return GradlePropertiesFileFacade.forProject(project).readProperty("kotlin.coroutines")
?: CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState)
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.cli.common.arguments.ManualLanguageFeatureSetting
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.configuration.GradlePropertiesFileFacade.Companion.KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
@@ -237,8 +238,10 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
targetData.moduleIds = compilationIds
}
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
for (sourceSet in mppModel.sourceSets.values) {
if (sourceSet.platform == KotlinPlatform.ANDROID) continue
if (sourceSet.platform == KotlinPlatform.COMMON && ignoreCommonSourceSets) continue
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
if (existingSourceSetDataNode?.kotlinSourceSet != null) continue
@@ -680,5 +683,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
private fun getKotlinModuleId(gradleModule: IdeaModule, kotlinModule: KotlinModule, resolverCtx: ProjectResolverContext) =
getModuleId(resolverCtx, gradleModule) + ":" + kotlinModule.fullName()
private fun ExternalProject.notImportedCommonSourceSets() =
GradlePropertiesFileFacade.forExternalProject(this).readProperty(KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING)?.equals("true", ignoreCase = true) ?: false
}
}
}
+1
View File
@@ -78,6 +78,7 @@
<scriptDefinitionContributor implementation="org.jetbrains.kotlin.idea.core.script.GradleScriptDefinitionsContributor" order="first"/>
<moduleBuilder implementation="org.jetbrains.kotlin.ide.konan.gradle.KotlinGradleNativeMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleWebMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSharedMultiplatformModuleBuilder"/>
</extensions>
+1
View File
@@ -78,6 +78,7 @@
<scriptDefinitionContributor implementation="org.jetbrains.kotlin.idea.core.script.GradleScriptDefinitionsContributor" order="first"/>
<moduleBuilder implementation="org.jetbrains.kotlin.ide.konan.gradle.KotlinGradleNativeMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleWebMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSharedMultiplatformModuleBuilder"/>
</extensions>
+1
View File
@@ -86,6 +86,7 @@
<scriptDefinitionContributor implementation="org.jetbrains.kotlin.idea.core.script.GradleScriptDefinitionsContributor" order="first"/>
<moduleBuilder implementation="org.jetbrains.kotlin.ide.konan.gradle.KotlinGradleNativeMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleWebMultiplatformModuleBuilder"/>
<moduleBuilder implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleSharedMultiplatformModuleBuilder"/>
@@ -284,4 +284,16 @@ open class HostManager(subtargetProvider: SubTargetProvider = NoSubTargets()) {
}
}
val KonanTarget.presetName: String
get() = when (this) {
KonanTarget.ANDROID_ARM32 -> "androidNativeArm32"
KonanTarget.ANDROID_ARM64 -> "androidNativeArm64"
else -> evaluatePresetName(this.name)
}
private fun evaluatePresetName(targetName: String): String {
val nameParts = targetName.split('_').mapNotNull { it.takeIf(String::isNotEmpty) }
return nameParts.asSequence().drop(1).joinToString("", nameParts.firstOrNull().orEmpty(), transform = String::capitalize)
}
class TargetSupportException(message: String = "", cause: Throwable? = null) : Exception(message, cause)
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.presetName
internal val Project.multiplatformExtension get(): KotlinMultiplatformExtension? =
project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
@@ -338,13 +338,6 @@ internal val KonanTarget.isCurrentHost: Boolean
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KonanTarget.presetName: String
get() = when (this) {
KonanTarget.ANDROID_ARM32 -> "androidNativeArm32"
KonanTarget.ANDROID_ARM64 -> "androidNativeArm64"
else -> lowerCamelCaseName(*this.name.split('_').toTypedArray())
}
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME