as32: Update to AS 3.2 C10
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<project name="Kotlin CI Steps" default="none">
|
||||
<import file="common.xml" optional="false"/>
|
||||
<property name="kotlin-home" value="${output}/kotlinc"/>
|
||||
|
||||
<property name="build.number" value="snapshot"/>
|
||||
<property name="fail.on.plugin.verifier.error" value="true"/>
|
||||
|
||||
<property name="version_substitute_dir" value="${basedir}/versions_temp/"/>
|
||||
|
||||
<property name="artifact.output.path" value="${basedir}/dist/artifacts/ideaPlugin"/>
|
||||
<property name="plugin.xml" value="idea/src/META-INF/plugin.xml"/>
|
||||
<property name="plugin.xml.bk" value="${version_substitute_dir}/plugin.xml.bk"/>
|
||||
<property name="plugin.xml.versioned" value="${plugin.xml}.versioned"/>
|
||||
<property name="plugin.xml.version.number" value="${build.number}"/>
|
||||
|
||||
<property name="compiler.version.java" value="core/util.runtime/src/org/jetbrains/kotlin/config/KotlinCompilerVersion.java"/>
|
||||
<property name="compiler.version.java.bk" value="${version_substitute_dir}/KotlinCompilerVersion.java.bk"/>
|
||||
<property name="compiler.version.java.versioned" value="${compiler.version.java}.versioned"/>
|
||||
<property name="compiler.version.number" value="${build.number}"/>
|
||||
<property name="compiler.ant.fork.jvmargs" value="-Xmx1024m"/>
|
||||
|
||||
<property name="plugin.zip" value="${artifact.output.path}/kotlin-plugin-${build.number}.zip"/>
|
||||
<property name="pluginArtifactDir" value="Kotlin" />
|
||||
|
||||
<macrodef name="echoprop">
|
||||
<attribute name="prop"/>
|
||||
<sequential>
|
||||
<echo>@{prop}=${@{prop}}</echo>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<echoprop prop="os.name"/>
|
||||
<echoprop prop="os.version"/>
|
||||
<echoprop prop="os.arch"/>
|
||||
<echoprop prop="java.home"/>
|
||||
<echoprop prop="java.vendor"/>
|
||||
<echoprop prop="java.version"/>
|
||||
<echoprop prop="user.name"/>
|
||||
<echoprop prop="user.home"/>
|
||||
<echoprop prop="user.dir"/>
|
||||
|
||||
<macrodef name="run-gradle">
|
||||
<attribute name="tasks" />
|
||||
<attribute name="args" default="" />
|
||||
<sequential>
|
||||
<java classname="org.gradle.wrapper.GradleWrapperMain"
|
||||
fork="true"
|
||||
dir="${basedir}"
|
||||
failonerror="true"
|
||||
timeout="4000000"
|
||||
maxmemory="400m"
|
||||
taskname="gradle">
|
||||
<classpath>
|
||||
<pathelement location="${basedir}/gradle/wrapper/gradle-wrapper.jar"/>
|
||||
</classpath>
|
||||
<arg line="--no-daemon" />
|
||||
<arg line="@{tasks}" />
|
||||
<arg line="@{args}" />
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="cleanupArtifacts">
|
||||
<run-gradle tasks="cleanupArtifacts" />
|
||||
</target>
|
||||
|
||||
<target name="zip-compiler">
|
||||
<run-gradle tasks="zipCompiler" args="-PdeployVersion=${build.number}" />
|
||||
</target>
|
||||
|
||||
<target name="zip-test-data">
|
||||
<run-gradle tasks="zipTestData" />
|
||||
</target>
|
||||
|
||||
<target name="writeCompilerVersionToTemplateFile">
|
||||
<!-- empty, version is written in gradle build -->
|
||||
</target>
|
||||
|
||||
<target name="writePluginVersionToTemplateFile">
|
||||
<run-gradle tasks="writePluginVersion" args="-PpluginVersion=${plugin.xml.version.number}" />
|
||||
</target>
|
||||
|
||||
<target name="zipArtifacts">
|
||||
<run-gradle tasks="zipPlugin" args="-PpluginArtifactDir=${pluginArtifactDir} -PpluginZipPath=${plugin.zip}"/>
|
||||
</target>
|
||||
|
||||
<macrodef name="print-statistic">
|
||||
<attribute name="key"/>
|
||||
<attribute name="value"/>
|
||||
|
||||
<sequential>
|
||||
<echo message="##teamcity[buildStatisticValue key='@{key}' value='@{value}']"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<macrodef name="print-file-size-statistic">
|
||||
<attribute name="path"/>
|
||||
<attribute name="file-name"/>
|
||||
|
||||
<sequential>
|
||||
<local name="file.size"/>
|
||||
<length file="@{path}/@{file-name}" property="file.size"/>
|
||||
<print-statistic key="@{file-name} size" value="${file.size}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="printStatistics">
|
||||
<print-file-size-statistic path="${kotlin-home}/lib" file-name="kotlin-stdlib.jar"/>
|
||||
<print-file-size-statistic path="${kotlin-home}/lib" file-name="kotlin-reflect.jar"/>
|
||||
|
||||
<print-file-size-statistic path="${kotlin-home}/lib" file-name="kotlin-stdlib-js.jar"/>
|
||||
<print-file-size-statistic path="${js.stdlib.output.dir}" file-name="kotlin.js"/>
|
||||
<print-file-size-statistic path="${js.stdlib.output.dir}" file-name="kotlin-test.js"/>
|
||||
<print-file-size-statistic path="${basedir}/libraries/stdlib/js/build/classes/main" file-name="kotlin.meta.js"/>
|
||||
</target>
|
||||
|
||||
<target name="none">
|
||||
<fail message="Either specify pre_build or post_build"/>
|
||||
</target>
|
||||
|
||||
<macrodef name="patch_plugin_xml">
|
||||
<attribute name="plugin.xml" />
|
||||
<sequential>
|
||||
<replace file="@{plugin.xml}" token="<!-- DEPENDS-ON-AS-PLACEHOLDER -->" value="<depends>com.intellij.modules.androidstudio</depends>"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="patchXmlForAndroidStudio">
|
||||
<unzip src="${artifact.output.path}/Kotlin/lib/kotlin-plugin.jar" dest="tmpAndroidStudio">
|
||||
<patternset>
|
||||
<include name="META-INF/plugin.xml"/>
|
||||
</patternset>
|
||||
</unzip>
|
||||
|
||||
<patch_plugin_xml plugin.xml="tmpAndroidStudio/META-INF/plugin.xml"/>
|
||||
|
||||
<jar destfile="${artifact.output.path}/Kotlin/lib/kotlin-plugin.jar" update="true">
|
||||
<fileset dir="tmpAndroidStudio"/>
|
||||
<file file="META-INF/plugin.xml"/>
|
||||
</jar>
|
||||
|
||||
<delete file="tmpAndroidStudio"/>
|
||||
</target>
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=false
|
||||
org.gradle.configureondemand=false
|
||||
org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m
|
||||
|
||||
#buildSrc.kotlin.repo=https://jcenter.bintray.com
|
||||
#buildSrc.kotlin.version=1.1.50
|
||||
|
||||
intellijUltimateEnabled=false
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.light.LightJavaModule
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.config.ContentRoot
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isValidJavaFqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
|
||||
import java.io.IOException
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
|
||||
class ClasspathRootsResolver(
|
||||
private val psiManager: PsiManager,
|
||||
private val messageCollector: MessageCollector?,
|
||||
private val additionalModules: List<String>,
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
||||
private val javaModuleFinder: CliJavaModuleFinder,
|
||||
private val requireStdlibModule: Boolean,
|
||||
private val outputDirectory: VirtualFile?
|
||||
) {
|
||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||
|
||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||
|
||||
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
|
||||
|
||||
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
|
||||
val javaSourceRoots = mutableListOf<RootWithPrefix>()
|
||||
val jvmClasspathRoots = mutableListOf<VirtualFile>()
|
||||
val jvmModulePathRoots = mutableListOf<VirtualFile>()
|
||||
|
||||
for (contentRoot in contentRoots) {
|
||||
if (contentRoot !is JvmContentRoot) continue
|
||||
val root = contentRootToVirtualFile(contentRoot) ?: continue
|
||||
when (contentRoot) {
|
||||
is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix)
|
||||
is JvmClasspathRoot -> jvmClasspathRoots += root
|
||||
is JvmModulePathRoot -> jvmModulePathRoots += root
|
||||
else -> error("Unknown root type: $contentRoot")
|
||||
}
|
||||
}
|
||||
|
||||
return computeRoots(javaSourceRoots, jvmClasspathRoots, jvmModulePathRoots)
|
||||
}
|
||||
|
||||
private fun computeRoots(
|
||||
javaSourceRoots: List<RootWithPrefix>,
|
||||
jvmClasspathRoots: List<VirtualFile>,
|
||||
jvmModulePathRoots: List<VirtualFile>
|
||||
): RootsAndModules {
|
||||
val result = mutableListOf<JavaRoot>()
|
||||
val modules = mutableListOf<JavaModule>()
|
||||
|
||||
val hasOutputDirectoryInClasspath = outputDirectory in jvmClasspathRoots || outputDirectory in jvmModulePathRoots
|
||||
|
||||
for ((root, packagePrefix) in javaSourceRoots) {
|
||||
val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath)
|
||||
if (modularRoot != null) {
|
||||
modules += modularRoot
|
||||
}
|
||||
else {
|
||||
result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix ->
|
||||
if (isValidJavaFqName(prefix)) FqName(prefix)
|
||||
else null.also {
|
||||
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (root in jvmClasspathRoots) {
|
||||
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
||||
}
|
||||
|
||||
val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } }
|
||||
|
||||
for (root in jvmModulePathRoots) {
|
||||
// Do not add output directory as a separate module if we're compiling an explicit named module.
|
||||
// It's going to be included as a root of our module in modularSourceRoot.
|
||||
if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue
|
||||
|
||||
val module = modularBinaryRoot(root)
|
||||
if (module != null) {
|
||||
modules += module
|
||||
}
|
||||
}
|
||||
|
||||
addModularRoots(modules, result)
|
||||
|
||||
return RootsAndModules(result, modules)
|
||||
}
|
||||
|
||||
/*
|
||||
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
|
||||
val moduleInfoFile =
|
||||
when {
|
||||
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
|
||||
root.name == PsiJavaModule.MODULE_INFO_FILE -> root
|
||||
else -> null
|
||||
} ?: return null
|
||||
|
||||
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
|
||||
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
|
||||
|
||||
return moduleInfoFile to psiJavaModule
|
||||
}
|
||||
*/
|
||||
|
||||
private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? {
|
||||
/*
|
||||
val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null
|
||||
val sourceRoot = JavaModule.Root(root, isBinary = false)
|
||||
val roots =
|
||||
if (hasOutputDirectoryInClasspath)
|
||||
listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true))
|
||||
else listOf(sourceRoot)
|
||||
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile)
|
||||
*/
|
||||
return null
|
||||
}
|
||||
|
||||
private fun modularBinaryRoot(root: VirtualFile): JavaModule? {
|
||||
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
|
||||
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
|
||||
|
||||
/*
|
||||
val moduleInfoFile =
|
||||
root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
|
||||
?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf {
|
||||
manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true
|
||||
}
|
||||
|
||||
if (moduleInfoFile != null) {
|
||||
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
|
||||
}
|
||||
*/
|
||||
|
||||
// Only .jar files can be automatic modules
|
||||
if (isJar) {
|
||||
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
|
||||
|
||||
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
|
||||
if (automaticModuleName != null) {
|
||||
return JavaModule.Automatic(automaticModuleName, moduleRoot)
|
||||
}
|
||||
|
||||
val originalFile = VfsUtilCore.virtualToIoFile(root)
|
||||
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
|
||||
if (moduleName.isEmpty()) {
|
||||
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
|
||||
return null
|
||||
}
|
||||
return JavaModule.Automatic(moduleName, moduleRoot)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? {
|
||||
val manifestFile = jarRoot.findChild("META-INF")?.findChild("MANIFEST.MF")
|
||||
return try {
|
||||
manifestFile?.inputStream?.let(::Manifest)?.mainAttributes
|
||||
}
|
||||
catch (e: IOException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
||||
// In current implementation, at most one source module is supported. This can be relaxed in the future if we support another
|
||||
// compilation mode, similar to java's --module-source-path
|
||||
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filter(JavaModule::isSourceModule)
|
||||
if (sourceModules.size > 1) {
|
||||
for (module in sourceModules) {
|
||||
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for (module in modules) {
|
||||
val existing = javaModuleFinder.findModule(module.name)
|
||||
if (existing == null) {
|
||||
javaModuleFinder.addUserModule(module)
|
||||
}
|
||||
else if (module.moduleRoots != existing.moduleRoots) {
|
||||
fun JavaModule.getRootFile() =
|
||||
moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it }
|
||||
|
||||
val thisFile = module.getRootFile()
|
||||
val existingFile = existing.getRootFile()
|
||||
val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}"
|
||||
report(STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " +
|
||||
"has been found earlier on the module path$atExistingPath", thisFile)
|
||||
}
|
||||
}
|
||||
|
||||
if (javaModuleFinder.allObservableModules.none()) return
|
||||
|
||||
val sourceModule = sourceModules.singleOrNull()
|
||||
val addAllModulePathToRoots = "ALL-MODULE-PATH" in additionalModules
|
||||
if (addAllModulePathToRoots && sourceModule != null) {
|
||||
report(ERROR, "-Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module")
|
||||
return
|
||||
}
|
||||
|
||||
val rootModules = when {
|
||||
sourceModule != null -> listOf(sourceModule.name) + additionalModules
|
||||
addAllModulePathToRoots -> modules.map(JavaModule::name)
|
||||
else -> computeDefaultRootModules() + additionalModules
|
||||
}
|
||||
|
||||
val allDependencies = javaModuleGraph.getAllDependencies(rootModules)
|
||||
if (allDependencies.any { moduleName -> javaModuleFinder.findModule(moduleName) is JavaModule.Automatic }) {
|
||||
// According to java.lang.module javadoc, if at least one automatic module is added to the module graph,
|
||||
// all observable automatic modules should be added.
|
||||
// There are no automatic modules in the JDK, so we select all automatic modules out of user modules
|
||||
for (module in modules) {
|
||||
if (module is JavaModule.Automatic) {
|
||||
allDependencies += module.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report(LOGGING, "Loading modules: $allDependencies")
|
||||
|
||||
for (moduleName in allDependencies) {
|
||||
val module = javaModuleFinder.findModule(moduleName)
|
||||
if (module == null) {
|
||||
report(ERROR, "Module $moduleName cannot be found in the module graph")
|
||||
}
|
||||
else {
|
||||
for ((root, isBinary) in module.moduleRoots) {
|
||||
result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requireStdlibModule && sourceModule != null && !javaModuleGraph.reads(sourceModule.name, KOTLIN_STDLIB_MODULE_NAME)) {
|
||||
report(
|
||||
ERROR,
|
||||
"The Kotlin standard library is not found in the module graph. " +
|
||||
"Please ensure you have the 'requires $KOTLIN_STDLIB_MODULE_NAME' clause in your module definition",
|
||||
sourceModule.moduleInfoFile
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// See http://openjdk.java.net/jeps/261
|
||||
private fun computeDefaultRootModules(): List<String> {
|
||||
val result = arrayListOf<String>()
|
||||
|
||||
val systemModules = javaModuleFinder.systemModules.associateBy(JavaModule::name)
|
||||
val javaSeExists = "java.se" in systemModules
|
||||
if (javaSeExists) {
|
||||
// The java.se module is a root, if it exists.
|
||||
result.add("java.se")
|
||||
}
|
||||
|
||||
fun JavaModule.Explicit.exportsAtLeastOnePackageUnqualified(): Boolean = moduleInfo.exports.any { it.toModules.isEmpty() }
|
||||
|
||||
if (!javaSeExists) {
|
||||
// If it does not exist then every java.* module on the upgrade module path or among the system modules
|
||||
// that exports at least one package, without qualification, is a root.
|
||||
for ((name, module) in systemModules) {
|
||||
if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
|
||||
result.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((name, module) in systemModules) {
|
||||
// Every non-java.* module on the upgrade module path or among the system modules that exports at least one package,
|
||||
// without qualification, is also a root.
|
||||
if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) {
|
||||
result.add(name)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) {
|
||||
if (messageCollector == null) {
|
||||
throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)")
|
||||
}
|
||||
messageCollector.report(
|
||||
severity, message,
|
||||
if (file == null) null else CompilerMessageLocation.create(MessageUtil.virtualFileToPath(file))
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}"
|
||||
const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"
|
||||
const val IS_MULTI_RELEASE = "Multi-Release"
|
||||
}
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.cli.jvm.compiler
|
||||
|
||||
import com.intellij.core.CoreJavaFileManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.file.PsiPackageImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import gnu.trove.THashMap
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
|
||||
import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.isNotTopLevelClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
|
||||
import org.jetbrains.kotlin.util.PerformanceCounter
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
// TODO: do not inherit from CoreJavaFileManager to avoid accidental usage of its methods which do not use caches/indices
|
||||
// Currently, the only relevant usage of this class as CoreJavaFileManager is at CoreJavaDirectoryService.getPackage,
|
||||
// which is indirectly invoked from PsiPackage.getSubPackages
|
||||
class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager {
|
||||
private val perfCounter = PerformanceCounter.create("Find Java class")
|
||||
private lateinit var index: JvmDependenciesIndex
|
||||
private lateinit var singleJavaFileRootsIndex: SingleJavaFileRootsIndex
|
||||
private lateinit var packagePartProviders: List<JvmPackagePartProvider>
|
||||
private val topLevelClassesCache: MutableMap<FqName, VirtualFile?> = THashMap()
|
||||
private val allScope = GlobalSearchScope.allScope(myPsiManager.project)
|
||||
private var useFastClassFilesReading = false
|
||||
|
||||
fun initialize(
|
||||
index: JvmDependenciesIndex,
|
||||
packagePartProviders: List<JvmPackagePartProvider>,
|
||||
singleJavaFileRootsIndex: SingleJavaFileRootsIndex,
|
||||
useFastClassFilesReading: Boolean
|
||||
) {
|
||||
this.index = index
|
||||
this.packagePartProviders = packagePartProviders
|
||||
this.singleJavaFileRootsIndex = singleJavaFileRootsIndex
|
||||
this.useFastClassFilesReading = useFastClassFilesReading
|
||||
}
|
||||
|
||||
private fun findPsiClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? = perfCounter.time {
|
||||
findVirtualFileForTopLevelClass(classId, searchScope)?.findPsiClassInVirtualFile(classId.relativeClassName.asString())
|
||||
}
|
||||
|
||||
private fun findVirtualFileForTopLevelClass(classId: ClassId, searchScope: GlobalSearchScope): VirtualFile? {
|
||||
val relativeClassName = classId.relativeClassName.asString()
|
||||
return topLevelClassesCache.getOrPut(classId.packageFqName.child(classId.relativeClassName.pathSegments().first())) {
|
||||
index.findClass(classId) { dir, type ->
|
||||
findVirtualFileGivenPackage(dir, relativeClassName, type)
|
||||
}
|
||||
?: singleJavaFileRootsIndex.findJavaSourceClass(classId)
|
||||
}?.takeIf { it in searchScope }
|
||||
}
|
||||
|
||||
private val binaryCache: MutableMap<ClassId, JavaClass?> = THashMap()
|
||||
private val signatureParsingComponent =
|
||||
BinaryClassSignatureParser()
|
||||
|
||||
override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? {
|
||||
val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null
|
||||
|
||||
if (useFastClassFilesReading && virtualFile.extension == "class") {
|
||||
// We return all class files' names in the directory in knownClassNamesInPackage method, so one may request an inner class
|
||||
return binaryCache.getOrPut(classId) {
|
||||
// Note that currently we implicitly suppose that searchScope for binary classes is constant and we do not use it
|
||||
// as a key in cache
|
||||
// This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary
|
||||
// When it become wrong because we introduce the modules into CLI, it's worth to consider
|
||||
// having different KotlinCliJavaFileManagerImpl's for different modules
|
||||
val classContent = virtualFile.contentsToByteArray()
|
||||
if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null
|
||||
classId.outerClassId?.let { outerClassId ->
|
||||
val outerClass = findClass(outerClassId, searchScope)
|
||||
return@getOrPut outerClass?.findInnerClass(classId.shortClassName)
|
||||
}
|
||||
|
||||
val resolver = ClassifierResolutionContext { findClass(it, allScope) }
|
||||
|
||||
BinaryJavaClass(
|
||||
virtualFile,
|
||||
classId.asSingleFqName(),
|
||||
resolver,
|
||||
signatureParsingComponent,
|
||||
outerClass = null,
|
||||
classContent = classContent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return virtualFile.findPsiClassInVirtualFile(classId.relativeClassName.asString())?.let(::JavaClassImpl)
|
||||
}
|
||||
|
||||
// this method is called from IDEA to resolve dependencies in Java code
|
||||
// which supposedly shouldn't have errors so the dependencies exist in general
|
||||
override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? {
|
||||
// String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin.
|
||||
// For example, if qName is "a.b.c.d.e", we should either look for a top level class "e" in the package "a.b.c.d",
|
||||
// or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b".
|
||||
// Below, we start by looking for the top level class "e" in the package "a.b.c.d" first, then for the class "d.e" in the package
|
||||
// "a.b.c", and so on, until we find something. Most classes are top level, so most of the times the search ends quickly
|
||||
|
||||
forEachClassId(qName) { classId ->
|
||||
findPsiClass(classId, scope)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private inline fun forEachClassId(fqName: String, block: (ClassId) -> Unit) {
|
||||
var classId = fqName.toSafeTopLevelClassId() ?: return
|
||||
|
||||
while (true) {
|
||||
block(classId)
|
||||
|
||||
val packageFqName = classId.packageFqName
|
||||
if (packageFqName.isRoot) break
|
||||
|
||||
classId = ClassId(
|
||||
packageFqName.parent(),
|
||||
FqName(packageFqName.shortName().asString() + "." + classId.relativeClassName.asString()),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun findClasses(qName: String, scope: GlobalSearchScope): Array<PsiClass> = perfCounter.time {
|
||||
val result = ArrayList<PsiClass>(1)
|
||||
forEachClassId(qName) { classId ->
|
||||
val relativeClassName = classId.relativeClassName.asString()
|
||||
index.traverseDirectoriesInPackage(classId.packageFqName) { dir, rootType ->
|
||||
val psiClass =
|
||||
findVirtualFileGivenPackage(dir, relativeClassName, rootType)
|
||||
?.takeIf { it in scope }
|
||||
?.findPsiClassInVirtualFile(relativeClassName)
|
||||
if (psiClass != null) {
|
||||
result.add(psiClass)
|
||||
}
|
||||
// traverse all
|
||||
true
|
||||
}
|
||||
|
||||
result.addIfNotNull(
|
||||
singleJavaFileRootsIndex.findJavaSourceClass(classId)
|
||||
?.takeIf { it in scope }
|
||||
?.findPsiClassInVirtualFile(relativeClassName)
|
||||
)
|
||||
|
||||
if (result.isNotEmpty()) {
|
||||
return@time result.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
PsiClass.EMPTY_ARRAY
|
||||
}
|
||||
|
||||
override fun findPackage(packageName: String): PsiPackage? {
|
||||
var found = false
|
||||
val packageFqName = packageName.toSafeFqName() ?: return null
|
||||
index.traverseDirectoriesInPackage(packageFqName) { _, _ ->
|
||||
found = true
|
||||
//abort on first found
|
||||
false
|
||||
}
|
||||
if (!found) {
|
||||
found = packagePartProviders.any { it.findPackageParts(packageName).isNotEmpty() }
|
||||
}
|
||||
if (!found) {
|
||||
found = singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName).isNotEmpty()
|
||||
}
|
||||
return if (found) PsiPackageImpl(myPsiManager, packageName) else null
|
||||
}
|
||||
|
||||
private fun findVirtualFileGivenPackage(
|
||||
packageDir: VirtualFile,
|
||||
classNameWithInnerClasses: String,
|
||||
rootType: JavaRoot.RootType
|
||||
): VirtualFile? {
|
||||
val topLevelClassName = classNameWithInnerClasses.substringBefore('.')
|
||||
|
||||
val vFile = when (rootType) {
|
||||
JavaRoot.RootType.BINARY -> packageDir.findChild("$topLevelClassName.class")
|
||||
JavaRoot.RootType.SOURCE -> packageDir.findChild("$topLevelClassName.java")
|
||||
} ?: return null
|
||||
|
||||
if (!vFile.isValid) {
|
||||
LOG.error("Invalid child of valid parent: ${vFile.path}; ${packageDir.isValid} path=${packageDir.path}")
|
||||
return null
|
||||
}
|
||||
|
||||
return vFile
|
||||
}
|
||||
|
||||
private fun VirtualFile.findPsiClassInVirtualFile(
|
||||
classNameWithInnerClasses: String
|
||||
): PsiClass? {
|
||||
val file = myPsiManager.findFile(this) as? PsiClassOwner ?: return null
|
||||
return findClassInPsiFile(classNameWithInnerClasses, file)
|
||||
}
|
||||
|
||||
override fun knownClassNamesInPackage(packageFqName: FqName): Set<String> {
|
||||
val result = hashSetOf<String>()
|
||||
index.traverseDirectoriesInPackage(packageFqName, continueSearch = {
|
||||
dir, _ ->
|
||||
|
||||
for (child in dir.children) {
|
||||
if (child.extension == "class" || child.extension == "java") {
|
||||
result.add(child.nameWithoutExtension)
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
|
||||
for (classId in singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName)) {
|
||||
assert(!classId.isNestedClass) { "ClassId of a single .java source class should not be nested: $classId" }
|
||||
result.add(classId.shortClassName.asString())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
override fun findModules(moduleName: String, scope: GlobalSearchScope): Collection<PsiJavaModule> {
|
||||
// TODO
|
||||
return emptySet()
|
||||
}
|
||||
*/
|
||||
|
||||
override fun getNonTrivialPackagePrefixes(): Collection<String> = emptyList()
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java)
|
||||
|
||||
private fun findClassInPsiFile(classNameWithInnerClassesDotSeparated: String, file: PsiClassOwner): PsiClass? {
|
||||
for (topLevelClass in file.classes) {
|
||||
val candidate = findClassByTopLevelClass(classNameWithInnerClassesDotSeparated, topLevelClass)
|
||||
if (candidate != null) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findClassByTopLevelClass(className: String, topLevelClass: PsiClass): PsiClass? {
|
||||
if (className.indexOf('.') < 0) {
|
||||
return if (className == topLevelClass.name) topLevelClass else null
|
||||
}
|
||||
|
||||
val segments = StringUtil.split(className, ".").iterator()
|
||||
if (!segments.hasNext() || segments.next() != topLevelClass.name) {
|
||||
return null
|
||||
}
|
||||
var curClass = topLevelClass
|
||||
while (segments.hasNext()) {
|
||||
val innerClassName = segments.next()
|
||||
val innerClass = curClass.findInnerClassByName(innerClassName, false) ?: return null
|
||||
curClass = innerClass
|
||||
}
|
||||
return curClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a sad workaround to avoid throwing exception when called from inside IDEA code
|
||||
private fun <T : Any> safely(compute: () -> T): T? = try {
|
||||
compute()
|
||||
}
|
||||
catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
catch (e: AssertionError) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun String.toSafeFqName(): FqName? = safely { FqName(this) }
|
||||
private fun String.toSafeTopLevelClassId(): ClassId? = safely { ClassId.topLevel(FqName(this)) }
|
||||
+484
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* 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.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.script.tryConstructClassFromStringArgs
|
||||
import org.jetbrains.kotlin.util.PerformanceCounter
|
||||
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
|
||||
import java.io.File
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object KotlinToJVMBytecodeCompiler {
|
||||
|
||||
private fun getAbsolutePaths(buildFile: File, module: Module): List<String> {
|
||||
return module.getSourceFiles().map { sourceFile ->
|
||||
val source = File(sourceFile)
|
||||
if (!source.isAbsolute) {
|
||||
File(buildFile.absoluteFile.parentFile, sourceFile).absolutePath
|
||||
}
|
||||
else {
|
||||
source.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeOutput(
|
||||
configuration: CompilerConfiguration,
|
||||
outputFiles: OutputFileCollection,
|
||||
mainClass: FqName?
|
||||
) {
|
||||
val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
|
||||
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
|
||||
if (jarPath != null) {
|
||||
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
|
||||
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, mainClass, outputFiles)
|
||||
if (reportOutputFiles) {
|
||||
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
|
||||
messageCollector.report(OUTPUT, message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
|
||||
outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles)
|
||||
}
|
||||
|
||||
private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
|
||||
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
|
||||
return GenerationStateEventCallback.DO_NOTHING
|
||||
}
|
||||
return GenerationStateEventCallback { state ->
|
||||
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
|
||||
writeOutput(configuration, currentOutput, mainClass = null)
|
||||
if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
|
||||
state.factory.releaseGeneratedOutput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File, chunk: List<Module>): Boolean {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
|
||||
|
||||
val projectConfiguration = environment.configuration
|
||||
for (module in chunk) {
|
||||
moduleVisibilityManager.addModule(module)
|
||||
}
|
||||
|
||||
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
|
||||
for (path in friendPaths) {
|
||||
moduleVisibilityManager.addFriendPath(path)
|
||||
}
|
||||
|
||||
val targetDescription = "in targets [" + chunk.joinToString { input -> input.getModuleName() + "-" + input.getModuleType() } + "]"
|
||||
|
||||
val result = repeatAnalysisIfNeeded(analyze(environment, targetDescription), environment, targetDescription)
|
||||
if (result == null || !result.shouldGenerateCode) return false
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
result.throwIfError()
|
||||
|
||||
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
|
||||
|
||||
for (module in chunk) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
val ktFiles = CompileEnvironmentUtil.getKtFiles(
|
||||
environment.project, getAbsolutePaths(buildFile, module), projectConfiguration
|
||||
) { path -> throw IllegalStateException("Should have been checked before: $path") }
|
||||
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
|
||||
|
||||
val moduleConfiguration = projectConfiguration.copy().apply {
|
||||
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
|
||||
}
|
||||
|
||||
outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module)
|
||||
}
|
||||
|
||||
try {
|
||||
for ((_, state) in outputs) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
writeOutput(state.configuration, state.factory, null)
|
||||
}
|
||||
|
||||
if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) {
|
||||
val singleModule = chunk.singleOrNull()
|
||||
if (singleModule != null) {
|
||||
return JavacWrapper.getInstance(environment.project).use {
|
||||
it.compile(File(singleModule.getOutputDirectory()))
|
||||
}
|
||||
}
|
||||
else {
|
||||
projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).let {
|
||||
it.report(WARNING, "A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). -Xuse-javac option couldn't be used to compile java files")
|
||||
}
|
||||
JavacWrapper.getInstance(environment.project).close()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
finally {
|
||||
outputs.values.forEach(GenerationState::destroy)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File) {
|
||||
for (module in chunk) {
|
||||
configuration.addKotlinSourceRoots(getAbsolutePaths(buildFile, module))
|
||||
}
|
||||
|
||||
for (module in chunk) {
|
||||
for ((path, packagePrefix) in module.getJavaSourceRoots()) {
|
||||
configuration.addJavaSourceRoot(File(path), packagePrefix)
|
||||
}
|
||||
}
|
||||
|
||||
val isJava9Module = false /*chunk.any { module ->
|
||||
module.getJavaSourceRoots().any { (path, packagePrefix) ->
|
||||
val file = File(path)
|
||||
packagePrefix == null &&
|
||||
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
|
||||
(file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
|
||||
}
|
||||
}*/
|
||||
|
||||
for (module in chunk) {
|
||||
for (classpathRoot in module.getClasspathRoots()) {
|
||||
configuration.add(
|
||||
JVMConfigurationKeys.CONTENT_ROOTS,
|
||||
if (isJava9Module) JvmModulePathRoot(File(classpathRoot)) else JvmClasspathRoot(File(classpathRoot))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (module in chunk) {
|
||||
val modularJdkRoot = module.modularJdkRoot
|
||||
if (modularJdkRoot != null) {
|
||||
// We use the SDK of the first module in the chunk, which is not always correct because some other module in the chunk
|
||||
// might depend on a different SDK
|
||||
configuration.put(JVMConfigurationKeys.JDK_HOME, File(modularJdkRoot))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
|
||||
}
|
||||
|
||||
private fun findMainClass(generationState: GenerationState, files: List<KtFile>): FqName? {
|
||||
val mainFunctionDetector = MainFunctionDetector(generationState.bindingContext)
|
||||
return files.asSequence()
|
||||
.map { file ->
|
||||
if (mainFunctionDetector.hasMain(file.declarations))
|
||||
JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName
|
||||
else
|
||||
null
|
||||
}
|
||||
.singleOrNull { it != null }
|
||||
}
|
||||
|
||||
fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean {
|
||||
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
|
||||
|
||||
val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)
|
||||
for (path in friendPaths) {
|
||||
moduleVisibilityManager.addFriendPath(path)
|
||||
}
|
||||
|
||||
if (!checkKotlinPackageUsage(environment, environment.getSourceFiles())) return false
|
||||
|
||||
val generationState = analyzeAndGenerate(environment) ?: return false
|
||||
|
||||
val mainClass = findMainClass(generationState, environment.getSourceFiles())
|
||||
|
||||
try {
|
||||
writeOutput(environment.configuration, generationState.factory, mainClass)
|
||||
return true
|
||||
}
|
||||
finally {
|
||||
generationState.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun compileAndExecuteScript(environment: KotlinCoreEnvironment, scriptArgs: List<String>): ExitCode {
|
||||
val scriptClass = compileScript(environment) ?: return ExitCode.COMPILATION_ERROR
|
||||
|
||||
try {
|
||||
try {
|
||||
tryConstructClassFromStringArgs(scriptClass, scriptArgs)
|
||||
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
|
||||
}
|
||||
finally {
|
||||
// NB: these lines are required (see KT-9546) but aren't covered by tests
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
reportExceptionFromScript(e)
|
||||
return ExitCode.SCRIPT_EXECUTION_ERROR
|
||||
}
|
||||
|
||||
return ExitCode.OK
|
||||
}
|
||||
|
||||
private fun repeatAnalysisIfNeeded(
|
||||
result: AnalysisResult?,
|
||||
environment: KotlinCoreEnvironment,
|
||||
targetDescription: String?
|
||||
): AnalysisResult? {
|
||||
if (result is AnalysisResult.RetryWithAdditionalJavaRoots) {
|
||||
val configuration = environment.configuration
|
||||
|
||||
val oldReadOnlyValue = configuration.isReadOnly
|
||||
configuration.isReadOnly = false
|
||||
configuration.addJavaSourceRoots(result.additionalJavaRoots)
|
||||
configuration.isReadOnly = oldReadOnlyValue
|
||||
|
||||
if (result.addToEnvironment) {
|
||||
environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) })
|
||||
}
|
||||
|
||||
// Clear package caches (see KotlinJavaPsiFacade)
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
(PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
|
||||
}
|
||||
|
||||
// Clear all diagnostic messages
|
||||
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]?.clear()
|
||||
|
||||
// Repeat analysis with additional Java roots (kapt generated sources)
|
||||
return analyze(environment, targetDescription)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun reportExceptionFromScript(exception: Throwable) {
|
||||
// expecting InvocationTargetException from constructor invocation with cause that describes the actual cause
|
||||
val stream = System.err
|
||||
val cause = exception.cause
|
||||
if (exception !is InvocationTargetException || cause == null) {
|
||||
exception.printStackTrace(stream)
|
||||
return
|
||||
}
|
||||
stream.println(cause)
|
||||
val fullTrace = cause.stackTrace
|
||||
for (i in 0 until fullTrace.size - exception.stackTrace.size) {
|
||||
stream.println("\tat " + fullTrace[i])
|
||||
}
|
||||
}
|
||||
|
||||
fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader? = null): Class<*>? {
|
||||
val state = analyzeAndGenerate(environment) ?: return null
|
||||
|
||||
try {
|
||||
val urls = environment.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
|
||||
when (root) {
|
||||
is JvmModulePathRoot -> root.file // TODO: only add required modules
|
||||
is JvmClasspathRoot -> root.file
|
||||
else -> null
|
||||
}
|
||||
}.map { it.toURI().toURL() }
|
||||
|
||||
val classLoader = GeneratedClassLoader(state.factory, parentClassLoader ?: URLClassLoader(urls.toTypedArray(), null))
|
||||
|
||||
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
|
||||
return classLoader.loadClass(script.fqName.asString())
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw RuntimeException("Failed to evaluate script: " + e, e)
|
||||
}
|
||||
}
|
||||
|
||||
fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? {
|
||||
val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null
|
||||
|
||||
if (!result.shouldGenerateCode) return null
|
||||
|
||||
result.throwIfError()
|
||||
|
||||
return generate(environment, environment.configuration, result, environment.getSourceFiles(), null)
|
||||
}
|
||||
|
||||
private fun analyze(environment: KotlinCoreEnvironment, targetDescription: String?): AnalysisResult? {
|
||||
val sourceFiles = environment.getSourceFiles()
|
||||
val collector = environment.messageCollector
|
||||
|
||||
val analysisStart = PerformanceCounter.currentTime()
|
||||
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector, environment.configuration.languageVersionSettings)
|
||||
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
|
||||
val project = environment.project
|
||||
val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNullTo(hashSetOf()) { module ->
|
||||
environment.findLocalFile(module.getOutputDirectory())
|
||||
}.orEmpty()
|
||||
val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
|
||||
// To support partial and incremental compilation, we add the scope which contains binaries from output directories
|
||||
// of the compiled modules (.class) to the list of scopes of the source module
|
||||
val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs))
|
||||
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||
project,
|
||||
sourceFiles,
|
||||
NoScopeRecordCliBindingTrace(),
|
||||
environment.configuration,
|
||||
environment::createPackagePartProvider,
|
||||
sourceModuleSearchScope = scope
|
||||
)
|
||||
}
|
||||
|
||||
val analysisNanos = PerformanceCounter.currentTime() - analysisStart
|
||||
|
||||
val sourceLinesOfCode = environment.countLinesOfCode(sourceFiles)
|
||||
val time = TimeUnit.NANOSECONDS.toMillis(analysisNanos)
|
||||
val speed = sourceLinesOfCode.toFloat() * 1000 / time
|
||||
|
||||
val message = "ANALYZE: ${sourceFiles.size} files ($sourceLinesOfCode lines) ${targetDescription ?: ""}" +
|
||||
"in $time ms - ${"%.3f".format(speed)} loc/s"
|
||||
|
||||
K2JVMCompiler.reportPerf(environment.configuration, message)
|
||||
|
||||
val analysisResult = analyzerWithCompilerReport.analysisResult
|
||||
|
||||
return if (!analyzerWithCompilerReport.hasErrors() || analysisResult is AnalysisResult.RetryWithAdditionalJavaRoots)
|
||||
analysisResult
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
class DirectoriesScope(
|
||||
project: Project,
|
||||
private val directories: Set<VirtualFile>
|
||||
) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) {
|
||||
private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem)
|
||||
|
||||
override fun contains(file: VirtualFile): Boolean {
|
||||
if (file.fileSystem !in fileSystems) return false
|
||||
|
||||
var parent: VirtualFile = file
|
||||
while (true) {
|
||||
if (parent in directories) return true
|
||||
parent = parent.parent ?: return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "All files under: $directories"
|
||||
}
|
||||
|
||||
private fun GenerationState.Builder.withModule(module: Module?) =
|
||||
apply {
|
||||
targetId(module?.let { TargetId(it) })
|
||||
moduleName(module?.getModuleName())
|
||||
outDirectory(module?.let { File(it.getOutputDirectory()) })
|
||||
}
|
||||
|
||||
private fun generate(
|
||||
environment: KotlinCoreEnvironment,
|
||||
configuration: CompilerConfiguration,
|
||||
result: AnalysisResult,
|
||||
sourceFiles: List<KtFile>,
|
||||
module: Module?
|
||||
): GenerationState {
|
||||
val isKapt2Enabled = environment.project.getUserData(IS_KAPT2_ENABLED_KEY) ?: false
|
||||
val generationState = GenerationState.Builder(
|
||||
environment.project,
|
||||
ClassBuilderFactories.binaries(isKapt2Enabled),
|
||||
result.moduleDescriptor,
|
||||
result.bindingContext,
|
||||
sourceFiles,
|
||||
configuration
|
||||
)
|
||||
.codegenFactory(if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory else DefaultCodegenFactory)
|
||||
.withModule(module)
|
||||
.onIndependentPartCompilationEnd(createOutputFilesFlushingCallbackIfPossible(configuration))
|
||||
.build()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
val generationStart = PerformanceCounter.currentTime()
|
||||
|
||||
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
val generationNanos = PerformanceCounter.currentTime() - generationStart
|
||||
val desc = if (module != null) "target " + module.getModuleName() + "-" + module.getModuleType() + " " else ""
|
||||
val numberOfSourceFiles = sourceFiles.size
|
||||
val numberOfLines = environment.countLinesOfCode(sourceFiles)
|
||||
val time = TimeUnit.NANOSECONDS.toMillis(generationNanos)
|
||||
val speed = numberOfLines.toFloat() * 1000 / time
|
||||
val message = "GENERATE: $numberOfSourceFiles files ($numberOfLines lines) ${desc}in $time ms - ${"%.3f".format(speed)} loc/s"
|
||||
|
||||
K2JVMCompiler.reportPerf(environment.configuration, message)
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
FilteredJvmDiagnostics(
|
||||
generationState.collectedExtraJvmDiagnostics,
|
||||
result.bindingContext.diagnostics
|
||||
),
|
||||
environment.messageCollector
|
||||
)
|
||||
|
||||
AnalyzerWithCompilerReport.reportBytecodeVersionErrors(
|
||||
generationState.extraJvmDiagnosticsTrace.bindingContext, environment.messageCollector
|
||||
)
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
return generationState
|
||||
}
|
||||
|
||||
private val KotlinCoreEnvironment.messageCollector: MessageCollector
|
||||
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.cli.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
|
||||
class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
||||
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
|
||||
private val userModules = linkedMapOf<String, JavaModule>()
|
||||
|
||||
fun addUserModule(module: JavaModule) {
|
||||
userModules.putIfAbsent(module.name, module)
|
||||
}
|
||||
|
||||
val allObservableModules: Sequence<JavaModule>
|
||||
get() = systemModules + userModules.values
|
||||
|
||||
val systemModules: Sequence<JavaModule.Explicit>
|
||||
get() = modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule)
|
||||
|
||||
override fun findModule(name: String): JavaModule? =
|
||||
modulesRoot?.findChild(name)?.let(this::findSystemModule) ?: userModules[name]
|
||||
|
||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||
/*
|
||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
||||
val moduleInfo = JavaModuleInfo.read(file) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
|
||||
*/
|
||||
return null
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.load.java.structure.impl
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiTypeParameter
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassMarker
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
|
||||
class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass), VirtualFileBoundJavaClass, JavaAnnotationOwnerImpl, JavaModifierListOwnerImpl {
|
||||
init {
|
||||
assert(psiClass !is PsiTypeParameter) { "PsiTypeParameter should be wrapped in JavaTypeParameter, not JavaClass: use JavaClassifier.create()" }
|
||||
}
|
||||
|
||||
override val innerClassNames: Collection<Name>
|
||||
get() = psi.innerClasses.mapNotNull { it.name?.takeIf(Name::isValidIdentifier)?.let(Name::identifier) }
|
||||
|
||||
override fun findInnerClass(name: Name): JavaClass? {
|
||||
return psi.findInnerClassByName(name.asString(), false)?.let(::JavaClassImpl)
|
||||
}
|
||||
|
||||
override val fqName: FqName?
|
||||
get() {
|
||||
val qualifiedName = psi.qualifiedName
|
||||
return if (qualifiedName == null) null else FqName(qualifiedName)
|
||||
}
|
||||
|
||||
override val name: Name
|
||||
get() = KtPsiUtil.safeName(psi.name)
|
||||
|
||||
override val isInterface: Boolean
|
||||
get() = psi.isInterface
|
||||
|
||||
override val isAnnotationType: Boolean
|
||||
get() = psi.isAnnotationType
|
||||
|
||||
override val isEnum: Boolean
|
||||
get() = psi.isEnum
|
||||
|
||||
override val outerClass: JavaClassImpl?
|
||||
get() {
|
||||
val outer = psi.containingClass
|
||||
return if (outer == null) null else JavaClassImpl(outer)
|
||||
}
|
||||
|
||||
override val typeParameters: List<JavaTypeParameter>
|
||||
get() = typeParameters(psi.typeParameters)
|
||||
|
||||
override val supertypes: Collection<JavaClassifierType>
|
||||
get() = classifierTypes(psi.superTypes)
|
||||
|
||||
override val methods: Collection<JavaMethod>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
// We apply distinct here because PsiClass#getMethods() can return duplicate PSI methods, for example in Lombok (see KT-11778)
|
||||
// Return type seems to be null for example for the 'clone' Groovy method generated by @AutoClone (see EA-73795)
|
||||
return methods(psi.methods.filter { method -> !method.isConstructor && method.returnType != null }).distinct()
|
||||
}
|
||||
|
||||
override val fields: Collection<JavaField>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
return fields(psi.fields.filter { field ->
|
||||
val name = field.name
|
||||
// ex. Android plugin generates LightFields for resources started from '.' (.DS_Store file etc)
|
||||
name != null && Name.isValidIdentifier(name)
|
||||
})
|
||||
}
|
||||
|
||||
override val constructors: Collection<JavaConstructor>
|
||||
get() {
|
||||
assertNotLightClass()
|
||||
// See for example org.jetbrains.plugins.scala.lang.psi.light.ScFunctionWrapper,
|
||||
// which is present in getConstructors(), but its isConstructor() returns false
|
||||
return constructors(psi.constructors.filter { method -> method.isConstructor })
|
||||
}
|
||||
|
||||
override val isAbstract: Boolean
|
||||
get() = JavaElementUtil.isAbstract(this)
|
||||
|
||||
override val isStatic: Boolean
|
||||
get() = JavaElementUtil.isStatic(this)
|
||||
|
||||
override val isFinal: Boolean
|
||||
get() = JavaElementUtil.isFinal(this)
|
||||
|
||||
override val visibility: Visibility
|
||||
get() = JavaElementUtil.getVisibility(this)
|
||||
|
||||
override val lightClassOriginKind: LightClassOriginKind?
|
||||
get() = (psi as? KtLightClassMarker)?.originKind
|
||||
|
||||
override val virtualFile: VirtualFile?
|
||||
get() = psi.containingFile?.virtualFile
|
||||
|
||||
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile in scope
|
||||
|
||||
override fun getAnnotationOwnerPsi() = psi.modifierList
|
||||
|
||||
private fun assertNotLightClass() {
|
||||
val psiClass = psi
|
||||
if (psiClass !is KtLightClassMarker) return
|
||||
|
||||
val message = "Querying members of JavaClass created for $psiClass of type ${psiClass::class.java} defined in file ${psiClass.containingFile?.virtualFile?.canonicalPath}"
|
||||
LOGGER.error(message)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = Logger.getInstance(JavaClassImpl::class.java)
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.load.kotlin
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
|
||||
class KotlinBinaryClassCache : Disposable {
|
||||
private class RequestCache {
|
||||
internal var virtualFile: VirtualFile? = null
|
||||
internal var modificationStamp: Long = 0
|
||||
internal var virtualFileKotlinClass: VirtualFileKotlinClass? = null
|
||||
|
||||
fun cache(file: VirtualFile, aClass: VirtualFileKotlinClass?): VirtualFileKotlinClass? {
|
||||
virtualFile = file
|
||||
virtualFileKotlinClass = aClass
|
||||
modificationStamp = file.modificationStamp
|
||||
|
||||
return aClass
|
||||
}
|
||||
}
|
||||
|
||||
private val cache = object : ThreadLocal<RequestCache>() {
|
||||
override fun initialValue(): RequestCache {
|
||||
return RequestCache()
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
// This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is
|
||||
// also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
|
||||
// thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
|
||||
cache.remove()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
|
||||
if (file.fileType !== JavaClassFileType.INSTANCE) return null
|
||||
|
||||
val service = ServiceManager.getService(KotlinBinaryClassCache::class.java)
|
||||
val requestCache = service.cache.get()
|
||||
|
||||
if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
|
||||
return requestCache.virtualFileKotlinClass
|
||||
}
|
||||
|
||||
val aClass = ApplicationManager.getApplication().runReadAction(Computable {
|
||||
@Suppress("DEPRECATION")
|
||||
VirtualFileKotlinClass.create(file, fileContent)
|
||||
})
|
||||
|
||||
return requestCache.cache(file, aClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.resolve.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiModifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
//import org.jetbrains.org.objectweb.asm.ModuleVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
//import org.jetbrains.org.objectweb.asm.Opcodes.ACC_TRANSITIVE
|
||||
import java.io.IOException
|
||||
|
||||
class JavaModuleInfo(
|
||||
val moduleName: String,
|
||||
val requires: List<Requires>,
|
||||
val exports: List<Exports>
|
||||
) {
|
||||
data class Requires(val moduleName: String, val isTransitive: Boolean)
|
||||
|
||||
data class Exports(val packageFqName: FqName, val toModules: List<String>)
|
||||
|
||||
override fun toString(): String =
|
||||
"Module $moduleName (${requires.size} requires, ${exports.size} exports)"
|
||||
|
||||
companion object {
|
||||
/*fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo {
|
||||
return JavaModuleInfo(
|
||||
psiJavaModule.name,
|
||||
psiJavaModule.requires.mapNotNull { statement ->
|
||||
statement.moduleName?.let { moduleName ->
|
||||
JavaModuleInfo.Requires(moduleName, statement.hasModifierProperty(PsiModifier.TRANSITIVE))
|
||||
}
|
||||
},
|
||||
psiJavaModule.exports.mapNotNull { statement ->
|
||||
statement.packageName?.let { packageName ->
|
||||
JavaModuleInfo.Exports(FqName(packageName), statement.moduleNames)
|
||||
}
|
||||
}
|
||||
)
|
||||
}*/
|
||||
|
||||
/*fun read(file: VirtualFile): JavaModuleInfo? {
|
||||
val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null }
|
||||
|
||||
var moduleName: String? = null
|
||||
val requires = arrayListOf<Requires>()
|
||||
val exports = arrayListOf<Exports>()
|
||||
|
||||
ClassReader(contents).accept(object : ClassVisitor(Opcodes.ASM6) {
|
||||
override fun visitModule(name: String, access: Int, version: String?): ModuleVisitor {
|
||||
moduleName = name
|
||||
|
||||
return object : ModuleVisitor(Opcodes.ASM6) {
|
||||
override fun visitRequire(module: String, access: Int, version: String?) {
|
||||
requires.add(Requires(module, (access and ACC_TRANSITIVE) != 0))
|
||||
}
|
||||
|
||||
override fun visitExport(packageFqName: String, access: Int, modules: Array<String>?) {
|
||||
// For some reason, '/' is the delimiter in packageFqName here
|
||||
exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG or ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return if (moduleName != null)
|
||||
JavaModuleInfo(moduleName!!, requires.compact(), exports.compact())
|
||||
else null
|
||||
}*/
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(projectTests(":compiler:cli"))
|
||||
compile(projectTests(":idea:idea-maven"))
|
||||
compile(projectTests(":j2k"))
|
||||
compile(projectTests(":idea:idea-android"))
|
||||
compile(projectTests(":jps-plugin"))
|
||||
compile(projectTests(":plugins:android-extensions-compiler"))
|
||||
compile(projectTests(":plugins:android-extensions-ide"))
|
||||
compile(projectTests(":plugins:android-extensions-jps"))
|
||||
compile(projectTests(":kotlin-annotation-processing"))
|
||||
compile(projectTests(":kotlin-allopen-compiler-plugin"))
|
||||
compile(projectTests(":kotlin-noarg-compiler-plugin"))
|
||||
compile(projectTests(":kotlin-sam-with-receiver-compiler-plugin"))
|
||||
compile(projectTests(":generators:test-generator"))
|
||||
// testCompileOnly(intellijDep("jps-build-test"))
|
||||
testCompileOnly(project(":kotlin-reflect-api"))
|
||||
testRuntime(intellijDep()) { includeJars("idea_rt") }
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateTestsKt")
|
||||
|
||||
val generateProtoBuf by generator("org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufKt")
|
||||
val generateProtoBufCompare by generator("org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufCompare")
|
||||
|
||||
val generateGradleOptions by generator("org.jetbrains.kotlin.generators.arguments.GenerateGradleOptionsKt")
|
||||
|
||||
testsJar()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":idea"))
|
||||
compileOnly(project(":idea:idea-maven"))
|
||||
compileOnly(project(":idea:idea-gradle"))
|
||||
compileOnly(project(":idea:idea-jvm"))
|
||||
|
||||
compile(intellijDep())
|
||||
|
||||
runtimeOnly(files(toolsJar()))
|
||||
}
|
||||
|
||||
val ideaPluginDir: File by rootProject.extra
|
||||
val ideaSandboxDir: File by rootProject.extra
|
||||
|
||||
runIdeTask("runIde", ideaPluginDir, ideaSandboxDir) {
|
||||
dependsOn(":dist", ":ideaPlugin")
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(project(":core:descriptors"))
|
||||
compile(project(":core:descriptors.jvm"))
|
||||
compile(project(":compiler:backend"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
compile(project(":js:js.frontend"))
|
||||
compile(project(":js:js.serializer"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
compile(project(":compiler:daemon-common"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
compile(project(":kotlin-compiler-runner")) { isTransitive = false }
|
||||
compile(project(":compiler:plugin-api"))
|
||||
compile(project(":eval4j"))
|
||||
compile(project(":j2k"))
|
||||
compile(project(":idea:formatter"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":idea:ide-common"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
compile(project(":plugins:uast-kotlin"))
|
||||
compile(project(":plugins:uast-kotlin-idea"))
|
||||
compile(project(":kotlin-script-util")) { isTransitive = false }
|
||||
|
||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||
compile(commonDep("org.jetbrains", "markdown"))
|
||||
|
||||
compileOnly(project(":kotlin-daemon-client"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(commonDep("com.google.code.findbugs", "jsr305"))
|
||||
compileOnly(intellijPluginDep("IntelliLang"))
|
||||
compileOnly(intellijPluginDep("copyright"))
|
||||
compileOnly(intellijPluginDep("properties"))
|
||||
compileOnly(intellijPluginDep("java-i18n"))
|
||||
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-gradle")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-maven")) { isTransitive = false }
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(projectDist(":kotlin-preloader"))
|
||||
|
||||
testCompile(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false }
|
||||
|
||||
testRuntime(project(":plugins:android-extensions-compiler"))
|
||||
testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false }
|
||||
testRuntime(project(":allopen-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-allopen-compiler-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-noarg-compiler-plugin"))
|
||||
testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false }
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-android")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:lint")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:uast-kotlin"))
|
||||
|
||||
(rootProject.extra["compilerModules"] as Array<String>).forEach {
|
||||
testRuntime(project(it))
|
||||
}
|
||||
|
||||
testCompile(intellijPluginDep("IntelliLang"))
|
||||
testCompile(intellijPluginDep("copyright"))
|
||||
testCompile(intellijPluginDep("properties"))
|
||||
testCompile(intellijPluginDep("java-i18n"))
|
||||
testCompileOnly(intellijDep())
|
||||
testCompileOnly(commonDep("com.google.code.findbugs", "jsr305"))
|
||||
testCompileOnly(intellijPluginDep("gradle"))
|
||||
testCompileOnly(intellijPluginDep("Groovy"))
|
||||
//testCompileOnly(intellijPluginDep("maven"))
|
||||
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
java.srcDirs("idea-completion/src",
|
||||
"idea-live-templates/src",
|
||||
"idea-repl/src")
|
||||
resources.srcDirs("idea-repl/src").apply { include("META-INF/**") }
|
||||
}
|
||||
"test" {
|
||||
projectDefault()
|
||||
java.srcDirs(
|
||||
"idea-completion/tests",
|
||||
"idea-live-templates/tests")
|
||||
}
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
classesDirsArtifact()
|
||||
configureInstrumentation()
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":idea:ide-common"))
|
||||
compile(project(":idea:idea-gradle"))
|
||||
|
||||
compile(androidDxJar())
|
||||
|
||||
compileOnly(project(":kotlin-android-extensions-runtime"))
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("android"))
|
||||
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":plugins:lint")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":idea:idea-gradle"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testCompile(intellijDep())
|
||||
testCompile(intellijPluginDep("properties"))
|
||||
testCompileOnly(intellijPluginDep("android"))
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:kapt3-idea"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
testRuntime(intellijPluginDep("copyright"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("IntelliLang"))
|
||||
testRuntime(intellijPluginDep("java-decompiler"))
|
||||
testRuntime(intellijPluginDep("java-i18n"))
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
useAndroidSdk()
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.android
|
||||
|
||||
import com.android.tools.idea.npw.template.ConvertJavaToKotlinProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator
|
||||
import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator
|
||||
import org.jetbrains.kotlin.idea.configuration.getCanBeConfiguredModules
|
||||
import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
|
||||
|
||||
class ConvertJavaToKotlinProviderImpl : ConvertJavaToKotlinProvider {
|
||||
override fun configureKotlin(project: Project) {
|
||||
val configurator = KotlinProjectConfigurator.EP_NAME.findExtension(KotlinAndroidGradleModuleConfigurator::class.java)
|
||||
val nonConfiguredModules = getCanBeConfiguredModules(project, configurator)
|
||||
configurator.configureSilently(project, nonConfiguredModules, bundledRuntimeVersion())
|
||||
}
|
||||
|
||||
override fun getKotlinVersion(): String {
|
||||
return bundledRuntimeVersion()
|
||||
}
|
||||
|
||||
override fun convertToKotlin(project: Project, files: List<PsiJavaFile>): List<PsiFile> {
|
||||
return JavaToKotlinAction.convertFiles(files, project, askExternalCodeProcessing = false)
|
||||
}
|
||||
}
|
||||
-1
@@ -25,7 +25,6 @@ import com.android.resources.ResourceType;
|
||||
import com.android.tools.idea.configurations.Configuration;
|
||||
import com.android.tools.idea.configurations.ConfigurationManager;
|
||||
import com.android.tools.idea.res.AppResourceRepository;
|
||||
import com.android.tools.idea.res.LocalResourceRepository;
|
||||
import com.android.tools.idea.res.ResourceHelper;
|
||||
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
|
||||
import com.android.utils.XmlUtils;
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.JavaModuleModel
|
||||
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys
|
||||
import com.android.tools.idea.io.FilePaths
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import org.jetbrains.kotlin.idea.configuration.detectPlatformByPlugin
|
||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.libraryKind
|
||||
import java.io.File
|
||||
|
||||
class KotlinAndroidGradleLibraryDataService : AbstractProjectDataService<JavaModuleModel, Void>() {
|
||||
override fun getTargetDataKey() = AndroidProjectKeys.JAVA_MODULE_MODEL
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<JavaModuleModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
for (dataNode in toImport) {
|
||||
val targetLibraryKind = detectPlatformByPlugin(dataNode.parent as DataNode<ModuleData>)?.libraryKind
|
||||
if (targetLibraryKind != null) {
|
||||
for (dep in dataNode.data.jarLibraryDependencies) {
|
||||
val library = modelsProvider.findLibraryByBinaryPath(dep.binaryPath) as LibraryEx? ?: continue
|
||||
if (library.kind == null) {
|
||||
val model = modelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx
|
||||
detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IdeModifiableModelsProvider.findLibraryByBinaryPath(path: File?): Library? {
|
||||
if (path == null) return null
|
||||
val url = FilePaths.pathToIdeaUrl(path)
|
||||
return allLibraries.firstOrNull { url in getModifiableLibraryModel(it).getUrls(OrderRootType.CLASSES) }
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
|
||||
import com.android.tools.idea.gradle.project.sync.setup.post.ModuleSetupStep
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.configuration.compilerArgumentsBySourceSet
|
||||
import org.jetbrains.kotlin.idea.configuration.configureFacetByCompilerArguments
|
||||
import org.jetbrains.kotlin.idea.configuration.sourceSetName
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
|
||||
class KotlinAndroidModuleSetupStep : ModuleSetupStep() {
|
||||
override fun setUpModule(module: Module, progressIndicator: ProgressIndicator?) {
|
||||
val facet = AndroidFacet.getInstance(module) ?: return
|
||||
val androidModel = AndroidModuleModel.get(facet) ?: return
|
||||
val sourceSetName = androidModel.selectedVariant.name
|
||||
if (module.sourceSetName == sourceSetName) return
|
||||
val argsInfo = module.compilerArgumentsBySourceSet?.get(sourceSetName) ?: return
|
||||
val kotlinFacet = KotlinFacet.get(module) ?: return
|
||||
module.sourceSetName = sourceSetName
|
||||
configureFacetByCompilerArguments(kotlinFacet, argsInfo, null)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.android.configure
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.Key
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler
|
||||
import org.jetbrains.kotlin.idea.configuration.configureFacetByGradleModule
|
||||
|
||||
class KotlinGradleAndroidModuleModelProjectDataService : AbstractProjectDataService<AndroidModuleModel, Void>() {
|
||||
companion object {
|
||||
val KEY = Key<AndroidModuleModel>(AndroidModuleModel::class.qualifiedName!!, 0)
|
||||
}
|
||||
|
||||
override fun getTargetDataKey() = KEY
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<AndroidModuleModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
super.postProcess(toImport, projectData, project, modelsProvider)
|
||||
for (moduleModelNode in toImport) {
|
||||
val moduleNode = ExternalSystemApiUtil.findParent(moduleModelNode, ProjectKeys.MODULE) ?: continue
|
||||
val moduleData = moduleNode.data
|
||||
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
|
||||
val sourceSetName = moduleModelNode.data.selectedVariant.name
|
||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
|
||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.android.navigation
|
||||
|
||||
import org.jetbrains.android.util.AndroidResourceUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.android.util.AndroidUtils
|
||||
import com.android.SdkConstants
|
||||
import org.jetbrains.android.augment.AndroidPsiElementFinder
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
fun getReferenceExpression(element: PsiElement?): KtSimpleNameExpression? {
|
||||
return PsiTreeUtil.getParentOfType<KtSimpleNameExpression>(element, KtSimpleNameExpression::class.java)
|
||||
}
|
||||
|
||||
// given 'R.a.b' returns info for all three parts of the expression 'a', 'b', 'R'
|
||||
fun getInfo(
|
||||
referenceExpression: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val info = getReferredInfo(referenceExpression, facet)
|
||||
if (info != null) return info
|
||||
|
||||
val topMostQualified = referenceExpression.getParentQualified().getParentQualified() ?: return null
|
||||
val selectorCandidate = topMostQualified.selectorExpression as? KtSimpleNameExpression ?: return null
|
||||
return getReferredInfo(selectorCandidate, facet)
|
||||
}
|
||||
|
||||
private fun KtExpression?.getParentQualified(): KtDotQualifiedExpression? {
|
||||
return this?.parent as? KtDotQualifiedExpression
|
||||
}
|
||||
|
||||
// returns info if passed expression is 'b' in 'R.a.b'
|
||||
private fun getReferredInfo(
|
||||
lastPart: KtSimpleNameExpression,
|
||||
facet: AndroidFacet
|
||||
): AndroidResourceUtil.MyReferredResourceFieldInfo? {
|
||||
val resFieldName = lastPart.getReferencedName()
|
||||
if (resFieldName.isEmpty()) return null
|
||||
|
||||
val middlePart = getReceiverAsSimpleNameExpression(lastPart) ?: return null
|
||||
|
||||
val resClassName = middlePart.getReferencedName()
|
||||
if (resClassName.isEmpty()) return null
|
||||
|
||||
val firstPart = getReceiverAsSimpleNameExpression(middlePart) ?: return null
|
||||
|
||||
val resolvedClass = firstPart.mainReference.resolve() as? PsiClass ?: return null
|
||||
|
||||
//the following code is copied from
|
||||
// org.jetbrains.android.util.AndroidResourceUtil.getReferredResourceOrManifestField
|
||||
// (org.jetbrains.android.facet.AndroidFacet, com.intellij.psi.PsiReferenceExpression, java.lang.String, boolean)
|
||||
val classShortName = resolvedClass.name
|
||||
|
||||
val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == classShortName
|
||||
|
||||
if (!fromManifest && AndroidUtils.R_CLASS_NAME != classShortName) {
|
||||
return null
|
||||
}
|
||||
val qName = resolvedClass.qualifiedName
|
||||
|
||||
if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) {
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, true, false)
|
||||
}
|
||||
val containingFile = resolvedClass.containingFile ?: return null
|
||||
|
||||
val isFromCorrectFile =
|
||||
if (fromManifest) AndroidResourceUtil.isManifestJavaFile(facet, containingFile)
|
||||
else AndroidResourceUtil.isRJavaFile(facet, containingFile)
|
||||
|
||||
if (!isFromCorrectFile) {
|
||||
return null
|
||||
}
|
||||
|
||||
return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, false, fromManifest)
|
||||
}
|
||||
|
||||
private fun getReceiverAsSimpleNameExpression(exp: KtSimpleNameExpression): KtSimpleNameExpression? {
|
||||
val receiver = exp.getReceiverExpression()
|
||||
return when (receiver) {
|
||||
is KtSimpleNameExpression -> {
|
||||
receiver
|
||||
}
|
||||
is KtDotQualifiedExpression -> {
|
||||
receiver.selectorExpression as? KtSimpleNameExpression
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.android.quickfix
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
|
||||
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.statement.KotlinIfSurrounder
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
|
||||
class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix {
|
||||
|
||||
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
|
||||
val targetExpression = getTargetExpression(startElement)
|
||||
val project = targetExpression?.project ?: return
|
||||
val editor = targetExpression.findExistingEditor() ?: return
|
||||
|
||||
val file = targetExpression.containingFile
|
||||
val documentManager = PsiDocumentManager.getInstance(project)
|
||||
val document = documentManager.getDocument(file) ?: return
|
||||
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) {
|
||||
return
|
||||
}
|
||||
|
||||
val surrounder = getSurrounder(targetExpression, "\"VERSION.SDK_INT < ${getVersionField(api, false)}\"")
|
||||
val conditionRange = surrounder.surroundElements(project, editor, arrayOf(targetExpression)) ?: return
|
||||
val conditionText = "android.os.Build.VERSION.SDK_INT >= ${getVersionField(api, true)}"
|
||||
document.replaceString(conditionRange.startOffset, conditionRange.endOffset, conditionText)
|
||||
documentManager.commitDocument(document)
|
||||
|
||||
ShortenReferences.DEFAULT.process(documentManager.getPsiFile(document) as KtFile,
|
||||
conditionRange.startOffset,
|
||||
conditionRange.startOffset + conditionText.length)
|
||||
}
|
||||
|
||||
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
|
||||
getTargetExpression(startElement) != null
|
||||
|
||||
override fun getName(): String = "Surround with if (VERSION.SDK_INT >= VERSION_CODES.${getVersionField(api, false)}) { ... }"
|
||||
|
||||
private fun getTargetExpression(element: PsiElement): KtElement? {
|
||||
var current = PsiTreeUtil.getParentOfType(element, KtExpression::class.java)
|
||||
while (current != null) {
|
||||
if (current.parent is KtBlockExpression ||
|
||||
current.parent is KtContainerNode ||
|
||||
current.parent is KtWhenEntry ||
|
||||
current.parent is KtFunction ||
|
||||
current.parent is KtPropertyAccessor ||
|
||||
current.parent is KtProperty ||
|
||||
current.parent is KtReturnExpression ||
|
||||
current.parent is KtDestructuringDeclaration) {
|
||||
break
|
||||
}
|
||||
current = PsiTreeUtil.getParentOfType(current, KtExpression::class.java, true)
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder {
|
||||
val used = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)[BindingContext.USED_AS_EXPRESSION, element] ?: false
|
||||
return if (used) {
|
||||
object : KotlinIfSurrounder() {
|
||||
override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}"
|
||||
}
|
||||
} else {
|
||||
KotlinIfSurrounder()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compileOnly(project(":idea"))
|
||||
compileOnly(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("gradle"))
|
||||
compileOnly(intellijPluginDep("Groovy"))
|
||||
compileOnly(intellijPluginDep("junit"))
|
||||
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
|
||||
testCompile(intellijPluginDep("gradle"))
|
||||
testCompileOnly(intellijPluginDep("Groovy"))
|
||||
testCompileOnly(intellijDep())
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:kapt3-idea"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
// TODO: the order of the plugins matters here, consider avoiding order-dependency
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
resources.srcDir("res").apply { include("**") }
|
||||
}
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
useAndroidSdk()
|
||||
}
|
||||
|
||||
configureInstrumentation()
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.idea.configuration
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.LibraryData
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.isQualifiedModuleNamesEnabled
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryImpl
|
||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
import org.jetbrains.kotlin.gradle.ArgsInfo
|
||||
import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet
|
||||
import org.jetbrains.kotlin.idea.facet.*
|
||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
var Module.compilerArgumentsBySourceSet
|
||||
by UserDataProperty(Key.create<CompilerArgumentsBySourceSet>("CURRENT_COMPILER_ARGUMENTS"))
|
||||
|
||||
var Module.sourceSetName
|
||||
by UserDataProperty(Key.create<String>("SOURCE_SET_NAME"))
|
||||
|
||||
interface GradleProjectImportHandler {
|
||||
companion object : ProjectExtensionDescriptor<GradleProjectImportHandler>(
|
||||
"org.jetbrains.kotlin.gradleProjectImportHandler",
|
||||
GradleProjectImportHandler::class.java
|
||||
)
|
||||
|
||||
fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode<GradleSourceSetData>)
|
||||
fun importByModule(facet: KotlinFacet, moduleNode: DataNode<ModuleData>)
|
||||
}
|
||||
|
||||
class KotlinGradleSourceSetDataService : AbstractProjectDataService<GradleSourceSetData, Void>() {
|
||||
override fun getTargetDataKey() = GradleSourceSetData.KEY
|
||||
|
||||
override fun postProcess(
|
||||
toImport: Collection<DataNode<GradleSourceSetData>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
for (sourceSetNode in toImport) {
|
||||
val sourceSetData = sourceSetNode.data
|
||||
val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue
|
||||
|
||||
val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue
|
||||
val sourceSetName = sourceSetNode.data.id.let { it.substring(it.lastIndexOf(':') + 1) }
|
||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue
|
||||
GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinGradleProjectDataService : AbstractProjectDataService<ModuleData, Void>() {
|
||||
override fun getTargetDataKey() = ProjectKeys.MODULE
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<ModuleData>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
for (moduleNode in toImport) {
|
||||
// If source sets are present, configure facets in the their modules
|
||||
if (ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY).isNotEmpty()) continue
|
||||
|
||||
val moduleData = moduleNode.data
|
||||
val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue
|
||||
val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue
|
||||
GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinGradleLibraryDataService : AbstractProjectDataService<LibraryData, Void>() {
|
||||
override fun getTargetDataKey() = ProjectKeys.LIBRARY
|
||||
|
||||
override fun postProcess(
|
||||
toImport: MutableCollection<DataNode<LibraryData>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
if (toImport.isEmpty()) return
|
||||
val projectDataNode = toImport.first().parent!!
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val moduleDataNodes = projectDataNode.children.filter { it.data is ModuleData } as List<DataNode<ModuleData>>
|
||||
val anyNonJvmModules = moduleDataNodes.any { detectPlatformByPlugin(it)?.takeIf { it !is TargetPlatformKind.Jvm } != null }
|
||||
for (libraryDataNode in toImport) {
|
||||
val ideLibrary = modelsProvider.findIdeLibrary(libraryDataNode.data) ?: continue
|
||||
|
||||
val modifiableModel = modelsProvider.getModifiableLibraryModel(ideLibrary) as LibraryEx.ModifiableModelEx
|
||||
if (anyNonJvmModules || ideLibrary.name?.looksAsNonJvmLibraryName() == true) {
|
||||
detectLibraryKind(modifiableModel.getFiles(OrderRootType.CLASSES))?.let { modifiableModel.kind = it }
|
||||
} else if (ideLibrary is LibraryImpl && (ideLibrary.kind === JSLibraryKind || ideLibrary.kind === CommonLibraryKind)) {
|
||||
resetLibraryKind(modifiableModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.looksAsNonJvmLibraryName() = nonJvmSuffixes.any { it in this }
|
||||
|
||||
private fun resetLibraryKind(modifiableModel: LibraryEx.ModifiableModelEx) {
|
||||
try {
|
||||
val cls = LibraryImpl::class.java
|
||||
// Don't use name-based lookup because field names are scrambled in IDEA Ultimate
|
||||
for (field in cls.declaredFields) {
|
||||
if (field.type == PersistentLibraryKind::class.java) {
|
||||
field.isAccessible = true
|
||||
field.set(modifiableModel, null)
|
||||
return
|
||||
}
|
||||
}
|
||||
LOG.info("Could not find field of type PersistentLibraryKind in LibraryImpl.class")
|
||||
} catch (e: Exception) {
|
||||
LOG.info("Failed to reset library kind", e)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LOG = Logger.getInstance(KotlinGradleLibraryDataService::class.java)
|
||||
|
||||
val nonJvmSuffixes = listOf("-common", "-js", "-native", "-kjsm")
|
||||
}
|
||||
}
|
||||
|
||||
fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
||||
return when (moduleNode.platformPluginId) {
|
||||
"kotlin-platform-jvm" -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
||||
"kotlin-platform-js" -> TargetPlatformKind.JavaScript
|
||||
"kotlin-platform-common" -> TargetPlatformKind.Common
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
||||
val detectedPlatforms =
|
||||
mavenLibraryIdToPlatform.entries
|
||||
.filter { moduleNode.getResolvedKotlinStdlibVersionByModuleData(listOf(it.key)) != null }
|
||||
.map { it.value }.distinct()
|
||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
||||
}
|
||||
|
||||
fun configureFacetByGradleModule(
|
||||
moduleNode: DataNode<ModuleData>,
|
||||
sourceSetName: String?,
|
||||
ideModule: Module,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
): KotlinFacet? {
|
||||
if (!moduleNode.isResolved) return null
|
||||
|
||||
if (!moduleNode.hasKotlinPlugin) {
|
||||
val facetModel = modelsProvider.getModifiableFacetModel(ideModule)
|
||||
val facet = facetModel.getFacetByType(KotlinFacetType.TYPE_ID)
|
||||
if (facet != null) {
|
||||
facetModel.removeFacet(facet)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val compilerVersion = moduleNode.findAll(BuildScriptClasspathData.KEY).firstOrNull()?.data?.let(::findKotlinPluginVersion)
|
||||
?: return null
|
||||
val platformKind = detectPlatformByPlugin(moduleNode) ?: detectPlatformByLibrary(moduleNode)
|
||||
|
||||
val coroutinesProperty = CoroutineSupport.byCompilerArgument(
|
||||
moduleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project)
|
||||
)
|
||||
|
||||
val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false)
|
||||
kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider)
|
||||
|
||||
ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet
|
||||
ideModule.sourceSetName = sourceSetName
|
||||
|
||||
val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main")
|
||||
if (argsInfo != null) {
|
||||
configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider)
|
||||
}
|
||||
|
||||
with(kotlinFacet.configuration.settings) {
|
||||
implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetName, ideModule.project)
|
||||
productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main")
|
||||
testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test")
|
||||
}
|
||||
|
||||
kotlinFacet.noVersionAutoAdvance()
|
||||
|
||||
return kotlinFacet
|
||||
}
|
||||
|
||||
fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) {
|
||||
val currentCompilerArguments = argsInfo.currentArguments
|
||||
val defaultCompilerArguments = argsInfo.defaultArguments
|
||||
val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) }
|
||||
if (currentCompilerArguments.isNotEmpty()) {
|
||||
parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider)
|
||||
}
|
||||
adjustClasspath(kotlinFacet, dependencyClasspath)
|
||||
}
|
||||
|
||||
private fun getImplementedModuleNames(moduleNode: DataNode<ModuleData>, sourceSetName: String?, project: Project): List<String> {
|
||||
val baseModuleNames = moduleNode.implementedModuleNames
|
||||
if (baseModuleNames.isEmpty() || sourceSetName == null) return baseModuleNames
|
||||
val delimiter = if(isQualifiedModuleNamesEnabled(project)) "." else "_"
|
||||
return baseModuleNames.map { "$it$delimiter$sourceSetName" }
|
||||
}
|
||||
|
||||
private fun getExplicitOutputPath(moduleNode: DataNode<ModuleData>, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? {
|
||||
if (platformKind !== TargetPlatformKind.JavaScript) return null
|
||||
val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null
|
||||
return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile
|
||||
}
|
||||
|
||||
private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List<String>) {
|
||||
if (dependencyClasspath.isEmpty()) return
|
||||
val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return
|
||||
val fullClasspath = arguments.classpath?.split(File.pathSeparator) ?: emptyList()
|
||||
if (fullClasspath.isEmpty()) return
|
||||
val newClasspath = fullClasspath - dependencyClasspath
|
||||
arguments.classpath = if (newClasspath.isNotEmpty()) newClasspath.joinToString(File.pathSeparator) else null
|
||||
}
|
||||
|
||||
private val gradlePropertyFiles = listOf("local.properties", "gradle.properties")
|
||||
|
||||
private fun findKotlinCoroutinesProperty(project: Project): String {
|
||||
for (propertyFileName in gradlePropertyFiles) {
|
||||
val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue
|
||||
val properties = Properties()
|
||||
properties.load(propertyFile.inputStream)
|
||||
properties.getProperty("kotlin.coroutines")?.let { return it }
|
||||
}
|
||||
|
||||
return CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState)
|
||||
}
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright 2000-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.codeInsight.CodeInsightUtilCore
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
||||
import com.intellij.ide.actions.OpenFileAction
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.WritingAccessProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
|
||||
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
|
||||
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
|
||||
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
|
||||
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||
val module = moduleSourceRootGroup.baseModule
|
||||
if (!isApplicable(module)) {
|
||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||
}
|
||||
|
||||
if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
|
||||
return ConfigureKotlinStatus.CONFIGURED
|
||||
}
|
||||
|
||||
val buildFiles = runReadAction {
|
||||
listOf(
|
||||
module.getBuildScriptPsiFile(),
|
||||
module.project.getTopLevelBuildScriptPsiFile()
|
||||
).filterNotNull()
|
||||
}
|
||||
|
||||
if (buildFiles.isEmpty()) {
|
||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||
}
|
||||
|
||||
if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) {
|
||||
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||
}
|
||||
|
||||
return ConfigureKotlinStatus.BROKEN
|
||||
}
|
||||
|
||||
private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean {
|
||||
return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
||||
.filterIsInstance<KotlinWithGradleConfigurator>()
|
||||
.any { it.isFileConfigured(this) }
|
||||
}
|
||||
|
||||
protected open fun isApplicable(module: Module): Boolean =
|
||||
module.getBuildSystemType() == Gradle
|
||||
|
||||
protected open fun getMinimumSupportedVersion() = "1.0.0"
|
||||
|
||||
private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName)
|
||||
|
||||
@JvmSuppressWildcards
|
||||
override fun configure(project: Project, excludeModules: Collection<Module>) {
|
||||
val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion())
|
||||
|
||||
dialog.show()
|
||||
if (!dialog.isOK) return
|
||||
|
||||
val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion)
|
||||
collector.showNotification()
|
||||
}
|
||||
|
||||
fun configureSilently(project: Project, modules: List<Module>, version: String): NotificationMessageCollector {
|
||||
return project.executeCommand("Configure Kotlin") {
|
||||
val collector = createConfigureKotlinNotificationCollector(project)
|
||||
val changedFiles = configureWithVersion(project, modules, version, collector)
|
||||
|
||||
for (file in changedFiles) {
|
||||
OpenFileAction.openFile(file.virtualFile, project)
|
||||
}
|
||||
collector
|
||||
}
|
||||
}
|
||||
|
||||
fun configureWithVersion(
|
||||
project: Project,
|
||||
modulesToConfigure: List<Module>,
|
||||
kotlinVersion: String,
|
||||
collector: NotificationMessageCollector
|
||||
): HashSet<PsiFile> {
|
||||
val filesToOpen = HashSet<PsiFile>()
|
||||
val buildScript = project.getTopLevelBuildScriptPsiFile()
|
||||
if (buildScript != null && canConfigureFile(buildScript)) {
|
||||
val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector)
|
||||
if (isModified) {
|
||||
filesToOpen.add(buildScript)
|
||||
}
|
||||
}
|
||||
|
||||
for (module in modulesToConfigure) {
|
||||
val file = module.getBuildScriptPsiFile()
|
||||
if (file != null && canConfigureFile(file)) {
|
||||
configureModule(module, file, false, kotlinVersion, collector, filesToOpen)
|
||||
} else {
|
||||
showErrorMessage(project, "Cannot find build.gradle file for module " + module.name)
|
||||
}
|
||||
}
|
||||
return filesToOpen
|
||||
}
|
||||
|
||||
open fun configureModule(
|
||||
module: Module,
|
||||
file: PsiFile,
|
||||
isTopLevelProjectFile: Boolean,
|
||||
version: String,
|
||||
collector: NotificationMessageCollector,
|
||||
filesToOpen: MutableCollection<PsiFile>
|
||||
) {
|
||||
val isModified = configureBuildScript(file, isTopLevelProjectFile, version, collector)
|
||||
if (isModified) {
|
||||
filesToOpen.add(file)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean {
|
||||
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
|
||||
val jvmTarget = getJvmTarget(sdk, version)
|
||||
return getManipulator(file).configureModuleBuildScript(
|
||||
kotlinPluginName,
|
||||
getStdlibArtifactName(sdk, version),
|
||||
version,
|
||||
jvmTarget
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version)
|
||||
|
||||
protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
|
||||
|
||||
protected abstract val kotlinPluginName: String
|
||||
|
||||
protected open fun addElementsToFile(
|
||||
file: PsiFile,
|
||||
isTopLevelProjectFile: Boolean,
|
||||
version: String
|
||||
): Boolean {
|
||||
if (!isTopLevelProjectFile) {
|
||||
var wasModified = configureProjectFile(file, version)
|
||||
wasModified = wasModified or configureModuleBuildScript(file, version)
|
||||
return wasModified
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun configureBuildScript(
|
||||
file: PsiFile,
|
||||
isTopLevelProjectFile: Boolean,
|
||||
version: String,
|
||||
collector: NotificationMessageCollector
|
||||
): Boolean {
|
||||
val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) {
|
||||
val isModified = addElementsToFile(file, isTopLevelProjectFile, version)
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file)
|
||||
isModified
|
||||
}
|
||||
|
||||
val virtualFile = file.virtualFile
|
||||
if (virtualFile != null && isModified) {
|
||||
collector.addMessage(virtualFile.path + " was modified")
|
||||
}
|
||||
return isModified
|
||||
}
|
||||
|
||||
override fun updateLanguageVersion(
|
||||
module: Module,
|
||||
languageVersion: String?,
|
||||
apiVersion: String?,
|
||||
requiredStdlibVersion: ApiVersion,
|
||||
forTests: Boolean
|
||||
) {
|
||||
val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion ->
|
||||
runtimeVersion < requiredStdlibVersion
|
||||
} ?: false
|
||||
|
||||
if (runtimeUpdateRequired) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " +
|
||||
"Please update the version in your build script.",
|
||||
"Update Language Version"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val element = changeLanguageVersion(module, languageVersion, apiVersion, forTests)
|
||||
|
||||
element?.let {
|
||||
OpenFileDescriptor(module.project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
|
||||
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
|
||||
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
|
||||
|
||||
if (runtimeUpdateRequired) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
|
||||
"Please update the version in your build script.",
|
||||
ChangeCoroutineSupportFix.getFixText(state)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state))
|
||||
if (element != null) {
|
||||
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addLibraryDependency(
|
||||
module: Module,
|
||||
element: PsiElement,
|
||||
library: ExternalLibraryDescriptor,
|
||||
libraryJarDescriptors: List<LibraryJarDescriptor>
|
||||
) {
|
||||
val scope = OrderEntryFix.suggestScopeByLocation(module, element)
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(module, scope, library)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) {
|
||||
is KtFile -> KotlinBuildScriptManipulator(file)
|
||||
is GroovyFile -> GroovyBuildScriptManipulator(file)
|
||||
else -> error("Unknown build script file type!")
|
||||
}
|
||||
|
||||
val GROUP_ID = "org.jetbrains.kotlin"
|
||||
val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
|
||||
|
||||
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
|
||||
|
||||
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
|
||||
|
||||
fun getGroovyDependencySnippet(artifactName: String, scope: String) =
|
||||
"$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
|
||||
|
||||
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
||||
|
||||
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
|
||||
val buildScript = module.getBuildScriptPsiFile() ?: return
|
||||
if (!canConfigureFile(buildScript)) {
|
||||
return
|
||||
}
|
||||
|
||||
getManipulator(buildScript)
|
||||
.addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, module.getBuildSystemType() == AndroidGradle)
|
||||
|
||||
buildScript.virtualFile?.let {
|
||||
createConfigureKotlinNotificationCollector(buildScript.project)
|
||||
.addMessage(it.path + " was modified")
|
||||
.showNotification()
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) {
|
||||
getManipulator(it).changeCoroutineConfiguration(coroutineOption)
|
||||
}
|
||||
|
||||
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) =
|
||||
changeBuildGradle(module) { buildScriptFile ->
|
||||
val manipulator = getManipulator(buildScriptFile)
|
||||
var result: PsiElement? = null
|
||||
if (languageVersion != null) {
|
||||
result = manipulator.changeLanguageVersion(languageVersion, forTests)
|
||||
}
|
||||
|
||||
if (apiVersion != null) {
|
||||
result = manipulator.changeApiVersion(apiVersion, forTests)
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
|
||||
val buildScriptFile = module.getBuildScriptPsiFile()
|
||||
if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
|
||||
return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) {
|
||||
body(buildScriptFile)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getKotlinStdlibVersion(module: Module): String? {
|
||||
return module.getBuildScriptPsiFile()?.let {
|
||||
getManipulator(it).getKotlinStdlibVersion()
|
||||
}
|
||||
}
|
||||
|
||||
fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version)
|
||||
|
||||
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
||||
|
||||
private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project)
|
||||
|
||||
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) }
|
||||
|
||||
private fun Module.getBuildScriptFile(): File? {
|
||||
val moduleDir = File(moduleFilePath).parent
|
||||
findBuildGradleFile(moduleDir)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
|
||||
findBuildGradleFile(root.path)?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
|
||||
findBuildGradleFile(externalProjectPath)?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findBuildGradleFile(path: String): File? =
|
||||
File(path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME).takeIf { it.exists() }
|
||||
?: File(path + "/" + KOTLIN_BUILD_SCRIPT_NAME).takeIf { it.exists() }
|
||||
|
||||
private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
|
||||
PsiManager.getInstance(project).findFile(it)
|
||||
}
|
||||
|
||||
private fun showErrorMessage(project: Project, message: String?) {
|
||||
Messages.showErrorDialog(
|
||||
project,
|
||||
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
|
||||
(if (message != null) message + "<br/>" else "") +
|
||||
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
|
||||
"Configure Kotlin-Gradle Plugin"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1912
File diff suppressed because it is too large
Load Diff
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.framework;
|
||||
|
||||
import com.intellij.framework.library.LibraryVersionProperties;
|
||||
import com.intellij.openapi.roots.libraries.LibraryPresentationProvider;
|
||||
import com.intellij.openapi.roots.libraries.LibraryProperties;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaRuntimePresentationProvider extends LibraryPresentationProvider<LibraryVersionProperties> {
|
||||
public static JavaRuntimePresentationProvider getInstance() {
|
||||
return LibraryPresentationProvider.EP_NAME.findExtension(JavaRuntimePresentationProvider.class);
|
||||
}
|
||||
|
||||
protected JavaRuntimePresentationProvider() {
|
||||
super(JavaRuntimeLibraryDescription.Companion.getKOTLIN_JAVA_RUNTIME_KIND());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Icon getIcon(@Nullable LibraryVersionProperties properties) {
|
||||
return KotlinIcons.SMALL_LOGO;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LibraryVersionProperties detect(@NotNull List<VirtualFile> classesRoots) {
|
||||
String version = JavaRuntimeDetectionUtil.getJavaRuntimeVersion(classesRoots);
|
||||
return version == null ? null : new LibraryVersionProperties(version);
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2000-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.quickfix
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
|
||||
import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator
|
||||
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
sealed class ChangeCoroutineSupportFix(
|
||||
element: PsiElement,
|
||||
protected val coroutineSupport: LanguageFeature.State
|
||||
) : KotlinQuickFixAction<PsiElement>(element) {
|
||||
protected val coroutineSupportEnabled: Boolean
|
||||
get() = coroutineSupport == LanguageFeature.State.ENABLED || coroutineSupport == LanguageFeature.State.ENABLED_WITH_WARNING
|
||||
|
||||
class InModule(element: PsiElement, coroutineSupport: LanguageFeature.State) : ChangeCoroutineSupportFix(element, coroutineSupport) {
|
||||
override fun getText() = "${super.getText()} in the current module"
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return
|
||||
|
||||
findApplicableConfigurator(module).changeCoroutineConfiguration(module, coroutineSupport)
|
||||
}
|
||||
}
|
||||
|
||||
class InProject(element: PsiElement, coroutineSupport: LanguageFeature.State) : ChangeCoroutineSupportFix(element, coroutineSupport) {
|
||||
override fun getText() = "${super.getText()} in the project"
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
if (coroutineSupportEnabled) {
|
||||
if (!checkUpdateRuntime(project, LanguageFeature.Coroutines.sinceApiVersion)) return
|
||||
}
|
||||
|
||||
KotlinCommonCompilerArgumentsHolder.getInstance(project).update {
|
||||
coroutinesState = when (coroutineSupport) {
|
||||
LanguageFeature.State.ENABLED -> CommonCompilerArguments.ENABLE
|
||||
LanguageFeature.State.ENABLED_WITH_WARNING -> CommonCompilerArguments.WARN
|
||||
LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> CommonCompilerArguments.ERROR
|
||||
}
|
||||
}
|
||||
ProjectRootManagerEx.getInstanceEx(project).makeRootsChange({}, false, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getFamilyName() = "Enable/Disable coroutine support"
|
||||
|
||||
override fun getText(): String {
|
||||
return getFixText(coroutineSupport)
|
||||
}
|
||||
|
||||
companion object : KotlinIntentionActionsFactory() {
|
||||
fun getFixText(state: LanguageFeature.State): String {
|
||||
return when (state) {
|
||||
LanguageFeature.State.ENABLED -> "Enable coroutine support"
|
||||
LanguageFeature.State.ENABLED_WITH_WARNING -> "Enable coroutine support (with warning)"
|
||||
LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> "Disable coroutine support"
|
||||
}
|
||||
}
|
||||
|
||||
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
|
||||
val newCoroutineSupports = when (diagnostic.factory) {
|
||||
Errors.EXPERIMENTAL_FEATURE_ERROR -> {
|
||||
if (Errors.EXPERIMENTAL_FEATURE_ERROR.cast(diagnostic).a.first != LanguageFeature.Coroutines) return emptyList()
|
||||
listOf(LanguageFeature.State.ENABLED_WITH_WARNING, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
Errors.EXPERIMENTAL_FEATURE_WARNING -> {
|
||||
if (Errors.EXPERIMENTAL_FEATURE_WARNING.cast(diagnostic).a.first != LanguageFeature.Coroutines) return emptyList()
|
||||
listOf(LanguageFeature.State.ENABLED, LanguageFeature.State.ENABLED_WITH_ERROR)
|
||||
}
|
||||
else -> return emptyList()
|
||||
}
|
||||
val module = ModuleUtilCore.findModuleForPsiElement(diagnostic.psiElement) ?: return emptyList()
|
||||
val facetSettings = KotlinFacet.get(module)?.configuration?.settings
|
||||
|
||||
val configureInProject = (facetSettings == null || facetSettings.useProjectSettings) &&
|
||||
module.getBuildSystemType() == BuildSystemType.JPS
|
||||
val quickFixConstructor: (PsiElement, LanguageFeature.State) -> ChangeCoroutineSupportFix =
|
||||
if (configureInProject) ::InProject else ::InModule
|
||||
return newCoroutineSupports.map { quickFixConstructor(diagnostic.psiElement, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":core:util.runtime"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:cli-common"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
|
||||
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
|
||||
testCompileOnly(intellijDep())
|
||||
//testCompileOnly(intellijPluginDep("maven"))
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
|
||||
testRuntime(intellijDep())
|
||||
// TODO: the order of the plugins matters here, consider avoiding order-dependency
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { /*projectDefault()*/ }
|
||||
"test" { /*projectDefault()*/ }
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2000-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.maven.configuration
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
||||
import com.intellij.ide.actions.OpenFileAction
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.openapi.roots.JavaProjectModelModificationService
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.vfs.WritingAccessProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FileTypeIndex
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.xml.XmlFile
|
||||
import org.jetbrains.idea.maven.dom.MavenDomUtil
|
||||
import org.jetbrains.idea.maven.dom.model.MavenDomPlugin
|
||||
import org.jetbrains.idea.maven.model.MavenId
|
||||
import org.jetbrains.idea.maven.project.MavenProjectsManager
|
||||
import org.jetbrains.idea.maven.utils.MavenArtifactScope
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.CoroutineSupport
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.configuration.*
|
||||
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
|
||||
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
|
||||
import org.jetbrains.kotlin.idea.maven.*
|
||||
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
|
||||
|
||||
abstract class KotlinMavenConfigurator
|
||||
protected constructor(
|
||||
private val testArtifactId: String?,
|
||||
private val addJunit: Boolean,
|
||||
override val name: String,
|
||||
override val presentableText: String
|
||||
) : KotlinProjectConfigurator {
|
||||
|
||||
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||
val module = moduleSourceRootGroup.baseModule
|
||||
if (module.getBuildSystemType() != Maven)
|
||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||
|
||||
val psi = runReadAction { findModulePomFile(module) }
|
||||
if (psi == null
|
||||
|| !psi.isValid
|
||||
|| psi !is XmlFile
|
||||
|| psi.virtualFile == null) {
|
||||
return ConfigureKotlinStatus.BROKEN
|
||||
}
|
||||
|
||||
if (isKotlinModule(module)) {
|
||||
return runReadAction { checkKotlinPlugin(module) }
|
||||
}
|
||||
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||
}
|
||||
|
||||
private fun checkKotlinPlugin(module: Module): ConfigureKotlinStatus {
|
||||
val psi = findModulePomFile(module) as? XmlFile ?: return ConfigureKotlinStatus.BROKEN
|
||||
val pom = PomFile.forFileOrNull(psi) ?: return ConfigureKotlinStatus.NON_APPLICABLE
|
||||
|
||||
if (hasKotlinPlugin(pom)) {
|
||||
return ConfigureKotlinStatus.CONFIGURED
|
||||
}
|
||||
|
||||
val mavenProjectsManager = MavenProjectsManager.getInstance(module.project)
|
||||
val mavenProject = mavenProjectsManager.findProject(module) ?: return ConfigureKotlinStatus.BROKEN
|
||||
|
||||
val kotlinPluginId = kotlinPluginId(null)
|
||||
val kotlinPlugin = mavenProject.plugins.find { it.mavenId.equals(kotlinPluginId.groupId, kotlinPluginId.artifactId) }
|
||||
?: return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||
|
||||
if (kotlinPlugin.executions.any { it.goals.any(this::isRelevantGoal) }) {
|
||||
return ConfigureKotlinStatus.CONFIGURED
|
||||
}
|
||||
|
||||
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||
}
|
||||
|
||||
private fun hasKotlinPlugin(pom: PomFile): Boolean {
|
||||
val plugin = pom.findPlugin(kotlinPluginId(null)) ?: return false
|
||||
|
||||
return plugin.executions.executions.any {
|
||||
it.goals.goals.any { isRelevantGoal(it.stringValue ?: "") }
|
||||
}
|
||||
}
|
||||
|
||||
override fun configure(project: Project, excludeModules: Collection<Module>) {
|
||||
val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion())
|
||||
|
||||
dialog.show()
|
||||
if (!dialog.isOK) return
|
||||
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
val collector = createConfigureKotlinNotificationCollector(project)
|
||||
for (module in excludeMavenChildrenModules(project, dialog.modulesToConfigure)) {
|
||||
val file = findModulePomFile(module)
|
||||
if (file != null && canConfigureFile(file)) {
|
||||
configureModule(module, file, dialog.kotlinVersion, collector)
|
||||
OpenFileAction.openFile(file.virtualFile, project)
|
||||
} else {
|
||||
showErrorMessage(project, "Cannot find pom.xml for module " + module.name)
|
||||
}
|
||||
}
|
||||
collector.showNotification()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getMinimumSupportedVersion() = "1.0.0"
|
||||
|
||||
protected abstract fun isKotlinModule(module: Module): Boolean
|
||||
protected abstract fun isRelevantGoal(goalName: String): Boolean
|
||||
|
||||
protected abstract fun createExecutions(pomFile: PomFile, kotlinPlugin: MavenDomPlugin, module: Module)
|
||||
protected abstract fun getStdlibArtifactId(module: Module, version: String): String
|
||||
|
||||
open fun configureModule(module: Module, file: PsiFile, version: String, collector: NotificationMessageCollector): Boolean =
|
||||
changePomFile(module, file, version, collector)
|
||||
|
||||
private fun changePomFile(
|
||||
module: Module,
|
||||
file: PsiFile,
|
||||
version: String,
|
||||
collector: NotificationMessageCollector
|
||||
): Boolean {
|
||||
val virtualFile = file.virtualFile ?: error("Virtual file should exists for psi file " + file.name)
|
||||
val domModel = MavenDomUtil.getMavenDomProjectModel(module.project, virtualFile)
|
||||
if (domModel == null) {
|
||||
showErrorMessage(module.project, null)
|
||||
return false
|
||||
}
|
||||
|
||||
val pom = PomFile.forFileOrNull(file as XmlFile) ?: return false
|
||||
pom.addProperty(KOTLIN_VERSION_PROPERTY, version)
|
||||
|
||||
pom.addDependency(
|
||||
MavenId(GROUP_ID, getStdlibArtifactId(module, version), "\${$KOTLIN_VERSION_PROPERTY}"),
|
||||
MavenArtifactScope.COMPILE,
|
||||
null,
|
||||
false,
|
||||
null
|
||||
)
|
||||
if (testArtifactId != null) {
|
||||
pom.addDependency(MavenId(GROUP_ID, testArtifactId, "\${$KOTLIN_VERSION_PROPERTY}"), MavenArtifactScope.TEST, null, false, null)
|
||||
}
|
||||
if (addJunit) {
|
||||
// TODO currently it is always disabled: junit version selection could be shown in the configurator dialog
|
||||
pom.addDependency(MavenId("junit", "junit", "4.12"), MavenArtifactScope.TEST, null, false, null)
|
||||
}
|
||||
|
||||
val repositoryDescription = getRepositoryForVersion(version)
|
||||
if (repositoryDescription != null) {
|
||||
pom.addLibraryRepository(repositoryDescription)
|
||||
pom.addPluginRepository(repositoryDescription)
|
||||
}
|
||||
|
||||
val plugin = pom.addPlugin(MavenId(GROUP_ID, MAVEN_PLUGIN_ID, "\${$KOTLIN_VERSION_PROPERTY}"))
|
||||
createExecutions(pom, plugin, module)
|
||||
|
||||
configurePlugin(pom, plugin, module, version)
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement<PsiFile>(file)
|
||||
|
||||
collector.addMessage(virtualFile.path + " was modified")
|
||||
return true
|
||||
}
|
||||
|
||||
protected open fun configurePlugin(pom: PomFile, plugin: MavenDomPlugin, module: Module, version: String) {
|
||||
}
|
||||
|
||||
protected fun createExecution(
|
||||
pomFile: PomFile,
|
||||
kotlinPlugin: MavenDomPlugin,
|
||||
executionId: String,
|
||||
goalName: String,
|
||||
module: Module,
|
||||
isTest: Boolean
|
||||
) {
|
||||
pomFile.addKotlinExecution(module, kotlinPlugin, executionId, PomFile.getPhase(false, isTest), isTest, listOf(goalName))
|
||||
|
||||
if (hasJavaFiles(module)) {
|
||||
pomFile.addJavacExecutions(module, kotlinPlugin)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateLanguageVersion(
|
||||
module: Module,
|
||||
languageVersion: String?,
|
||||
apiVersion: String?,
|
||||
requiredStdlibVersion: ApiVersion,
|
||||
forTests: Boolean
|
||||
) {
|
||||
fun doUpdateMavenLanguageVersion(): PsiElement? {
|
||||
val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null
|
||||
val pom = PomFile.forFileOrNull(psi) ?: return null
|
||||
return pom.changeLanguageVersion(
|
||||
languageVersion,
|
||||
apiVersion
|
||||
)
|
||||
}
|
||||
|
||||
val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion ->
|
||||
runtimeVersion < requiredStdlibVersion
|
||||
} ?: false
|
||||
|
||||
if (runtimeUpdateRequired) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " +
|
||||
"Please update the version in your build script.",
|
||||
"Update Language Version"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val element = doUpdateMavenLanguageVersion()
|
||||
if (element == null) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"Failed to update.pom.xml. Please update the file manually.",
|
||||
"Update Language Version"
|
||||
)
|
||||
} else {
|
||||
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addLibraryDependency(
|
||||
module: Module,
|
||||
element: PsiElement,
|
||||
library: ExternalLibraryDescriptor,
|
||||
libraryJarDescriptors: List<LibraryJarDescriptor>
|
||||
) {
|
||||
val scope = OrderEntryFix.suggestScopeByLocation(module, element)
|
||||
JavaProjectModelModificationService.getInstance(module.project).addDependency(module, library, scope)
|
||||
}
|
||||
|
||||
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
|
||||
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
|
||||
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
|
||||
|
||||
val messageTitle = ChangeCoroutineSupportFix.getFixText(state)
|
||||
if (runtimeUpdateRequired) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
|
||||
"Please update the version in your build script.",
|
||||
messageTitle
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val element = changeMavenCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state), messageTitle)
|
||||
|
||||
if (element != null) {
|
||||
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun changeMavenCoroutineConfiguration(module: Module, value: String, messageTitle: String): PsiElement? {
|
||||
fun doChangeMavenCoroutineConfiguration(): PsiElement? {
|
||||
val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null
|
||||
val pom = PomFile.forFileOrNull(psi) ?: return null
|
||||
return pom.changeCoroutineConfiguration(value)
|
||||
}
|
||||
|
||||
val element = doChangeMavenCoroutineConfiguration()
|
||||
if (element == null) {
|
||||
Messages.showErrorDialog(
|
||||
module.project,
|
||||
"Failed to update.pom.xml. Please update the file manually.",
|
||||
messageTitle
|
||||
)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val GROUP_ID = "org.jetbrains.kotlin"
|
||||
const val MAVEN_PLUGIN_ID = "kotlin-maven-plugin"
|
||||
private const val KOTLIN_VERSION_PROPERTY = "kotlin.version"
|
||||
|
||||
private fun hasJavaFiles(module: Module): Boolean {
|
||||
return !FileTypeIndex.getFiles(JavaFileType.INSTANCE, GlobalSearchScope.moduleScope(module)).isEmpty()
|
||||
}
|
||||
|
||||
private fun findModulePomFile(module: Module): PsiFile? {
|
||||
val files = MavenProjectsManager.getInstance(module.project).projectsFiles
|
||||
for (file in files) {
|
||||
val fileModule = ModuleUtilCore.findModuleForFile(file, module.project)
|
||||
if (module != fileModule) continue
|
||||
val psiFile = PsiManager.getInstance(module.project).findFile(file) ?: continue
|
||||
if (!MavenDomUtil.isProjectFile(psiFile)) continue
|
||||
return psiFile
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun canConfigureFile(file: PsiFile): Boolean {
|
||||
return WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
||||
}
|
||||
|
||||
private fun showErrorMessage(project: Project, message: String?) {
|
||||
Messages.showErrorDialog(
|
||||
project,
|
||||
"<html>Couldn't configure kotlin-maven plugin automatically.<br/>" +
|
||||
(if (message != null) "$message</br>" else "") +
|
||||
"See manual installation instructions <a href=\"http://confluence.jetbrains.com/display/Kotlin/Kotlin+Build+Tools#KotlinBuildTools-Maven\">here</a>.</html>",
|
||||
"Configure Kotlin-Maven Plugin"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintExternalAnnotator"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,120 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="android-lint.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.navigation.KotlinAndroidGotoDeclarationHandler"/>
|
||||
|
||||
<moduleService serviceInterface="org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager"
|
||||
serviceImplementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidLayoutXmlFileManager"/>
|
||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor"/>
|
||||
<errorHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsReportSubmitter"/>
|
||||
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidGotoDeclarationHandler"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection"
|
||||
displayName="Illegal Android Identifier"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
level="ERROR"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.TypeParameterFindViewByIdInspection"
|
||||
displayName="Cast can be converted to findViewById with type parameter"
|
||||
groupName="Kotlin Android"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WEAK WARNING"
|
||||
language="kotlin" />
|
||||
|
||||
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.android.KotlinAndroidLineMarkerProvider"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.KotlinAndroidAddStringResource</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinGradleAndroidModuleModelProjectDataService"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddActivityToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddServiceToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.AddBroadcastReceiverToManifest</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.ImplementParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RemoveParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.android.intention.RedoParcelableAction</className>
|
||||
<category>Kotlin Android</category>
|
||||
</intentionAction>
|
||||
|
||||
<codeInsight.unresolvedReferenceQuickFixProvider
|
||||
implementation="org.jetbrains.kotlin.android.inspection.KotlinAndroidResourceQuickFixProvider"/>
|
||||
|
||||
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.android.folding.ResourceFoldingBuilder" />
|
||||
|
||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.android.AndroidResourceReferenceAnnotator" />
|
||||
|
||||
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
|
||||
|
||||
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
|
||||
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidExtensionsExpressionCodegenExtension"/>
|
||||
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidOnDestroyClassBuilderInterceptorExtension"/>
|
||||
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
|
||||
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
|
||||
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
|
||||
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsGradleImportHandler"/>
|
||||
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.quickfix.AndroidQuickFixRegistrar"/>
|
||||
<projectConfigurator implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator"/>
|
||||
<gradleModelFacade implementation="org.jetbrains.kotlin.android.configure.AndroidGradleModelFacade"/>
|
||||
<completionInformationProvider implementation="org.jetbrains.kotlin.AndroidExtensionsCompletionInformationProvider" />
|
||||
|
||||
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
|
||||
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/>
|
||||
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
|
||||
|
||||
<androidDexer implementation="org.jetbrains.kotlin.android.debugger.AndroidDexerImpl"/>
|
||||
|
||||
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.android.tools.idea">
|
||||
<npw.template.convertJavaToKotlinProvider implementation="org.jetbrains.kotlin.android.ConvertJavaToKotlinProviderImpl"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.android">
|
||||
<androidLintQuickFixProvider implementation="org.jetbrains.kotlin.android.quickfix.KotlinAndroidQuickFixProvider" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.android.gradle.sync">
|
||||
<postSyncModuleSetupStep implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidModuleSetupStep"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
|
||||
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -6,9 +6,10 @@
|
||||
<version>@snapshot@</version>
|
||||
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
|
||||
|
||||
<idea-version since-build="173.4548.28" until-build="173.*"/>
|
||||
<idea-version since-build="181.1" until-build="181.*"/>
|
||||
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
<depends>com.intellij.modules.remoteServers</depends>
|
||||
|
||||
<!-- required for Kotlin/Native plugin -->
|
||||
<depends optional="true">org.jetbrains.kotlin.native.platform.deps</depends>
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.actions
|
||||
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.ide.scratch.ScratchFileService
|
||||
import com.intellij.ide.scratch.ScratchRootType
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MessagesEx
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileVisitor
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiErrorElement
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
|
||||
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class JavaToKotlinAction : AnAction() {
|
||||
companion object {
|
||||
private fun uniqueKotlinFileName(javaFile: VirtualFile): String {
|
||||
val ioFile = File(javaFile.path.replace('/', File.separatorChar))
|
||||
|
||||
var i = 0
|
||||
while (true) {
|
||||
val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt"
|
||||
if (!ioFile.resolveSibling(fileName).exists()) return fileName
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
val title = "Convert Java to Kotlin"
|
||||
|
||||
private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
|
||||
try {
|
||||
val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile)
|
||||
if (document == null) {
|
||||
MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater()
|
||||
continue
|
||||
}
|
||||
document.replaceString(0, document.textLength, text)
|
||||
FileDocumentManager.getInstance().saveDocument(document)
|
||||
|
||||
val virtualFile = psiFile.virtualFile
|
||||
if (ScratchRootType.getInstance().containsFile(virtualFile)) {
|
||||
val mapping = ScratchFileService.getInstance().scratchesMapping
|
||||
mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language)
|
||||
}
|
||||
else {
|
||||
val fileName = uniqueKotlinFileName(virtualFile)
|
||||
virtualFile.rename(this, fileName)
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun convertFiles(javaFiles: List<PsiJavaFile>, project: Project,
|
||||
enableExternalCodeProcessing: Boolean = true,
|
||||
askExternalCodeProcessing: Boolean = true): List<KtFile> {
|
||||
var converterResult: JavaToKotlinConverter.FilesResult? = null
|
||||
fun convert() {
|
||||
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
|
||||
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator!!)
|
||||
}
|
||||
|
||||
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
::convert,
|
||||
title,
|
||||
true,
|
||||
project)) return emptyList()
|
||||
|
||||
|
||||
var externalCodeUpdate: (() -> Unit)? = null
|
||||
|
||||
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
|
||||
val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
|
||||
if (!askExternalCodeProcessing || (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK)) {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
runReadAction {
|
||||
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator!!)
|
||||
}
|
||||
},
|
||||
title,
|
||||
true,
|
||||
project)
|
||||
}
|
||||
}
|
||||
|
||||
return project.executeWriteCommand("Convert files from Java to Kotlin", null) {
|
||||
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
|
||||
|
||||
val newFiles = saveResults(javaFiles, converterResult!!.results)
|
||||
|
||||
externalCodeUpdate?.invoke()
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
newFiles.singleOrNull()?.let {
|
||||
FileEditorManager.getInstance(project).openFile(it, true)
|
||||
}
|
||||
|
||||
newFiles.map { it.toPsiFile(project) as KtFile }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList()
|
||||
val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
|
||||
|
||||
val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull()
|
||||
|
||||
if (firstSyntaxError != null) {
|
||||
val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count()
|
||||
val question = firstSyntaxError.containingFile.name +
|
||||
(if (count > 1) " and ${count - 1} more Java files" else " file") +
|
||||
" contain syntax errors, the conversion result may be incorrect"
|
||||
|
||||
val okText = "Investigate Errors"
|
||||
val cancelText = "Proceed with Conversion"
|
||||
if (Messages.showOkCancelDialog(
|
||||
project,
|
||||
question,
|
||||
title,
|
||||
okText,
|
||||
cancelText,
|
||||
Messages.getWarningIcon()
|
||||
) == Messages.OK) {
|
||||
NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
convertFiles(javaFiles, project)
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
|
||||
val project = e.project ?: return
|
||||
|
||||
e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles)
|
||||
}
|
||||
|
||||
private fun isAnyJavaFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
|
||||
if (files.any { manager.findFile(it) is PsiJavaFile && it.isWritable }) return true
|
||||
return files.any { it.isDirectory && isAnyJavaFileSelected(project, it.children) }
|
||||
}
|
||||
|
||||
private fun selectedJavaFiles(e: AnActionEvent): Sequence<PsiJavaFile> {
|
||||
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
|
||||
val project = e.project ?: return sequenceOf()
|
||||
return allJavaFiles(virtualFiles, project)
|
||||
}
|
||||
|
||||
private fun allJavaFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<PsiJavaFile> {
|
||||
val manager = PsiManager.getInstance(project)
|
||||
return allFiles(filesOrDirs)
|
||||
.asSequence()
|
||||
.mapNotNull { manager.findFile(it) as? PsiJavaFile }
|
||||
}
|
||||
|
||||
private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>()
|
||||
for (file in filesOrDirs) {
|
||||
VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
|
||||
override fun visitFile(file: VirtualFile): Boolean {
|
||||
result.add(file)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.idea.facet
|
||||
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdk
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModel
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.project.*
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.versions.*
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> {
|
||||
for (platform in TargetPlatformKind.ALL_PLATFORMS) {
|
||||
if (getRuntimeLibraryVersions(module, rootModel, platform).isNotEmpty()) {
|
||||
return platform
|
||||
}
|
||||
}
|
||||
|
||||
val sdk = ((rootModel ?: ModuleRootManager.getInstance(module))).sdk
|
||||
val sdkVersion = (sdk?.sdkType as? JavaSdk)?.getVersion(sdk!!)
|
||||
return when {
|
||||
sdkVersion == null || sdkVersion >= JavaSdkVersion.JDK_1_8 -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_8]
|
||||
else -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinFacetSettings.initializeIfNeeded(
|
||||
module: Module,
|
||||
rootModel: ModuleRootModel?,
|
||||
platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies
|
||||
languageVersion: String? = null
|
||||
) {
|
||||
val project = module.project
|
||||
|
||||
val shouldInferLanguageLevel = languageLevel == null
|
||||
val shouldInferAPILevel = apiLevel == null
|
||||
|
||||
if (compilerSettings == null) {
|
||||
compilerSettings = KotlinCompilerSettings.getInstance(project).settings
|
||||
}
|
||||
|
||||
val commonArguments = KotlinCommonCompilerArgumentsHolder.getInstance(module.project).settings
|
||||
|
||||
if (compilerArguments == null) {
|
||||
val targetPlatformKind = platformKind ?: getDefaultTargetPlatform(module, rootModel)
|
||||
compilerArguments = targetPlatformKind.createCompilerArguments {
|
||||
targetPlatformKind.getPlatformCompilerArgumentsByProject(module.project)?.let { mergeBeans(it, this) }
|
||||
mergeBeans(commonArguments, this)
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldInferLanguageLevel) {
|
||||
languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null)
|
||||
?: getDefaultLanguageLevel(module, languageVersion)
|
||||
}
|
||||
|
||||
if (shouldInferAPILevel) {
|
||||
apiLevel = if (useProjectSettings) {
|
||||
LanguageVersion.fromVersionString(commonArguments.apiVersion) ?: languageLevel
|
||||
}
|
||||
else {
|
||||
languageLevel!!.coerceAtMost(getLibraryLanguageLevel(module, rootModel, targetPlatformKind))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TargetPlatformKind<*>.getPlatformCompilerArgumentsByProject(project: Project): CommonCompilerArguments? {
|
||||
return when (this) {
|
||||
is TargetPlatformKind.Jvm -> Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings
|
||||
is TargetPlatformKind.JavaScript -> Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val TargetPlatformKind<*>.mavenLibraryIds: List<String>
|
||||
get() = when (this) {
|
||||
is TargetPlatformKind.Jvm -> listOf(MAVEN_STDLIB_ID, MAVEN_STDLIB_ID_JRE7, MAVEN_STDLIB_ID_JDK7, MAVEN_STDLIB_ID_JRE8, MAVEN_STDLIB_ID_JDK8)
|
||||
is TargetPlatformKind.JavaScript -> listOf(MAVEN_JS_STDLIB_ID, MAVEN_OLD_JS_STDLIB_ID)
|
||||
is TargetPlatformKind.Common -> listOf(MAVEN_COMMON_STDLIB_ID)
|
||||
}
|
||||
|
||||
val mavenLibraryIdToPlatform: Map<String, TargetPlatformKind<*>> by lazy {
|
||||
TargetPlatformKind.ALL_PLATFORMS
|
||||
.flatMap { platform -> platform.mavenLibraryIds.map { it to platform } }
|
||||
.sortedByDescending { it.first.length }
|
||||
.toMap()
|
||||
}
|
||||
|
||||
fun Module.getOrCreateFacet(modelsProvider: IdeModifiableModelsProvider,
|
||||
useProjectSettings: Boolean,
|
||||
commitModel: Boolean = false): KotlinFacet {
|
||||
val facetModel = modelsProvider.getModifiableFacetModel(this)
|
||||
|
||||
val facet = facetModel.findFacet(KotlinFacetType.TYPE_ID, KotlinFacetType.INSTANCE.defaultFacetName)
|
||||
?: with(KotlinFacetType.INSTANCE) { createFacet(this@getOrCreateFacet, defaultFacetName, createDefaultConfiguration(), null) }
|
||||
.apply { facetModel.addFacet(this) }
|
||||
facet.configuration.settings.useProjectSettings = useProjectSettings
|
||||
if (commitModel) {
|
||||
runWriteAction {
|
||||
facetModel.commit()
|
||||
}
|
||||
}
|
||||
return facet
|
||||
}
|
||||
|
||||
fun KotlinFacet.configureFacet(
|
||||
compilerVersion: String,
|
||||
coroutineSupport: LanguageFeature.State,
|
||||
platformKind: TargetPlatformKind<*>?, // if null, detect by module dependencies
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
val module = module
|
||||
with(configuration.settings) {
|
||||
compilerArguments = null
|
||||
compilerSettings = null
|
||||
initializeIfNeeded(
|
||||
module,
|
||||
modelsProvider.getModifiableRootModel(module),
|
||||
platformKind,
|
||||
compilerVersion
|
||||
)
|
||||
val apiLevel = apiLevel
|
||||
val languageLevel = languageLevel
|
||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
||||
this.apiLevel = languageLevel
|
||||
}
|
||||
this.coroutineSupport = coroutineSupport
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinFacet.noVersionAutoAdvance() {
|
||||
configuration.settings.compilerArguments?.let {
|
||||
it.autoAdvanceLanguageVersion = false
|
||||
it.autoAdvanceApiVersion = false
|
||||
}
|
||||
}
|
||||
|
||||
// "Primary" fields are written to argument beans directly and thus not presented in the "additional arguments" string
|
||||
// Update these lists when facet/project settings UI changes
|
||||
val commonUIExposedFields = listOf(CommonCompilerArguments::languageVersion.name,
|
||||
CommonCompilerArguments::apiVersion.name,
|
||||
CommonCompilerArguments::suppressWarnings.name,
|
||||
CommonCompilerArguments::coroutinesState.name)
|
||||
private val commonUIHiddenFields = listOf(CommonCompilerArguments::pluginClasspaths.name,
|
||||
CommonCompilerArguments::pluginOptions.name)
|
||||
private val commonPrimaryFields = commonUIExposedFields + commonUIHiddenFields
|
||||
|
||||
private val jvmSpecificUIExposedFields = listOf(K2JVMCompilerArguments::jvmTarget.name,
|
||||
K2JVMCompilerArguments::destination.name,
|
||||
K2JVMCompilerArguments::classpath.name)
|
||||
val jvmUIExposedFields = commonUIExposedFields + jvmSpecificUIExposedFields
|
||||
private val jvmPrimaryFields = commonPrimaryFields + jvmSpecificUIExposedFields
|
||||
|
||||
private val jsSpecificUIExposedFields = listOf(K2JSCompilerArguments::sourceMap.name,
|
||||
K2JSCompilerArguments::sourceMapPrefix.name,
|
||||
K2JSCompilerArguments::sourceMapEmbedSources.name,
|
||||
K2JSCompilerArguments::outputPrefix.name,
|
||||
K2JSCompilerArguments::outputPostfix.name,
|
||||
K2JSCompilerArguments::moduleKind.name)
|
||||
val jsUIExposedFields = commonUIExposedFields + jsSpecificUIExposedFields
|
||||
private val jsPrimaryFields = commonPrimaryFields + jsSpecificUIExposedFields
|
||||
|
||||
private val metadataSpecificUIExposedFields = listOf(K2MetadataCompilerArguments::destination.name,
|
||||
K2MetadataCompilerArguments::classpath.name)
|
||||
val metadataUIExposedFields = commonUIExposedFields + metadataSpecificUIExposedFields
|
||||
private val metadataPrimaryFields = commonPrimaryFields + metadataSpecificUIExposedFields
|
||||
|
||||
private val CommonCompilerArguments.primaryFields: List<String>
|
||||
get() = when (this) {
|
||||
is K2JVMCompilerArguments -> jvmPrimaryFields
|
||||
is K2JSCompilerArguments -> jsPrimaryFields
|
||||
is K2MetadataCompilerArguments -> metadataPrimaryFields
|
||||
else -> commonPrimaryFields
|
||||
}
|
||||
|
||||
private val CommonCompilerArguments.ignoredFields: List<String>
|
||||
get() = when (this) {
|
||||
is K2JVMCompilerArguments -> listOf(K2JVMCompilerArguments::noJdk.name, K2JVMCompilerArguments::jdkHome.name)
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
private fun Module.configureSdkIfPossible(compilerArguments: CommonCompilerArguments, modelsProvider: IdeModifiableModelsProvider) {
|
||||
val allSdks = ProjectJdkTable.getInstance().allJdks
|
||||
val sdk = if (compilerArguments is K2JVMCompilerArguments) {
|
||||
val jdkHome = compilerArguments.jdkHome ?: return
|
||||
allSdks.firstOrNull { it.sdkType is JavaSdk && FileUtil.comparePaths(it.homePath, jdkHome) == 0 } ?: return
|
||||
} else {
|
||||
allSdks.firstOrNull { it.sdkType is KotlinSdkType } ?: KotlinSdkType.INSTANCE.createSdkWithUniqueName(allSdks.toList())
|
||||
}
|
||||
|
||||
modelsProvider.getModifiableRootModel(this).sdk = sdk
|
||||
}
|
||||
|
||||
fun parseCompilerArgumentsToFacet(
|
||||
arguments: List<String>,
|
||||
defaultArguments: List<String>,
|
||||
kotlinFacet: KotlinFacet,
|
||||
modelsProvider: IdeModifiableModelsProvider?
|
||||
) {
|
||||
with(kotlinFacet.configuration.settings) {
|
||||
val compilerArguments = this.compilerArguments ?: return
|
||||
|
||||
val defaultCompilerArguments = compilerArguments::class.java.newInstance()
|
||||
parseCommandLineArguments(defaultArguments, defaultCompilerArguments)
|
||||
defaultCompilerArguments.convertPathsToSystemIndependent()
|
||||
|
||||
parseCommandLineArguments(arguments, compilerArguments)
|
||||
|
||||
compilerArguments.convertPathsToSystemIndependent()
|
||||
|
||||
// Retain only fields exposed (and not explicitly ignored) in facet configuration editor.
|
||||
// The rest is combined into string and stored in CompilerSettings.additionalArguments
|
||||
|
||||
if (modelsProvider != null)
|
||||
kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider)
|
||||
|
||||
val primaryFields = compilerArguments.primaryFields
|
||||
val ignoredFields = compilerArguments.ignoredFields
|
||||
|
||||
fun exposeAsAdditionalArgument(property: KProperty1<CommonCompilerArguments, Any?>) =
|
||||
property.name !in primaryFields && property.get(compilerArguments) != property.get(defaultCompilerArguments)
|
||||
|
||||
val additionalArgumentsString = with(compilerArguments::class.java.newInstance()) {
|
||||
copyFieldsSatisfying(compilerArguments, this) { exposeAsAdditionalArgument(it) && it.name !in ignoredFields }
|
||||
ArgumentUtils.convertArgumentsToStringList(this).joinToString(separator = " ") {
|
||||
if (StringUtil.containsWhitespaces(it) || it.startsWith('"')) {
|
||||
StringUtil.wrapWithDoubleQuote(StringUtil.escapeQuotes(it))
|
||||
} else it
|
||||
}
|
||||
}
|
||||
compilerSettings?.additionalArguments =
|
||||
if (additionalArgumentsString.isNotEmpty()) additionalArgumentsString else CompilerSettings.DEFAULT_ADDITIONAL_ARGUMENTS
|
||||
|
||||
with(compilerArguments::class.java.newInstance()) {
|
||||
copyFieldsSatisfying(this, compilerArguments) { exposeAsAdditionalArgument(it) || it.name in ignoredFields }
|
||||
}
|
||||
|
||||
val languageLevel = languageLevel
|
||||
val apiLevel = apiLevel
|
||||
if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) {
|
||||
this.apiLevel = languageLevel
|
||||
}
|
||||
|
||||
updateMergedArguments()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.reporter
|
||||
|
||||
import com.intellij.diagnostic.ITNReporter
|
||||
import com.intellij.openapi.diagnostic.IdeaLoggingEvent
|
||||
import com.intellij.openapi.diagnostic.SubmittedReportInfo
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.util.Consumer
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUpdater
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.PluginUpdateStatus
|
||||
import org.jetbrains.kotlin.idea.actions.internal.KotlinInternalMode
|
||||
import java.awt.Component
|
||||
|
||||
/**
|
||||
* We need to wrap ITNReporter for force showing or errors from kotlin plugin even from released version of IDEA.
|
||||
*/
|
||||
class KotlinReportSubmitter : ITNReporter() {
|
||||
private var hasUpdate = false
|
||||
private var hasLatestVersion = false
|
||||
|
||||
override fun showErrorInRelease(event: IdeaLoggingEvent): Boolean {
|
||||
val notificationEnabled = "disabled" != System.getProperty("kotlin.fatal.error.notification", "enabled")
|
||||
return notificationEnabled && (!hasUpdate || KotlinInternalMode.enabled)
|
||||
}
|
||||
|
||||
override fun submit(events: Array<IdeaLoggingEvent>, additionalInfo: String?, parentComponent: Component?, consumer: Consumer<SubmittedReportInfo>): Boolean {
|
||||
if (hasUpdate) {
|
||||
if (KotlinInternalMode.enabled) {
|
||||
return super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (hasLatestVersion) {
|
||||
return super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
|
||||
KotlinPluginUpdater.getInstance().runUpdateCheck { status ->
|
||||
if (status is PluginUpdateStatus.Update) {
|
||||
hasUpdate = true
|
||||
if (parentComponent != null) {
|
||||
|
||||
if (KotlinInternalMode.enabled) {
|
||||
super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
|
||||
val rc = Messages.showDialog(parentComponent,
|
||||
"You're running Kotlin plugin version ${KotlinPluginUtil.getPluginVersion()}, " +
|
||||
"while the latest version is ${status.pluginDescriptor.version}",
|
||||
"Update Kotlin Plugin",
|
||||
arrayOf("Update", "Ignore"),
|
||||
0, Messages.getInformationIcon())
|
||||
if (rc == 0) {
|
||||
KotlinPluginUpdater.getInstance().installPluginUpdate(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasLatestVersion = true
|
||||
super.submit(events, additionalInfo, parentComponent, consumer)
|
||||
}
|
||||
false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.AddArrayOfTypeFix" "false"
|
||||
// ACTION: Add 'value =' to argument
|
||||
// ACTION: Create test
|
||||
// ACTION: Do not show hints for current method
|
||||
// ACTION: Make internal
|
||||
// ACTION: Make private
|
||||
// ACTION: To raw string literal
|
||||
// ACTION: Do not show hints for current method
|
||||
|
||||
@ArrAnn(<caret>"123") class My
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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
|
||||
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
|
||||
import com.intellij.psi.codeStyle.PackageEntry
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractImportsTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory()
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
protected fun doTest(testPath: String) {
|
||||
val settingManager = CodeStyleSettingsManager.getInstance(project)
|
||||
val tempSettings = settingManager.currentSettings.clone()
|
||||
settingManager.setTemporarySettings(tempSettings)
|
||||
|
||||
val codeStyleSettings = KotlinCodeStyleSettings.getInstance(project)
|
||||
|
||||
try {
|
||||
val fixture = myFixture
|
||||
val dependencySuffixes = listOf(".dependency.kt", ".dependency.java", ".dependency1.kt", ".dependency2.kt")
|
||||
for (suffix in dependencySuffixes) {
|
||||
val dependencyPath = testPath.replace(".kt", suffix)
|
||||
if (File(dependencyPath).exists()) {
|
||||
fixture.configureByFile(dependencyPath)
|
||||
}
|
||||
}
|
||||
|
||||
fixture.configureByFile(testPath)
|
||||
|
||||
val file = fixture.file as KtFile
|
||||
|
||||
val fileText = file.text
|
||||
|
||||
codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT = InTextDirectivesUtils.getPrefixedInt(fileText, "// NAME_COUNT_TO_USE_STAR_IMPORT:") ?: nameCountToUseStarImportDefault
|
||||
codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS = InTextDirectivesUtils.getPrefixedInt(fileText, "// NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS:") ?: nameCountToUseStarImportForMembersDefault
|
||||
codeStyleSettings.IMPORT_NESTED_CLASSES = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// IMPORT_NESTED_CLASSES:") ?: false
|
||||
|
||||
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGE_TO_USE_STAR_IMPORTS:").forEach {
|
||||
codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), false))
|
||||
}
|
||||
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGES_TO_USE_STAR_IMPORTS:").forEach {
|
||||
codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), true))
|
||||
}
|
||||
|
||||
val log = project.executeWriteCommand<String?>("") { doTest(file) }
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(File(testPath + ".after"), myFixture.file.text)
|
||||
if (log != null) {
|
||||
val logFile = File(testPath + ".log")
|
||||
if (log.isNotEmpty()) {
|
||||
KotlinTestUtils.assertEqualsToFile(logFile, log)
|
||||
}
|
||||
else {
|
||||
TestCase.assertFalse(logFile.exists())
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
settingManager.dropTemporarySettings()
|
||||
}
|
||||
}
|
||||
|
||||
// returns test log
|
||||
protected abstract fun doTest(file: KtFile): String?
|
||||
|
||||
protected open val nameCountToUseStarImportDefault: Int
|
||||
get() = 1
|
||||
|
||||
protected open val nameCountToUseStarImportForMembersDefault: Int
|
||||
get() = 3
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":compiler:util"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
testCompile(project(":idea"))
|
||||
testCompile(projectTests(":idea:idea-test-framework"))
|
||||
testCompile(project(":compiler:light-classes"))
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testCompileOnly(intellijDep()) { includeJars("platform-api", "platform-impl") }
|
||||
|
||||
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-android"))
|
||||
testRuntime(project(":plugins:android-extensions-ide"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("coverage"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("IntelliLang"))
|
||||
testRuntime(intellijPluginDep("testng"))
|
||||
testRuntime(intellijPluginDep("copyright"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("java-i18n"))
|
||||
testRuntime(intellijPluginDep("java-decompiler"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
|
||||
|
||||
val testForWebDemo by task<Test> {
|
||||
include("**/*JavaToKotlinConverterForWebDemoTestGenerated*")
|
||||
classpath = the<JavaPluginConvention>().sourceSets["test"].runtimeClasspath
|
||||
workingDir = rootDir
|
||||
}
|
||||
val cleanTestForWebDemo by tasks
|
||||
|
||||
val test: Test by tasks
|
||||
test.apply {
|
||||
exclude("**/*JavaToKotlinConverterForWebDemoTestGenerated*")
|
||||
dependsOn(testForWebDemo)
|
||||
}
|
||||
val cleanTest by tasks
|
||||
cleanTest.dependsOn(cleanTestForWebDemo)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
val compilerModules: Array<String> by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
compile(project(":kotlin-build-common"))
|
||||
compile(project(":core:descriptors"))
|
||||
compile(project(":core:descriptors.jvm"))
|
||||
compile(project(":kotlin-compiler-runner"))
|
||||
compile(project(":compiler:daemon-common"))
|
||||
compile(projectRuntimeJar(":kotlin-daemon-client"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(projectRuntimeJar(":kotlin-preloader"))
|
||||
compile(project(":idea:idea-jps-common"))
|
||||
compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "platform-api", "util", "asm-all") }
|
||||
compileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
|
||||
testCompileOnly(project(":kotlin-reflect-api"))
|
||||
testCompile(project(":compiler:incremental-compilation-impl"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":compiler:incremental-compilation-impl"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(projectTests(":kotlin-build-common"))
|
||||
testCompileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
|
||||
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "platform-api", "log4j") }
|
||||
testCompile(intellijDep("jps-build-test"))
|
||||
compilerModules.forEach {
|
||||
testRuntime(project(it))
|
||||
}
|
||||
testRuntime(intellijDep())
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" {
|
||||
/*java.srcDirs("jps-tests/test"
|
||||
/*, "kannotator-jps-plugin-test/test"*/ // Obsolete
|
||||
)*/
|
||||
}
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":kotlin-compiler:dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
description = "Kotlin Android Extensions IDEA"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
jvmTarget = "1.6"
|
||||
|
||||
dependencies {
|
||||
testRuntime(intellijDep())
|
||||
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:light-classes"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:idea-gradle"))
|
||||
compile(project(":plugins:android-extensions-compiler"))
|
||||
compileOnly(project(":kotlin-android-extensions-runtime"))
|
||||
compileOnly(intellijPluginDep("android"))
|
||||
compileOnly(intellijPluginDep("Groovy"))
|
||||
compileOnly(intellijDep())
|
||||
|
||||
testCompile(project(":compiler:tests-common"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":compiler:frontend.java"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":plugins:kapt3-idea"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea"))
|
||||
testCompile(projectTests(":idea:idea-android"))
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testCompile(intellijPluginDep("android"))
|
||||
testCompile(intellijPluginDep("Groovy"))
|
||||
testCompile(intellijDep())
|
||||
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":plugins:android-extensions-jps"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":plugins:lint"))
|
||||
testRuntime(intellijPluginDep("junit"))
|
||||
testRuntime(intellijPluginDep("IntelliLang"))
|
||||
testRuntime(intellijPluginDep("properties"))
|
||||
testRuntime(intellijPluginDep("java-i18n"))
|
||||
testRuntime(intellijPluginDep("gradle"))
|
||||
testRuntime(intellijPluginDep("Groovy"))
|
||||
testRuntime(intellijPluginDep("java-decompiler"))
|
||||
//testRuntime(intellijPluginDep("maven"))
|
||||
testRuntime(intellijPluginDep("android"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":kotlin-android-extensions-runtime:dist")
|
||||
workingDir = rootDir
|
||||
useAndroidSdk()
|
||||
useAndroidJar()
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
|
||||
ideaPlugin()
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.kapt.idea
|
||||
|
||||
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.*
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.tooling.model.idea.IdeaModule
|
||||
import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder
|
||||
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
|
||||
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.lang.Exception
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
interface KaptSourceSetModel : Serializable {
|
||||
val sourceSetName: String
|
||||
val isTest: Boolean
|
||||
val generatedSourcesDir: String
|
||||
val generatedClassesDir: String
|
||||
val generatedKotlinSourcesDir: String
|
||||
|
||||
val generatedSourcesDirFile get() = generatedSourcesDir.takeIf { it.isNotEmpty() }?.let(::File)
|
||||
val generatedClassesDirFile get() = generatedClassesDir.takeIf { it.isNotEmpty() }?.let(::File)
|
||||
val generatedKotlinSourcesDirFile get() = generatedKotlinSourcesDir.takeIf { it.isNotEmpty() }?.let(::File)
|
||||
}
|
||||
|
||||
class KaptSourceSetModelImpl(
|
||||
override val sourceSetName: String,
|
||||
override val isTest: Boolean,
|
||||
override val generatedSourcesDir: String,
|
||||
override val generatedClassesDir: String,
|
||||
override val generatedKotlinSourcesDir: String
|
||||
) : KaptSourceSetModel
|
||||
|
||||
interface KaptGradleModel : Serializable {
|
||||
val isEnabled: Boolean
|
||||
val buildDirectory: File
|
||||
val sourceSets: List<KaptSourceSetModel>
|
||||
}
|
||||
|
||||
class KaptGradleModelImpl(
|
||||
override val isEnabled: Boolean,
|
||||
override val buildDirectory: File,
|
||||
override val sourceSets: List<KaptSourceSetModel>
|
||||
) : KaptGradleModel
|
||||
|
||||
internal typealias AndroidGradleModel = AndroidModuleModel
|
||||
|
||||
@Suppress("unused")
|
||||
class KaptProjectResolverExtension : AbstractProjectResolverExtension() {
|
||||
private companion object {
|
||||
private val LOG = Logger.getInstance(KaptProjectResolverExtension::class.java)
|
||||
}
|
||||
|
||||
override fun getExtraProjectModelClasses() = setOf(KaptGradleModel::class.java)
|
||||
override fun getToolingExtensionsClasses() = setOf(KaptModelBuilderService::class.java, Unit::class.java)
|
||||
|
||||
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
||||
val kaptModel = resolverCtx.getExtraProject(gradleModule, KaptGradleModel::class.java) ?: return
|
||||
|
||||
if (kaptModel.isEnabled) {
|
||||
for (sourceSet in kaptModel.sourceSets) {
|
||||
populateAndroidModuleModelIfNeeded(ideModule, sourceSet)
|
||||
|
||||
val sourceSetDataNode = ideModule.findGradleSourceSet(sourceSet.sourceSetName) ?: continue
|
||||
|
||||
fun addSourceSet(path: String, type: ExternalSystemSourceType) {
|
||||
val contentRootData = ContentRootData(GRADLE_SYSTEM_ID, path)
|
||||
contentRootData.storePath(type, path)
|
||||
sourceSetDataNode.createChild(ProjectKeys.CONTENT_ROOT, contentRootData)
|
||||
}
|
||||
|
||||
val sourceType = if (sourceSet.isTest) ExternalSystemSourceType.TEST_GENERATED else ExternalSystemSourceType.SOURCE_GENERATED
|
||||
sourceSet.generatedSourcesDirFile?.let { addSourceSet(it.absolutePath, sourceType) }
|
||||
sourceSet.generatedKotlinSourcesDirFile?.let { addSourceSet(it.absolutePath, sourceType) }
|
||||
|
||||
sourceSet.generatedClassesDirFile?.let { generatedClassesDir ->
|
||||
val libraryData = LibraryData(GRADLE_SYSTEM_ID, "kaptGeneratedClasses")
|
||||
libraryData.addPath(LibraryPathType.BINARY, generatedClassesDir.absolutePath)
|
||||
val libraryDependencyData = LibraryDependencyData(sourceSetDataNode.data, libraryData, LibraryLevel.MODULE)
|
||||
sourceSetDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.populateModuleExtraModels(gradleModule, ideModule)
|
||||
}
|
||||
|
||||
private fun populateAndroidModuleModelIfNeeded(ideModule: DataNode<ModuleData>, sourceSet: KaptSourceSetModel) {
|
||||
ideModule.findAndroidModuleModel()?.let { androidModelAny ->
|
||||
// We can cast to AndroidModuleModel cause we already checked in findAndroidModuleModel() that the class exists
|
||||
|
||||
val generatedKotlinSources = sourceSet.generatedKotlinSourcesDirFile ?: return
|
||||
|
||||
val androidModel = androidModelAny.data as? AndroidModuleModel ?: return
|
||||
val variant = androidModel.findVariantByName(sourceSet.sourceSetName) ?: return
|
||||
|
||||
androidModel.registerExtraGeneratedSourceFolder(generatedKotlinSources)
|
||||
|
||||
// TODO remove this when IDEA eventually migrate to the newer Android plugin
|
||||
try {
|
||||
variant.mainArtifact.generatedSourceFolders += generatedKotlinSources
|
||||
} catch (e: Throwable) {
|
||||
// There was an error being thrown here, but the code above doesn't work for the newer versions of Android Studio 3
|
||||
// (generatedSourceFolders returns a wrapped unmodifiable list), and the thrown exception breaks the import.
|
||||
// The error will be moved back when I find a work-around for AS3.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DataNode<ModuleData>.findAndroidModuleModel(): DataNode<*>? {
|
||||
val modelClassName = "com.android.tools.idea.gradle.project.model.AndroidModuleModel"
|
||||
val node = children.firstOrNull { it.key.dataType == modelClassName } ?: return null
|
||||
return if (!hasClassInClasspath(modelClassName)) null else node
|
||||
}
|
||||
|
||||
private fun hasClassInClasspath(name: String): Boolean {
|
||||
return try {
|
||||
Class.forName(name) != null
|
||||
} catch (thr: Throwable) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun DataNode<ModuleData>.findGradleSourceSet(sourceSetName: String): DataNode<GradleSourceSetData>? {
|
||||
val moduleName = data.id
|
||||
for (child in children) {
|
||||
val gradleSourceSetData = child.data as? GradleSourceSetData ?: continue
|
||||
if (gradleSourceSetData.id == "$moduleName:$sourceSetName") {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return child as DataNode<GradleSourceSetData>?
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class KaptModelBuilderService : AbstractKotlinGradleModelBuilder() {
|
||||
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
|
||||
return ErrorMessageBuilder.create(project, e, "Gradle import errors")
|
||||
.withDescription("Unable to build kotlin-kapt plugin configuration")
|
||||
}
|
||||
|
||||
override fun canBuild(modelName: String?): Boolean = modelName == KaptGradleModel::class.java.name
|
||||
|
||||
override fun buildAll(modelName: String?, project: Project): Any {
|
||||
val kaptPlugin: Plugin<*>? = project.plugins.findPlugin("kotlin-kapt")
|
||||
val kaptIsEnabled = kaptPlugin != null
|
||||
|
||||
val sourceSets = mutableListOf<KaptSourceSetModel>()
|
||||
|
||||
if (kaptIsEnabled) {
|
||||
project.getAllTasks(false)[project]?.forEach { compileTask ->
|
||||
if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach
|
||||
|
||||
val sourceSetName = compileTask.getSourceSetName()
|
||||
val isTest = sourceSetName.toLowerCase().endsWith("test")
|
||||
|
||||
val kaptGeneratedSourcesDir = getKaptDirectory("getKaptGeneratedSourcesDir", project, sourceSetName)
|
||||
val kaptGeneratedClassesDir = getKaptDirectory("getKaptGeneratedClassesDir", project, sourceSetName)
|
||||
val kaptGeneratedKotlinSourcesDir = getKaptDirectory("getKaptGeneratedKotlinSourcesDir", project, sourceSetName)
|
||||
sourceSets += KaptSourceSetModelImpl(
|
||||
sourceSetName, isTest, kaptGeneratedSourcesDir, kaptGeneratedClassesDir, kaptGeneratedKotlinSourcesDir)
|
||||
}
|
||||
}
|
||||
|
||||
return KaptGradleModelImpl(kaptIsEnabled, project.buildDir, sourceSets)
|
||||
}
|
||||
|
||||
private fun getKaptDirectory(funName: String, project: Project, sourceSetName: String): String {
|
||||
val kotlinKaptPlugin = project.plugins.findPlugin("kotlin-kapt") ?: return ""
|
||||
|
||||
val targetMethod = kotlinKaptPlugin::class.java.methods.firstOrNull {
|
||||
Modifier.isStatic(it.modifiers) && it.name == funName && it.parameterCount == 2
|
||||
} ?: return ""
|
||||
|
||||
return (targetMethod(null, project, sourceSetName) as? File)?.absolutePath ?: ""
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.samWithReceiver.ide
|
||||
|
||||
/*
|
||||
import org.jetbrains.kotlin.annotation.plugin.ide.AbstractMavenImportHandler
|
||||
import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedCompilerPluginSetup
|
||||
import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedCompilerPluginSetup.PluginOption
|
||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
|
||||
class SamWithReceiverMavenProjectImportHandler : AbstractMavenImportHandler() {
|
||||
private companion object {
|
||||
val ANNOTATION_PARAMETER_PREFIX = "sam-with-receiver:${SamWithReceiverCommandLineProcessor.ANNOTATION_OPTION.name}="
|
||||
}
|
||||
|
||||
override val compilerPluginId = SamWithReceiverCommandLineProcessor.PLUGIN_ID
|
||||
override val pluginName = "samWithReceiver"
|
||||
override val mavenPluginArtifactName = "kotlin-maven-sam-with-receiver"
|
||||
override val pluginJarFileFromIdea = PathUtil.kotlinPathsForIdeaPlugin.allOpenPluginJarPath
|
||||
|
||||
override fun getOptions(enabledCompilerPlugins: List<String>, compilerPluginOptions: List<String>): List<PluginOption>? {
|
||||
if ("sam-with-receiver" !in enabledCompilerPlugins) {
|
||||
return null
|
||||
}
|
||||
|
||||
val annotations = mutableListOf<String>()
|
||||
|
||||
for ((presetName, presetAnnotations) in SamWithReceiverCommandLineProcessor.SUPPORTED_PRESETS) {
|
||||
if (presetName in enabledCompilerPlugins) {
|
||||
annotations.addAll(presetAnnotations)
|
||||
}
|
||||
}
|
||||
|
||||
annotations.addAll(compilerPluginOptions.mapNotNull { text ->
|
||||
if (!text.startsWith(ANNOTATION_PARAMETER_PREFIX)) return@mapNotNull null
|
||||
text.substring(ANNOTATION_PARAMETER_PREFIX.length)
|
||||
})
|
||||
|
||||
return annotations.map { PluginOption(SamWithReceiverCommandLineProcessor.ANNOTATION_OPTION.name, it) }
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
|
||||
description = "Kotlin IDEA Ultimate plugin"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
val ideaProjectResources = project(":idea").the<JavaPluginConvention>().sourceSets["main"].output.resourcesDir
|
||||
|
||||
evaluationDependsOn(":prepare:idea-plugin")
|
||||
|
||||
val intellijUltimateEnabled : Boolean by rootProject.extra
|
||||
|
||||
val springClasspath by configurations.creating
|
||||
|
||||
dependencies {
|
||||
if (intellijUltimateEnabled) {
|
||||
testRuntime(intellijUltimateDep())
|
||||
}
|
||||
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(projectDist(":kotlin-stdlib"))
|
||||
compile(project(":core:descriptors")) { isTransitive = false }
|
||||
compile(project(":compiler:psi")) { isTransitive = false }
|
||||
compile(project(":core:descriptors.jvm")) { isTransitive = false }
|
||||
compile(project(":core:util.runtime")) { isTransitive = false }
|
||||
compile(project(":compiler:light-classes")) { isTransitive = false }
|
||||
compile(project(":compiler:frontend")) { isTransitive = false }
|
||||
compile(project(":compiler:frontend.java")) { isTransitive = false }
|
||||
compile(project(":js:js.frontend")) { isTransitive = false }
|
||||
compile(projectClasses(":idea"))
|
||||
compile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||
compile(project(":idea:idea-core")) { isTransitive = false }
|
||||
compile(project(":idea:ide-common")) { isTransitive = false }
|
||||
compile(project(":idea:idea-gradle")) { isTransitive = false }
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
if (intellijUltimateEnabled) {
|
||||
compileOnly(intellijUltimatePluginDep("NodeJS"))
|
||||
compileOnly(intellijUltimateDep()) { includeJars("annotations", "trove4j", "openapi", "platform-api", "platform-impl", "java-api", "java-impl", "idea", "util", "jdom") }
|
||||
compileOnly(intellijUltimatePluginDep("CSS"))
|
||||
compileOnly(intellijUltimatePluginDep("DatabaseTools"))
|
||||
compileOnly(intellijUltimatePluginDep("JavaEE"))
|
||||
compileOnly(intellijUltimatePluginDep("jsp"))
|
||||
compileOnly(intellijUltimatePluginDep("PersistenceSupport"))
|
||||
compileOnly(intellijUltimatePluginDep("Spring"))
|
||||
compileOnly(intellijUltimatePluginDep("properties"))
|
||||
compileOnly(intellijUltimatePluginDep("java-i18n"))
|
||||
compileOnly(intellijUltimatePluginDep("gradle"))
|
||||
compileOnly(intellijUltimatePluginDep("Groovy"))
|
||||
compileOnly(intellijUltimatePluginDep("junit"))
|
||||
compileOnly(intellijUltimatePluginDep("uml"))
|
||||
compileOnly(intellijUltimatePluginDep("JavaScriptLanguage"))
|
||||
compileOnly(intellijUltimatePluginDep("JavaScriptDebugger"))
|
||||
}
|
||||
|
||||
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(project(":plugins:lint")) { isTransitive = false }
|
||||
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea")) { isTransitive = false }
|
||||
testCompile(projectTests(":generators:test-generator"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
if (intellijUltimateEnabled) {
|
||||
testCompileOnly(intellijUltimateDep()) { includeJars("platform-api", "platform-impl", "gson", "annotations", "trove4j", "openapi", "idea", "util", "jdom", rootProject = rootProject) }
|
||||
}
|
||||
testCompile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||
|
||||
testRuntime(projectDist(":kotlin-reflect"))
|
||||
testRuntime(project(":kotlin-script-runtime"))
|
||||
testRuntime(projectRuntimeJar(":kotlin-compiler"))
|
||||
testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:android-extensions-compiler")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-android")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-maven")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-jps-common")) { isTransitive = false }
|
||||
testRuntime(project(":idea:formatter")) { isTransitive = false }
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":noarg-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-noarg-compiler-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":allopen-ide-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-allopen-compiler-plugin")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
|
||||
testRuntime(project(":plugins:uast-kotlin"))
|
||||
testRuntime(project(":plugins:uast-kotlin-idea"))
|
||||
testRuntime(intellijPluginDep("smali"))
|
||||
|
||||
if (intellijUltimateEnabled) {
|
||||
testCompile(intellijUltimatePluginDep("CSS"))
|
||||
testCompile(intellijUltimatePluginDep("DatabaseTools"))
|
||||
testCompile(intellijUltimatePluginDep("JavaEE"))
|
||||
testCompile(intellijUltimatePluginDep("jsp"))
|
||||
testCompile(intellijUltimatePluginDep("PersistenceSupport"))
|
||||
testCompile(intellijUltimatePluginDep("Spring"))
|
||||
testCompile(intellijUltimatePluginDep("uml"))
|
||||
testCompile(intellijUltimatePluginDep("JavaScriptLanguage"))
|
||||
testCompile(intellijUltimatePluginDep("JavaScriptDebugger"))
|
||||
testCompile(intellijUltimatePluginDep("NodeJS"))
|
||||
testCompile(intellijUltimatePluginDep("properties"))
|
||||
testCompile(intellijUltimatePluginDep("java-i18n"))
|
||||
testCompile(intellijUltimatePluginDep("gradle"))
|
||||
testCompile(intellijUltimatePluginDep("Groovy"))
|
||||
testCompile(intellijUltimatePluginDep("junit"))
|
||||
testRuntime(intellijUltimatePluginDep("coverage"))
|
||||
testRuntime(intellijUltimatePluginDep("maven"))
|
||||
testRuntime(intellijUltimatePluginDep("android"))
|
||||
testRuntime(intellijUltimatePluginDep("testng"))
|
||||
testRuntime(intellijUltimatePluginDep("IntelliLang"))
|
||||
testRuntime(intellijUltimatePluginDep("copyright"))
|
||||
testRuntime(intellijUltimatePluginDep("java-decompiler"))
|
||||
}
|
||||
|
||||
testRuntime(files("${System.getProperty("java.home")}/../lib/tools.jar"))
|
||||
|
||||
springClasspath(commonDep("org.springframework", "spring-core"))
|
||||
springClasspath(commonDep("org.springframework", "spring-beans"))
|
||||
springClasspath(commonDep("org.springframework", "spring-context"))
|
||||
springClasspath(commonDep("org.springframework", "spring-tx"))
|
||||
springClasspath(commonDep("org.springframework", "spring-web"))
|
||||
}
|
||||
|
||||
val preparedResources = File(buildDir, "prepResources")
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" {
|
||||
projectDefault()
|
||||
resources.srcDir(preparedResources)
|
||||
}
|
||||
}
|
||||
|
||||
val ultimatePluginXmlContent: String by lazy {
|
||||
val sectRex = Regex("""^\s*</?idea-plugin>\s*$""")
|
||||
File(projectDir, "resources/META-INF/ultimate-plugin.xml")
|
||||
.readLines()
|
||||
.filterNot { it.matches(sectRex) }
|
||||
.joinToString("\n")
|
||||
}
|
||||
|
||||
val prepareResources by task<Copy> {
|
||||
dependsOn(":idea:assemble")
|
||||
from(ideaProjectResources, {
|
||||
exclude("META-INF/plugin.xml")
|
||||
})
|
||||
into(preparedResources)
|
||||
}
|
||||
|
||||
val preparePluginXml by task<Copy> {
|
||||
dependsOn(":idea:assemble")
|
||||
from(ideaProjectResources, { include("META-INF/plugin.xml") })
|
||||
into(preparedResources)
|
||||
filter {
|
||||
it?.replace("<!-- ULTIMATE-PLUGIN-PLACEHOLDER -->", ultimatePluginXmlContent)
|
||||
}
|
||||
}
|
||||
|
||||
val communityPluginProject = ":prepare:idea-plugin"
|
||||
|
||||
val jar = runtimeJar(task<ShadowJar>("shadowJar")) {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
dependsOn(preparePluginXml)
|
||||
dependsOn("$communityPluginProject:shadowJar")
|
||||
val communityPluginJar = project(communityPluginProject).configurations["runtimeJar"].artifacts.files.singleFile
|
||||
from(zipTree(communityPluginJar), { exclude("META-INF/plugin.xml") })
|
||||
from(preparedResources, { include("META-INF/plugin.xml") })
|
||||
from(the<JavaPluginConvention>().sourceSets.getByName("main").output)
|
||||
archiveName = "kotlin-plugin.jar"
|
||||
}
|
||||
|
||||
val ideaPluginDir: File by rootProject.extra
|
||||
val ideaUltimatePluginDir: File by rootProject.extra
|
||||
|
||||
task<Copy>("ideaUltimatePlugin") {
|
||||
dependsOn(":ideaPlugin")
|
||||
into(ideaUltimatePluginDir)
|
||||
from(ideaPluginDir) { exclude("lib/kotlin-plugin.jar") }
|
||||
from(jar, { into("lib") })
|
||||
}
|
||||
|
||||
task("idea-ultimate-plugin") {
|
||||
dependsOn("ideaUltimatePlugin")
|
||||
doFirst { logger.warn("'$name' task is deprecated, use '${dependsOn.last()}' instead") }
|
||||
}
|
||||
|
||||
task("ideaUltimatePluginTest") {
|
||||
dependsOn("check")
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(prepareResources)
|
||||
dependsOn(preparePluginXml)
|
||||
workingDir = rootDir
|
||||
doFirst {
|
||||
if (intellijUltimateEnabled) {
|
||||
systemProperty("idea.home.path", intellijUltimateRootDir().canonicalPath)
|
||||
}
|
||||
systemProperty("spring.classpath", springClasspath.asPath)
|
||||
}
|
||||
}
|
||||
|
||||
val generateTests by generator("org.jetbrains.kotlin.tests.GenerateUltimateTestsKt")
|
||||
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
/*
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":idea"))
|
||||
compileOnly(project(":idea:idea-maven"))
|
||||
compileOnly(project(":idea:idea-gradle"))
|
||||
compileOnly(project(":idea:idea-jvm"))
|
||||
|
||||
compile(intellijDep())
|
||||
runtimeOnly(files(toolsJar()))
|
||||
}
|
||||
|
||||
val intellijUltimateEnabled : Boolean by rootProject.extra
|
||||
|
||||
val ideaUltimatePluginDir: File by rootProject.extra
|
||||
val ideaUltimateSandboxDir: File by rootProject.extra
|
||||
|
||||
if (intellijUltimateEnabled) {
|
||||
runIdeTask("runUltimate", ideaUltimatePluginDir, ideaUltimateSandboxDir) {
|
||||
dependsOn(":dist", ":ideaPlugin", ":ultimate:ideaUltimatePlugin")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -1,5 +1,4 @@
|
||||
<project xmlns:if="ant:if" xmlns:unless="ant:unless" name="Update Dependencies" default="update">
|
||||
<!--as31 -> as32-->
|
||||
<import file="common.xml" optional="false"/>
|
||||
|
||||
<property name="automerge_dummy" value="This property is used to trick git merge. Do not delete empty lines around."/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
extra["versions.intellijSdk"] = "173.4548.28"
|
||||
extra["versions.intellijSdk"] = "181.4203.6"
|
||||
extra["versions.androidBuildTools"] = "r23.0.1"
|
||||
extra["versions.idea.NodeJS"] = "172.3757.32"
|
||||
extra["versions.androidStudioRelease"] = "3.2.0.2"
|
||||
extra["versions.androidStudioBuild"] = "173.4595177"
|
||||
extra["versions.idea.NodeJS"] = "181.3494.12"
|
||||
//extra["versions.androidStudioRelease"] = "3.1.0.5"
|
||||
//extra["versions.androidStudioBuild"] = "173.4506631"
|
||||
|
||||
val gradleJars = listOf(
|
||||
"gradle-api",
|
||||
@@ -31,6 +31,7 @@ when (platform) {
|
||||
extra["versions.jar.kxml2"] = "2.3.0"
|
||||
extra["versions.jar.streamex"] = "0.6.5"
|
||||
extra["versions.jar.gson"] = "2.8.2"
|
||||
extra["versions.jar.oro"] = "2.0.8"
|
||||
for (jar in gradleJars) {
|
||||
extra["versions.jar.$jar"] = "4.4"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user