Pill: Move out Pill tasks from buildSrc
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import org.jetbrains.kotlin.pill.PillExtension
|
||||
import java.lang.reflect.Modifier
|
||||
import java.net.URLClassLoader
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
pill {
|
||||
variant = PillExtension.Variant.FULL
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(kotlin("stdlib", embeddedKotlinVersion))
|
||||
compileOnly(gradleApi())
|
||||
compileOnly(gradleKotlinDsl())
|
||||
compileOnly("com.github.jengelman.gradle.plugins:shadow:${rootProject.extra["versions.shadow"]}")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { }
|
||||
}
|
||||
|
||||
fun runPillTask(taskName: String) {
|
||||
val jarFile = configurations.archives.artifacts.single { it.type == "jar" }.file
|
||||
val cl = URLClassLoader(arrayOf(jarFile.toURI().toURL()), (object {}).javaClass.classLoader)
|
||||
|
||||
val pillImporterClass = Class.forName("org.jetbrains.kotlin.pill.PillImporter", true, cl)
|
||||
val runMethod = pillImporterClass.declaredMethods.single { it.name == "run" }
|
||||
require(Modifier.isStatic(runMethod.modifiers))
|
||||
|
||||
val platformDir = IntellijRootUtils.getIntellijRootDir(project)
|
||||
val resourcesDir = File(project.projectDir, "resources")
|
||||
runMethod.invoke(null, project.rootProject, taskName, platformDir, resourcesDir, EmbeddedComponents.CONFIGURATION_NAME)
|
||||
}
|
||||
|
||||
val jar: Jar by tasks
|
||||
|
||||
val pill by tasks.creating {
|
||||
dependsOn(jar)
|
||||
doLast { runPillTask("pill") }
|
||||
}
|
||||
|
||||
val unpill by tasks.creating {
|
||||
dependsOn(jar)
|
||||
doLast { runPillTask("unpill") }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[PILL] All Compiler Tests" type="CompoundRunConfigurationType" factoryName="Compound Run Configuration">
|
||||
<toRun type="JUnit" name="[Pill] Compiler Tests" />
|
||||
<toRun type="JUnit" name="[Pill] JS Backend Tests" />
|
||||
<toRun type="JUnit" name="[Pill] Java 8 Tests" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,37 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] All IDEA Plugin Tests" type="JUnit" factoryName="JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jetbrains.kotlin.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<module name="idea.test" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PACKAGE_NAME" value="org.jetbrains.kotlin" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -XX:+HeapDumpOnOutOfMemoryError -Xmx1250m -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=128m -Djna.nosys=true -Duse.jps=true -Didea.home.path=$IDEA_HOME_PATH$" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,35 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] Compiler Tests" type="JUnit" factoryName="JUnit">
|
||||
<module name="compiler.test" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jetbrains.kotlin.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PACKAGE_NAME" value="org.jetbrains.kotlin" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=128m -Djna.nosys=true -Duse.jps=true -Didea.home.path=$IDEA_HOME_PATH$" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<patterns />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,8 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] Generate All Tests" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.jetbrains.kotlin.pill.generateAllTests.Main" />
|
||||
<module name="pill.generate-all-tests.test" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,41 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] IDEA" type="Application" factoryName="Application" singleton="true">
|
||||
<log_file alias="idea.log" path="$PROJECT_DIR$/ideaSDK/system-idea/log/idea.log" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.intellij.idea.Main" />
|
||||
<module name="idea-runner.main" />
|
||||
<option name="VM_PARAMETERS" value="-Xmx1250m -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -ea -Didea.is.internal=true -Didea.debug.mode=true -Didea.system.path=$PROJECT_DIR$/local/ideaSandbox -Didea.config.path=$PROJECT_DIR$/local/ideaSandbox/config -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.io.useCanonCaches=false -Dplugin.path=$PROJECT_DIR$/out/artifacts/Kotlin $ADDITIONAL_IDEA_ARGS$" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$IDEA_HOME_PATH$" />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel Debug">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="debugPort" value="" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="transport" value="0" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Profile " />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="BuildArtifacts" enabled="true">
|
||||
<artifact name="KotlinPlugin" />
|
||||
</option>
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] IDEA (No ProcessCanceledException)" type="Application" factoryName="Application" singleton="true">
|
||||
<log_file alias="idea.log" path="$PROJECT_DIR$/ideaSDK/system-idea/log/idea.log" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.intellij.idea.Main" />
|
||||
<module name="idea-runner.main" />
|
||||
<option name="VM_PARAMETERS" value="-Xmx1250m -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -ea -Didea.is.internal=true -Didea.debug.mode=true -Didea.system.path=$PROJECT_DIR$/local/ideaSandbox -Didea.config.path=$PROJECT_DIR$/local/ideaSandbox/config -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.io.useCanonCaches=false -Dplugin.path=$PROJECT_DIR$/out/artifacts/Kotlin -Didea.ProcessCanceledException=disabled $ADDITIONAL_IDEA_ARGS$" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$IDEA_HOME_PATH$" />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel Debug">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="debugPort" value="" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="transport" value="0" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Profile " />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="BuildArtifacts" enabled="true">
|
||||
<artifact name="KotlinPlugin" />
|
||||
</option>
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,41 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] IDEA Ultimate" type="Application" factoryName="Application" singleton="true">
|
||||
<log_file alias="idea.log" path="$PROJECT_DIR$/ideaSDK/system-idea/log/idea.log" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.intellij.idea.Main" />
|
||||
<module name="kotlin-ultimate.ultimate.ultimate-runner.main" />
|
||||
<option name="VM_PARAMETERS" value="-Xmx1250m -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -ea -Didea.is.internal=true -Didea.debug.mode=true -Didea.system.path=$PROJECT_DIR$/local/ideaSandbox -Didea.config.path=$PROJECT_DIR$/local/ideaSandbox/config -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.io.useCanonCaches=false -Dplugin.path=$PROJECT_DIR$/out/artifacts/Kotlin $ADDITIONAL_IDEA_ARGS$" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$IDEA_HOME_PATH$" />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="JavaRebel Debug">
|
||||
<option name="bootstrapPath" />
|
||||
<option name="debugPort" value="" />
|
||||
<option name="jrebelArgs" value="" />
|
||||
<option name="loggingEnabled" value="false" />
|
||||
<option name="transport" value="0" />
|
||||
<option name="useBootstrapDefaults" value="true" />
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Profile " />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="BuildArtifacts" enabled="true">
|
||||
<artifact name="KotlinPlugin" />
|
||||
</option>
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,28 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] JS Backend Tests" type="JUnit" factoryName="JUnit">
|
||||
<module name="js.js.tests.test" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PACKAGE_NAME" value="org.jetbrains.kotlin.js.test" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -XX:+HeapDumpOnOutOfMemoryError -Xmx1250m -XX:+UseCodeCacheFlushing -Djna.nosys=true -Duse.jps=true -Dkotlin.js.skipMinificationTest=false" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<patterns />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,35 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] JVM Backend Tests" type="JUnit" factoryName="JUnit">
|
||||
<module name="compiler.test" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jetbrains.kotlin.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PACKAGE_NAME" value="org.jetbrains.kotlin.codegen" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=128m -Djna.nosys=true -Duse.jps=true -Didea.home.path=$IDEA_HOME_PATH$" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<patterns />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="true" />
|
||||
</RunnerSettings>
|
||||
<RunnerSettings RunnerId="Profile " />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,26 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] Java 8 Tests" type="JUnit" factoryName="JUnit">
|
||||
<module name="compiler.tests-java8.test" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jetbrains.kotlin.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="1.8" />
|
||||
<option name="PACKAGE_NAME" value="org.jetbrains.kotlin" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="package" />
|
||||
<option name="VM_PARAMETERS" value="-ea -XX:+HeapDumpOnOutOfMemoryError -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=128m -Djna.nosys=true -Duse.jps=true -Dkotlin.colors.enabled=false -Didea.home.path=$IDEA_HOME_PATH$" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,21 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="[Pill] Refresh PILL Model" type="GradleRunConfiguration" factoryName="Gradle" singleton="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="pill" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.BasePluginConvention
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import shadow.org.jdom2.input.SAXBuilder
|
||||
import shadow.org.jdom2.*
|
||||
import shadow.org.jdom2.output.Format
|
||||
import shadow.org.jdom2.output.XMLOutputter
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
const val EMBEDDED_CONFIGURATION_NAME = "embedded"
|
||||
|
||||
class JpsCompatiblePluginTasks(private val rootProject: Project, private val platformDir: File, private val resourcesDir: File) {
|
||||
companion object {
|
||||
private val DIST_LIBRARIES = listOf(
|
||||
":kotlin-annotations-jvm",
|
||||
":kotlin-stdlib",
|
||||
":kotlin-stdlib-jdk7",
|
||||
":kotlin-stdlib-jdk8",
|
||||
":kotlin-reflect",
|
||||
":kotlin-test:kotlin-test-jvm",
|
||||
":kotlin-test:kotlin-test-junit",
|
||||
":kotlin-script-runtime",
|
||||
":kotlin-serialization"
|
||||
)
|
||||
|
||||
private val IGNORED_LIBRARIES = listOf(
|
||||
// Libraries
|
||||
":kotlin-stdlib-common",
|
||||
":kotlin-reflect-api",
|
||||
":kotlin-test:kotlin-test-common",
|
||||
":kotlin-test:kotlin-test-annotations-common",
|
||||
// Other
|
||||
":kotlin-compiler",
|
||||
":kotlin-daemon-embeddable",
|
||||
":kotlin-compiler-embeddable",
|
||||
":kotlin-android-extensions",
|
||||
":kotlin-scripting-compiler-embeddable",
|
||||
":kotlin-scripting-compiler-impl-embeddable",
|
||||
":kotlin-scripting-jvm-host-embeddable"
|
||||
)
|
||||
|
||||
private val MAPPED_LIBRARIES = mapOf(
|
||||
":kotlin-reflect-api/java9" to ":kotlin-reflect/main"
|
||||
)
|
||||
|
||||
private val LIB_DIRECTORIES = listOf("dependencies", "dist")
|
||||
}
|
||||
|
||||
private lateinit var projectDir: File
|
||||
private lateinit var platformVersion: String
|
||||
private lateinit var platformBaseNumber: String
|
||||
private lateinit var intellijCoreDir: File
|
||||
private var isAndroidStudioPlatform: Boolean = false
|
||||
|
||||
private fun initEnvironment(project: Project) {
|
||||
projectDir = project.projectDir
|
||||
platformVersion = project.extensions.extraProperties.get("versions.intellijSdk").toString()
|
||||
platformBaseNumber = platformVersion.substringBefore(".", "").takeIf { it.isNotEmpty() }
|
||||
?: platformVersion.substringBefore("-", "").takeIf { it.isNotEmpty() }
|
||||
?: error("Invalid platform version: $platformVersion")
|
||||
intellijCoreDir = File(platformDir.parentFile.parentFile.parentFile, "intellij-core")
|
||||
isAndroidStudioPlatform = project.extensions.extraProperties.has("versions.androidStudioRelease")
|
||||
}
|
||||
|
||||
fun pill() {
|
||||
initEnvironment(rootProject)
|
||||
|
||||
val variantOptionValue = System.getProperty("pill.variant", "base").toUpperCase()
|
||||
val variant = PillExtensionMirror.Variant.values().firstOrNull { it.name == variantOptionValue }
|
||||
?: run {
|
||||
rootProject.logger.error("Invalid variant name: $variantOptionValue")
|
||||
return
|
||||
}
|
||||
|
||||
rootProject.logger.lifecycle("Pill: Setting up project for the '${variant.name.toLowerCase()}' variant...")
|
||||
|
||||
if (variant == PillExtensionMirror.Variant.NONE || variant == PillExtensionMirror.Variant.DEFAULT) {
|
||||
rootProject.logger.error("'none' and 'default' should not be passed as a Pill variant property value")
|
||||
return
|
||||
}
|
||||
|
||||
val parserContext = ParserContext(variant)
|
||||
|
||||
val dependencyPatcher = DependencyPatcher(rootProject)
|
||||
val dependencyMappers = listOf(dependencyPatcher, ::attachPlatformSources, ::attachAsmSources)
|
||||
|
||||
val jpsProject = parse(rootProject, parserContext)
|
||||
.mapDependencies(dependencyMappers)
|
||||
.copy(libraries = dependencyPatcher.libraries)
|
||||
|
||||
val files = render(jpsProject)
|
||||
|
||||
removeExistingIdeaLibrariesAndModules()
|
||||
removeJpsAndPillRunConfigurations()
|
||||
removeAllArtifactConfigurations()
|
||||
|
||||
val artifactDependencyMapper = object : OpaqueDependencyMapper {
|
||||
override fun map(dependency: PDependency): List<PDependency> {
|
||||
return jpsProject.mapDependency(dependency, dependencyMappers)
|
||||
}
|
||||
}
|
||||
|
||||
generateKotlinPluginArtifactFile(rootProject, artifactDependencyMapper).write()
|
||||
|
||||
copyRunConfigurations()
|
||||
setOptionsForDefaultJunitRunConfiguration(rootProject)
|
||||
|
||||
files.forEach { it.write() }
|
||||
}
|
||||
|
||||
fun unpill() {
|
||||
initEnvironment(rootProject)
|
||||
|
||||
removeExistingIdeaLibrariesAndModules()
|
||||
removeJpsAndPillRunConfigurations()
|
||||
removeAllArtifactConfigurations()
|
||||
}
|
||||
|
||||
private fun removeExistingIdeaLibrariesAndModules() {
|
||||
File(projectDir, ".idea/libraries").deleteRecursively()
|
||||
File(projectDir, ".idea/modules").deleteRecursively()
|
||||
}
|
||||
|
||||
private fun removeJpsAndPillRunConfigurations() {
|
||||
File(projectDir, ".idea/runConfigurations")
|
||||
.walk()
|
||||
.filter { (it.name.startsWith("JPS_") || it.name.startsWith("Pill_")) && it.extension.toLowerCase() == "xml" }
|
||||
.forEach { it.delete() }
|
||||
}
|
||||
|
||||
private fun removeAllArtifactConfigurations() {
|
||||
File(projectDir, ".idea/artifacts")
|
||||
.walk()
|
||||
.filter { it.extension.toLowerCase() == "xml" }
|
||||
.forEach { it.delete() }
|
||||
}
|
||||
|
||||
private fun copyRunConfigurations() {
|
||||
val runConfigurationsDir = File(resourcesDir, "runConfigurations")
|
||||
val targetDir = File(projectDir, ".idea/runConfigurations")
|
||||
val platformDirProjectRelative = "\$PROJECT_DIR\$/" + platformDir.toRelativeString(projectDir)
|
||||
val additionalIdeaArgs = if (isAndroidStudioPlatform) "-Didea.platform.prefix=AndroidStudio" else ""
|
||||
|
||||
targetDir.mkdirs()
|
||||
|
||||
fun substitute(text: String): String {
|
||||
return text
|
||||
.replace("\$IDEA_HOME_PATH\$", platformDirProjectRelative)
|
||||
.replace("\$ADDITIONAL_IDEA_ARGS\$", additionalIdeaArgs)
|
||||
}
|
||||
|
||||
(runConfigurationsDir.listFiles() ?: emptyArray())
|
||||
.filter { it.extension == "xml" }
|
||||
.map { it.name to substitute(it.readText()) }
|
||||
.forEach { File(targetDir, it.first).writeText(it.second) }
|
||||
}
|
||||
|
||||
/*
|
||||
This sets a proper (project root) working directory and a "idea.home.path" property to the default JUnit configuration,
|
||||
so one does not need to make these changes manually.
|
||||
*/
|
||||
private fun setOptionsForDefaultJunitRunConfiguration(project: Project) {
|
||||
val workspaceFile = File(projectDir, ".idea/workspace.xml")
|
||||
if (!workspaceFile.exists()) {
|
||||
project.logger.warn("${workspaceFile.name} does not exist, JUnit default run configuration was not modified")
|
||||
return
|
||||
}
|
||||
|
||||
val document = SAXBuilder().build(workspaceFile)
|
||||
val rootElement = document.rootElement
|
||||
|
||||
fun Element.getOrCreateChild(name: String, vararg attributes: Pair<String, String>): Element {
|
||||
for (child in getChildren(name)) {
|
||||
if (attributes.all { (attribute, value) -> child.getAttributeValue(attribute) == value }) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
|
||||
return Element(name).apply {
|
||||
for ((attributeName, value) in attributes) {
|
||||
setAttribute(attributeName, value)
|
||||
}
|
||||
|
||||
this@getOrCreateChild.addContent(this@apply)
|
||||
}
|
||||
}
|
||||
|
||||
val platformDirProjectRelative = "\$PROJECT_DIR\$/" + platformDir.toRelativeString(projectDir)
|
||||
|
||||
val runManagerComponent = rootElement.getOrCreateChild("component", "name" to "RunManager")
|
||||
|
||||
val junitConfiguration = runManagerComponent.getOrCreateChild(
|
||||
"configuration",
|
||||
"default" to "true",
|
||||
"type" to "JUnit",
|
||||
"factoryName" to "JUnit"
|
||||
)
|
||||
|
||||
val kotlinJunitConfiguration = runManagerComponent.getOrCreateChild(
|
||||
"configuration",
|
||||
"default" to "true",
|
||||
"type" to "KotlinJUnit",
|
||||
"factoryName" to "Kotlin JUnit"
|
||||
)
|
||||
|
||||
fun Element.applyJUnitTemplate() {
|
||||
getOrCreateChild("option", "name" to "WORKING_DIRECTORY").setAttribute("value", "file://\$PROJECT_DIR\$")
|
||||
getOrCreateChild("option", "name" to "VM_PARAMETERS").also { vmParams ->
|
||||
var options = vmParams.getAttributeValue("value", "")
|
||||
.split(' ')
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
|
||||
fun addOrReplaceOptionValue(name: String, value: Any?) {
|
||||
val optionsWithoutNewValue = options.filter { !it.startsWith("-D$name=") }
|
||||
options = if (value == null) optionsWithoutNewValue else (optionsWithoutNewValue + listOf("-D$name=$value"))
|
||||
}
|
||||
|
||||
if (options.none { it == "-ea" }) {
|
||||
options = options + "-ea"
|
||||
}
|
||||
|
||||
addOrReplaceOptionValue("idea.home.path", platformDirProjectRelative)
|
||||
addOrReplaceOptionValue("ideaSdk.androidPlugin.path", "$platformDirProjectRelative/plugins/android/lib")
|
||||
addOrReplaceOptionValue("use.jps", "true")
|
||||
addOrReplaceOptionValue("kotlinVersion", project.rootProject.extra["kotlinVersion"].toString())
|
||||
|
||||
val isAndroidStudioBunch = project.findProperty("versions.androidStudioRelease") != null
|
||||
addOrReplaceOptionValue("idea.platform.prefix", if (isAndroidStudioBunch) "AndroidStudio" else null)
|
||||
|
||||
val androidJarPath = project.configurations.findByName("androidJar")?.singleFile
|
||||
val androidSdkPath = project.configurations.findByName("androidSdk")?.singleFile
|
||||
|
||||
if (androidJarPath != null && androidSdkPath != null) {
|
||||
addOrReplaceOptionValue("android.sdk", "\$PROJECT_DIR\$/" + androidSdkPath.toRelativeString(projectDir))
|
||||
addOrReplaceOptionValue("android.jar", "\$PROJECT_DIR\$/" + androidJarPath.toRelativeString(projectDir))
|
||||
}
|
||||
|
||||
vmParams.setAttribute("value", options.joinToString(" "))
|
||||
}
|
||||
}
|
||||
|
||||
junitConfiguration.applyJUnitTemplate()
|
||||
kotlinJunitConfiguration.applyJUnitTemplate()
|
||||
|
||||
val output = XMLOutputter().also {
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
it.format = Format.getPrettyFormat().apply {
|
||||
setEscapeStrategy { c -> Verifier.isHighSurrogate(c) || c == '"' }
|
||||
setIndent(" ")
|
||||
setTextMode(Format.TextMode.TRIM)
|
||||
setOmitEncoding(false)
|
||||
setOmitDeclaration(false)
|
||||
}
|
||||
}
|
||||
|
||||
val postProcessedXml = output.outputString(document)
|
||||
.replace(""", """)
|
||||
.replace("
", " ")
|
||||
.replace("", " ")
|
||||
|
||||
workspaceFile.writeText(postProcessedXml)
|
||||
}
|
||||
|
||||
private class DependencyPatcher(private val rootProject: Project) : Function2<PProject, PDependency, List<PDependency>> {
|
||||
private val mappings: Map<String, Optional<PLibrary>> = run {
|
||||
val distLibDir = File(rootProject.extra["distLibDir"].toString())
|
||||
val result = HashMap<String, Optional<PLibrary>>()
|
||||
|
||||
fun List<File>.filterExisting() = filter { it.exists() }
|
||||
|
||||
for (path in DIST_LIBRARIES) {
|
||||
val project = rootProject.findProject(path) ?: error("Project not found")
|
||||
val archiveName = project.convention.findPlugin(BasePluginConvention::class.java)!!.archivesBaseName
|
||||
val classesJars = listOf(File(distLibDir, "$archiveName.jar")).filterExisting()
|
||||
val sourcesJars = listOf(File(distLibDir, "$archiveName-sources.jar")).filterExisting()
|
||||
result["$path/main"] = Optional.of(PLibrary(archiveName, classesJars, sourcesJars, originalName = path))
|
||||
}
|
||||
|
||||
for (path in IGNORED_LIBRARIES) {
|
||||
result["$path/main"] = Optional.empty<PLibrary>()
|
||||
}
|
||||
|
||||
for ((old, new) in MAPPED_LIBRARIES) {
|
||||
result[old] = result[new] ?: error("Mapped library $old -> $new not found")
|
||||
}
|
||||
|
||||
return@run result
|
||||
}
|
||||
|
||||
val libraries: List<PLibrary> = mappings.values.filter { it.isPresent }.map { it.get() }
|
||||
|
||||
override fun invoke(project: PProject, dependency: PDependency): List<PDependency> {
|
||||
if (dependency !is PDependency.ModuleLibrary) {
|
||||
return listOf(dependency)
|
||||
}
|
||||
|
||||
val root = dependency.library.classes.singleOrNull() ?: return listOf(dependency)
|
||||
val paths = project.artifacts[root.absolutePath]
|
||||
|
||||
if (paths == null) {
|
||||
val projectDir = rootProject.projectDir
|
||||
if (projectDir.isParent(root) && LIB_DIRECTORIES.none { File(projectDir, it).isParent(root) }) {
|
||||
rootProject.logger.warn("Paths not found for root: ${root.absolutePath}")
|
||||
return emptyList()
|
||||
}
|
||||
return listOf(dependency)
|
||||
}
|
||||
|
||||
val result = mutableListOf<PDependency>()
|
||||
for (path in paths) {
|
||||
val module = project.modules.find { it.path == path }
|
||||
if (module != null) {
|
||||
result += PDependency.Module(module.name)
|
||||
continue
|
||||
}
|
||||
|
||||
val maybeLibrary = mappings[path]
|
||||
if (maybeLibrary == null) {
|
||||
rootProject.logger.warn("Library not found for root: ${root.absolutePath} ($path)")
|
||||
continue
|
||||
}
|
||||
|
||||
if (maybeLibrary.isPresent) {
|
||||
result += PDependency.Library(maybeLibrary.get().name)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun File.isParent(child: File): Boolean {
|
||||
var parent = child.parentFile ?: return false
|
||||
while (true) {
|
||||
if (parent == this) {
|
||||
return true
|
||||
}
|
||||
parent = parent.parentFile ?: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachPlatformSources(@Suppress("UNUSED_PARAMETER") project: PProject, dependency: PDependency): List<PDependency> {
|
||||
if (dependency is PDependency.ModuleLibrary) {
|
||||
val library = dependency.library
|
||||
val platformSourcesJar = File(platformDir, "../../../sources/intellij-$platformVersion-sources.jar")
|
||||
|
||||
if (library.classes.any { it.startsWith(platformDir) || it.startsWith(intellijCoreDir) }) {
|
||||
return listOf(dependency.copy(library = library.attachSource(platformSourcesJar)))
|
||||
}
|
||||
}
|
||||
|
||||
return listOf(dependency)
|
||||
}
|
||||
|
||||
private fun attachAsmSources(@Suppress("UNUSED_PARAMETER") project: PProject, dependency: PDependency): List<PDependency> {
|
||||
if (dependency is PDependency.ModuleLibrary) {
|
||||
val library = dependency.library
|
||||
val asmSourcesJar = File(platformDir, "../asm-shaded-sources/asm-src-$platformBaseNumber.jar")
|
||||
val asmAllJar = File(platformDir, "lib/asm-all.jar")
|
||||
|
||||
if (library.classes.any { it == asmAllJar }) {
|
||||
return listOf(dependency.copy(library = library.attachSource(asmSourcesJar)))
|
||||
}
|
||||
}
|
||||
|
||||
return listOf(dependency)
|
||||
}
|
||||
|
||||
private fun PProject.mapDependencies(mappers: List<(PProject, PDependency) -> List<PDependency>>): PProject {
|
||||
fun mapRoot(root: POrderRoot): List<POrderRoot> {
|
||||
val dependencies = mapDependency(root.dependency, mappers)
|
||||
return dependencies.map { root.copy(dependency = it) }
|
||||
}
|
||||
|
||||
val modules = this.modules.map { module ->
|
||||
val newOrderRoots = module.orderRoots.flatMap(::mapRoot).distinct()
|
||||
module.copy(orderRoots = newOrderRoots)
|
||||
}
|
||||
|
||||
return this.copy(modules = modules)
|
||||
}
|
||||
|
||||
private fun PProject.mapDependency(
|
||||
dependency: PDependency,
|
||||
mappers: List<(PProject, PDependency) -> List<PDependency>>
|
||||
): List<PDependency> {
|
||||
var dependencies = listOf(dependency)
|
||||
for (mapper in mappers) {
|
||||
val newDependencies = mutableListOf<PDependency>()
|
||||
for (dep in dependencies) {
|
||||
newDependencies += mapper(this, dep)
|
||||
}
|
||||
dependencies = newDependencies
|
||||
}
|
||||
|
||||
return dependencies
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
|
||||
private val USER_HOME_DIR_PATH = System.getProperty("user.home").withSlash()
|
||||
|
||||
private fun replacePrefix(path: String, prefix: String, variableName: String): String {
|
||||
if (path.startsWith(prefix)) {
|
||||
return "$" + variableName + "$/" + path.drop(prefix.length)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
private fun simplifyUserHomeDirPath(path: String): String {
|
||||
return replacePrefix(path, USER_HOME_DIR_PATH, "USER_HOME")
|
||||
}
|
||||
|
||||
interface PathContext {
|
||||
operator fun invoke(file: File): String
|
||||
|
||||
fun url(file: File): Pair<String, String> {
|
||||
val path = when {
|
||||
file.isFile && file.extension.toLowerCase() == "jar" -> "jar://" + this(file) + "!/"
|
||||
else -> "file://" + this(file)
|
||||
}
|
||||
|
||||
return Pair("url", path)
|
||||
}
|
||||
}
|
||||
|
||||
class ProjectContext private constructor(private val projectDir: File) : PathContext {
|
||||
constructor(project: PProject) : this(project.rootDirectory)
|
||||
constructor(project: Project) : this(project.projectDir)
|
||||
|
||||
override fun invoke(file: File): String {
|
||||
return simplifyUserHomeDirPath(replacePrefix(file.absolutePath, projectDir.absolutePath.withSlash(), "PROJECT_DIR"))
|
||||
}
|
||||
}
|
||||
|
||||
class ModuleContext(private val project: PProject, val module: PModule) : PathContext {
|
||||
override fun invoke(file: File): String {
|
||||
if (!file.startsWith(project.rootDirectory)) {
|
||||
return simplifyUserHomeDirPath(file.absolutePath)
|
||||
}
|
||||
|
||||
return "\$MODULE_DIR\$/" + file.toRelativeString(module.moduleFile.parentFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.withSlash() = if (this.endsWith("/")) this else ("$this/")
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import java.io.File
|
||||
import org.gradle.api.Project
|
||||
|
||||
open class PillExtensionMirror(variant: String, val excludedDirs: List<File>) {
|
||||
val variant = Variant.valueOf(variant)
|
||||
|
||||
enum class Variant {
|
||||
// Default variant (./gradlew pill)
|
||||
BASE() {
|
||||
override val includes = setOf(BASE)
|
||||
},
|
||||
|
||||
// Full variant (./gradlew pill -Dpill.variant=full)
|
||||
FULL() {
|
||||
override val includes = setOf(BASE, FULL)
|
||||
},
|
||||
|
||||
// Do not import the project to JPS model, but set some options for it
|
||||
NONE() {
|
||||
override val includes = emptySet<Variant>()
|
||||
},
|
||||
|
||||
// 'BASE' if the "jps-compatible" plugin is applied, 'NONE' otherwise
|
||||
DEFAULT() {
|
||||
override val includes = emptySet<Variant>()
|
||||
};
|
||||
|
||||
abstract val includes: Set<Variant>
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.findPillExtensionMirror(): PillExtensionMirror? {
|
||||
val ext = extensions.findByName("pill") ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val serialized = ext::class.java.getMethod("serialize").invoke(ext) as Map<String, Any>
|
||||
|
||||
val variant = serialized["variant"] as String
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val excludedDirs = serialized["excludedDirs"] as List<File>
|
||||
|
||||
val constructor = PillExtensionMirror::class.java.declaredConstructors.single()
|
||||
return constructor.newInstance(variant, excludedDirs) as PillExtensionMirror
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
|
||||
object EmbeddedComponents {
|
||||
lateinit var CONFIGURATION_NAME: String
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
object PillImporter {
|
||||
private val TASKS = mapOf(
|
||||
"pill" to JpsCompatiblePluginTasks::pill,
|
||||
"unpill" to JpsCompatiblePluginTasks::unpill
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun run(rootProject: Project, taskName: String, platformDir: File, resourcesDir: File, embeddedConfigurationName: String) {
|
||||
EmbeddedComponents.CONFIGURATION_NAME = embeddedConfigurationName
|
||||
|
||||
val tasks = JpsCompatiblePluginTasks(rootProject, platformDir, resourcesDir)
|
||||
val task = TASKS[taskName] ?: error("Unknown task $taskName, available tasks: " + TASKS.keys.joinToString())
|
||||
task(tasks)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import shadow.org.jdom2.Document
|
||||
import shadow.org.jdom2.Element
|
||||
import shadow.org.jdom2.output.Format
|
||||
import shadow.org.jdom2.output.XMLOutputter
|
||||
|
||||
fun xml(name: String, vararg args: Pair<String, Any>, block: XmlNode.() -> Unit = {}): XmlNode {
|
||||
return XmlNode(name, args.asList(), block)
|
||||
}
|
||||
|
||||
class XmlNode(val name: String, private val args: List<Pair<String, Any>>, block: XmlNode.() -> Unit = {}) {
|
||||
private val children = mutableListOf<XmlNode>()
|
||||
private var value: Any? = null
|
||||
|
||||
init {
|
||||
@Suppress("UNUSED_EXPRESSION")
|
||||
block()
|
||||
}
|
||||
|
||||
fun xml(name: String, vararg args: Pair<String, Any>, block: XmlNode.() -> Unit = {}) {
|
||||
children += XmlNode(name, args.asList(), block = block)
|
||||
}
|
||||
|
||||
fun add(xml: XmlNode) {
|
||||
children += xml
|
||||
}
|
||||
|
||||
fun raw(text: String) {
|
||||
value = text
|
||||
}
|
||||
|
||||
private fun toElement(): Element {
|
||||
val element = Element(name)
|
||||
|
||||
for (arg in args) {
|
||||
element.setAttribute(arg.first, arg.second.toString())
|
||||
}
|
||||
|
||||
require(value == null || children.isEmpty())
|
||||
|
||||
value?.let { value ->
|
||||
element.addContent(value.toString())
|
||||
}
|
||||
|
||||
for (child in children) {
|
||||
element.addContent(child.toElement())
|
||||
}
|
||||
|
||||
return element
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val document = Document().also { it.rootElement = toElement() }
|
||||
val output = XMLOutputter().also { it.format = Format.getPrettyFormat() }
|
||||
return output.outputString(document)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.jetbrains.kotlin.pill.ArtifactElement.*
|
||||
import java.io.File
|
||||
|
||||
class PArtifact(val artifactName: String, private val outputDir: File, private val contents: Root) {
|
||||
fun render(context: PathContext) = xml("component", "name" to "ArtifactManager") {
|
||||
xml("artifact", "name" to artifactName) {
|
||||
xml("output-path") {
|
||||
raw(context(outputDir))
|
||||
}
|
||||
|
||||
add(contents.renderRecursively(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OpaqueDependencyMapper {
|
||||
fun map(dependency: PDependency): List<PDependency>
|
||||
}
|
||||
|
||||
sealed class ArtifactElement {
|
||||
private val myChildren = mutableListOf<ArtifactElement>()
|
||||
private val children get() = myChildren
|
||||
|
||||
fun add(child: ArtifactElement) {
|
||||
myChildren += child
|
||||
}
|
||||
|
||||
fun add(children: List<ArtifactElement>) {
|
||||
myChildren += children
|
||||
}
|
||||
|
||||
abstract fun render(context: PathContext): XmlNode
|
||||
|
||||
fun renderRecursively(context: PathContext): XmlNode {
|
||||
return render(context).apply {
|
||||
children.forEach { add(it.renderRecursively(context)) }
|
||||
}
|
||||
}
|
||||
|
||||
class Root : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("root", "id" to "root")
|
||||
}
|
||||
|
||||
data class Directory(val name: String) : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("element", "id" to "directory", "name" to name)
|
||||
}
|
||||
|
||||
data class Archive(val name: String) : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("element", "id" to "archive", "name" to name)
|
||||
}
|
||||
|
||||
data class ModuleOutput(val moduleName: String) : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("element", "id" to "module-output", "name" to moduleName)
|
||||
}
|
||||
|
||||
data class FileCopy(val source: File, val outputFileName: String? = null) : ArtifactElement() {
|
||||
override fun render(context: PathContext): XmlNode {
|
||||
val args = mutableListOf("id" to "file-copy", "path" to context(source))
|
||||
if (outputFileName != null) {
|
||||
args += "output-file-name" to outputFileName
|
||||
}
|
||||
|
||||
return xml("element", *args.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
data class DirectoryCopy(val source: File) : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("element", "id" to "dir-copy", "path" to context(source))
|
||||
}
|
||||
|
||||
data class ProjectLibrary(val name: String) : ArtifactElement() {
|
||||
override fun render(context: PathContext) = xml("element", "id" to "library", "level" to "project", "name" to name)
|
||||
}
|
||||
|
||||
data class ExtractedDirectory(val archive: File, val pathInJar: String = "/") : ArtifactElement() {
|
||||
override fun render(context: PathContext) =
|
||||
xml("element", "id" to "extracted-dir", "path" to context(archive), "path-in-jar" to pathInJar)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMapper: OpaqueDependencyMapper): PFile {
|
||||
val root = Root()
|
||||
|
||||
fun Project.getProject(name: String) = findProject(name) ?: error("Cannot find project $name")
|
||||
|
||||
val prepareIdeaPluginProject = rootProject.getProject(":prepare:idea-plugin")
|
||||
|
||||
root.add(Directory("kotlinc").apply {
|
||||
val kotlincDirectory = rootProject.extra["distKotlinHomeDir"].toString()
|
||||
add(DirectoryCopy(File(kotlincDirectory)))
|
||||
})
|
||||
|
||||
root.add(Directory("lib").apply {
|
||||
val librariesConfiguration = prepareIdeaPluginProject.configurations.getByName("libraries")
|
||||
add(getArtifactElements(librariesConfiguration, dependencyMapper, false))
|
||||
|
||||
add(Directory("jps").apply {
|
||||
val prepareJpsPluginProject = rootProject.getProject(":kotlin-jps-plugin")
|
||||
add(Archive(prepareJpsPluginProject.name + ".jar").apply {
|
||||
val jpsPluginConfiguration = prepareIdeaPluginProject.configurations.getByName("jpsPlugin")
|
||||
add(getArtifactElements(jpsPluginConfiguration, dependencyMapper, true))
|
||||
})
|
||||
})
|
||||
|
||||
add(Archive("kotlin-plugin.jar").apply {
|
||||
add(FileCopy(File(rootProject.projectDir, "resources/kotlinManifest.properties")))
|
||||
|
||||
val embeddedConfiguration = prepareIdeaPluginProject.configurations.getByName(EMBEDDED_CONFIGURATION_NAME)
|
||||
add(getArtifactElements(embeddedConfiguration, dependencyMapper, true))
|
||||
})
|
||||
})
|
||||
|
||||
val artifact = PArtifact("KotlinPlugin", File(rootProject.projectDir, "out/artifacts/Kotlin"), root)
|
||||
return PFile(
|
||||
File(rootProject.projectDir, ".idea/artifacts/${artifact.artifactName}.xml"),
|
||||
artifact.render(ProjectContext(rootProject))
|
||||
)
|
||||
}
|
||||
|
||||
private fun getArtifactElements(
|
||||
configuration: Configuration,
|
||||
dependencyMapper: OpaqueDependencyMapper,
|
||||
extractDependencies: Boolean
|
||||
): List<ArtifactElement> {
|
||||
val dependencies = parseDependencies(configuration, dependencyMapper)
|
||||
|
||||
val artifacts = mutableListOf<ArtifactElement>()
|
||||
|
||||
for (dependency in dependencies) {
|
||||
when (dependency) {
|
||||
is PDependency.Module -> {
|
||||
val moduleOutput = ModuleOutput(dependency.name)
|
||||
|
||||
if (extractDependencies) {
|
||||
artifacts += moduleOutput
|
||||
} else {
|
||||
artifacts += Archive(dependency.name + ".jar").apply {
|
||||
add(moduleOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PDependency.Library -> artifacts += ProjectLibrary(dependency.name)
|
||||
is PDependency.ModuleLibrary -> {
|
||||
val files = dependency.library.classes
|
||||
if (extractDependencies) {
|
||||
files.mapTo(artifacts) { ExtractedDirectory(it) }
|
||||
} else {
|
||||
files.mapTo(artifacts) { FileCopy(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return artifacts
|
||||
}
|
||||
|
||||
private fun parseDependencies(configuration: Configuration, dependencyMapper: OpaqueDependencyMapper): List<PDependency> {
|
||||
val dependencies = mutableListOf<PDependency>()
|
||||
for (file in configuration.resolve()) {
|
||||
val library = PLibrary(file.name, listOf(file))
|
||||
dependencies += dependencyMapper.map(PDependency.ModuleLibrary(library))
|
||||
}
|
||||
return dependencies
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.plugins.ide.idea.IdeaPlugin
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.internal.HasConvention
|
||||
import org.gradle.api.internal.file.copy.CopySpecInternal
|
||||
import org.gradle.api.internal.file.copy.SingleParentCopySpec
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.language.jvm.tasks.ProcessResources
|
||||
import org.jetbrains.kotlin.pill.POrderRoot.*
|
||||
import org.jetbrains.kotlin.pill.PSourceRoot.*
|
||||
import org.jetbrains.kotlin.pill.PillExtensionMirror.*
|
||||
import java.io.File
|
||||
|
||||
class ParserContext(val variant: Variant)
|
||||
|
||||
typealias OutputDir = String
|
||||
typealias GradleProjectPath = String
|
||||
|
||||
data class PProject(
|
||||
val name: String,
|
||||
val rootDirectory: File,
|
||||
val modules: List<PModule>,
|
||||
val libraries: List<PLibrary>,
|
||||
val artifacts: Map<OutputDir, List<GradleProjectPath>>
|
||||
)
|
||||
|
||||
data class PModule(
|
||||
val name: String,
|
||||
val path: GradleProjectPath,
|
||||
val forTests: Boolean,
|
||||
val rootDirectory: File,
|
||||
val moduleFile: File,
|
||||
val contentRoots: List<PContentRoot>,
|
||||
val orderRoots: List<POrderRoot>,
|
||||
val kotlinOptions: PSourceRootKotlinOptions?,
|
||||
val moduleForProductionSources: PModule? = null
|
||||
)
|
||||
|
||||
data class PContentRoot(
|
||||
val path: File,
|
||||
val sourceRoots: List<PSourceRoot>,
|
||||
val excludedDirectories: List<File>
|
||||
)
|
||||
|
||||
data class PSourceSet(
|
||||
val name: String,
|
||||
val forTests: Boolean,
|
||||
val sourceDirectories: List<File>,
|
||||
val resourceDirectories: List<File>,
|
||||
val kotlinOptions: PSourceRootKotlinOptions?,
|
||||
val compileClasspathConfigurationName: String,
|
||||
val runtimeClasspathConfigurationName: String
|
||||
)
|
||||
|
||||
data class PSourceRoot(val directory: File, val kind: Kind) {
|
||||
enum class Kind { PRODUCTION, TEST, RESOURCES, TEST_RESOURCES }
|
||||
}
|
||||
|
||||
data class PSourceRootKotlinOptions(
|
||||
val noStdlib: Boolean?,
|
||||
val noReflect: Boolean?,
|
||||
val moduleName: String?,
|
||||
val apiVersion: String?,
|
||||
val languageVersion: String?,
|
||||
val jvmTarget: String?,
|
||||
val extraArguments: List<String>
|
||||
)
|
||||
|
||||
data class POrderRoot(
|
||||
val dependency: PDependency,
|
||||
val scope: Scope,
|
||||
val isExported: Boolean = false,
|
||||
val isProductionOnTestDependency: Boolean = false
|
||||
) {
|
||||
enum class Scope { COMPILE, TEST, RUNTIME, PROVIDED }
|
||||
}
|
||||
|
||||
sealed class PDependency {
|
||||
data class Module(val name: String) : PDependency()
|
||||
data class Library(val name: String) : PDependency()
|
||||
data class ModuleLibrary(val library: PLibrary) : PDependency()
|
||||
}
|
||||
|
||||
data class PLibrary(
|
||||
val name: String,
|
||||
val classes: List<File>,
|
||||
val javadoc: List<File> = emptyList(),
|
||||
val sources: List<File> = emptyList(),
|
||||
val annotations: List<File> = emptyList(),
|
||||
val dependencies: List<PLibrary> = emptyList(),
|
||||
val originalName: String = name
|
||||
) {
|
||||
fun attachSource(file: File): PLibrary {
|
||||
return this.copy(sources = this.sources + listOf(file))
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(project: Project, context: ParserContext): PProject = with(context) {
|
||||
if (project != project.rootProject) {
|
||||
error("$project is not a root project")
|
||||
}
|
||||
|
||||
fun Project.matchesSelectedVariant(): Boolean {
|
||||
val extension = this.findPillExtensionMirror() ?: return true
|
||||
val projectVariant = extension.variant.takeUnless { it == Variant.DEFAULT } ?: Variant.BASE
|
||||
return projectVariant in context.variant.includes
|
||||
}
|
||||
|
||||
val (includedProjects, excludedProjects) = project.allprojects
|
||||
.partition { it.plugins.hasPlugin("jps-compatible") && it.matchesSelectedVariant() }
|
||||
|
||||
val modules = includedProjects.flatMap { parseModules(it, excludedProjects) }
|
||||
val artifacts = parseArtifacts(project)
|
||||
|
||||
return PProject("Kotlin", project.projectDir, modules, emptyList(), artifacts)
|
||||
}
|
||||
|
||||
private fun parseArtifacts(rootProject: Project): Map<String, List<GradleProjectPath>> {
|
||||
val artifacts = HashMap<OutputDir, List<GradleProjectPath>>()
|
||||
val additionalOutputs = HashMap<OutputDir, List<OutputDir>>()
|
||||
|
||||
for (project in rootProject.allprojects) {
|
||||
val sourceSets = project.sourceSets?.toList() ?: emptyList()
|
||||
|
||||
for (sourceSet in sourceSets) {
|
||||
val path = makePath(project, sourceSet.name)
|
||||
|
||||
for (output in sourceSet.output.toList()) {
|
||||
artifacts[output.absolutePath] = listOf(path)
|
||||
}
|
||||
|
||||
val jarTask = project.tasks.findByName(sourceSet.jarTaskName) as? Jar ?: continue
|
||||
val embeddedTask = findEmbeddableTask(project, sourceSet)
|
||||
|
||||
for (task in listOfNotNull(jarTask, embeddedTask)) {
|
||||
val archiveFile = task.archiveFile.get().asFile
|
||||
artifacts[archiveFile.absolutePath] = listOf(path)
|
||||
|
||||
val additionalOutputsForSourceSet = mutableListOf<File>()
|
||||
fun process(spec: CopySpecInternal) {
|
||||
spec.children.forEach { process(it) }
|
||||
if (spec is SingleParentCopySpec) {
|
||||
for (sourcePath in spec.sourcePaths) {
|
||||
if (sourcePath is SourceSetOutput) {
|
||||
additionalOutputsForSourceSet += sourcePath.classesDirs
|
||||
sourcePath.resourcesDir?.let { additionalOutputsForSourceSet += it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process(task.rootSpec)
|
||||
additionalOutputs[archiveFile.absolutePath] = additionalOutputsForSourceSet.map { it.absolutePath }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((sourceSetOutputDir, additionalOutputsForSourceSet) in additionalOutputs) {
|
||||
val projectPaths = artifacts[sourceSetOutputDir] ?: error("Unknown artifact $sourceSetOutputDir")
|
||||
val newPaths = projectPaths + additionalOutputsForSourceSet.mapNotNull { artifacts[it] }.flatten()
|
||||
artifacts[sourceSetOutputDir] = newPaths.distinct()
|
||||
}
|
||||
|
||||
return artifacts
|
||||
}
|
||||
|
||||
private fun findEmbeddableTask(project: Project, sourceSet: SourceSet): Jar? {
|
||||
val jarName = sourceSet.jarTaskName
|
||||
val embeddable = "embeddable"
|
||||
val embeddedName = if (jarName == "jar") embeddable else jarName.dropLast("jar".length) + embeddable.capitalize()
|
||||
return project.tasks.findByName(embeddedName) as? Jar
|
||||
}
|
||||
|
||||
private fun makePath(project: Project, sourceSetName: String): GradleProjectPath {
|
||||
return project.path + "/" + sourceSetName
|
||||
}
|
||||
|
||||
private fun parseModules(project: Project, excludedProjects: List<Project>): List<PModule> {
|
||||
val modules = mutableListOf<PModule>()
|
||||
|
||||
fun getModuleFile(name: String): File {
|
||||
val relativePath = File(project.projectDir, "$name.iml").toRelativeString(project.rootProject.projectDir)
|
||||
return File(project.rootProject.projectDir, ".idea/modules/$relativePath")
|
||||
}
|
||||
|
||||
val sourceSets = parseSourceSets(project).sortedBy { it.forTests }
|
||||
for (sourceSet in sourceSets) {
|
||||
val sourceRoots = mutableListOf<PSourceRoot>()
|
||||
|
||||
for (dir in sourceSet.sourceDirectories) {
|
||||
sourceRoots += PSourceRoot(dir, if (sourceSet.forTests) Kind.TEST else Kind.PRODUCTION)
|
||||
}
|
||||
|
||||
for (dir in sourceSet.resourceDirectories) {
|
||||
sourceRoots += PSourceRoot(dir, if (sourceSet.forTests) Kind.TEST_RESOURCES else Kind.RESOURCES)
|
||||
}
|
||||
|
||||
if (sourceRoots.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
val productionModule = if (sourceSet.forTests) modules.firstOrNull { !it.forTests } else null
|
||||
|
||||
val contentRoots = sourceRoots.map { PContentRoot(it.directory, listOf(it), emptyList()) }
|
||||
|
||||
var orderRoots = parseDependencies(project, sourceSet)
|
||||
if (productionModule != null) {
|
||||
val productionModuleDependency = PDependency.Module(productionModule.name)
|
||||
orderRoots = listOf(POrderRoot(productionModuleDependency, Scope.COMPILE, true)) + orderRoots
|
||||
}
|
||||
|
||||
val name = project.pillModuleName + "." + sourceSet.name
|
||||
|
||||
modules += PModule(
|
||||
name = name,
|
||||
path = makePath(project, sourceSet.name),
|
||||
forTests = sourceSet.forTests,
|
||||
rootDirectory = sourceRoots.first().directory,
|
||||
moduleFile = getModuleFile(name),
|
||||
contentRoots = contentRoots,
|
||||
orderRoots = orderRoots,
|
||||
kotlinOptions = sourceSet.kotlinOptions,
|
||||
moduleForProductionSources = productionModule
|
||||
)
|
||||
}
|
||||
|
||||
val mainModuleFileRelativePath = when (project) {
|
||||
project.rootProject -> File(project.rootProject.projectDir, project.rootProject.name + ".iml")
|
||||
else -> getModuleFile(project.pillModuleName)
|
||||
}
|
||||
|
||||
modules += PModule(
|
||||
name = project.pillModuleName,
|
||||
path = project.path,
|
||||
forTests = false,
|
||||
rootDirectory = project.projectDir,
|
||||
moduleFile = mainModuleFileRelativePath,
|
||||
contentRoots = listOf(PContentRoot(project.projectDir, listOf(), getExcludedDirs(project, excludedProjects))),
|
||||
orderRoots = emptyList(),
|
||||
kotlinOptions = null,
|
||||
moduleForProductionSources = null
|
||||
)
|
||||
|
||||
return modules
|
||||
}
|
||||
|
||||
private fun getExcludedDirs(project: Project, excludedProjects: List<Project>): List<File> {
|
||||
fun getJavaExcludedDirs() = project.plugins.findPlugin(IdeaPlugin::class.java)
|
||||
?.model?.module?.excludeDirs?.toList() ?: emptyList()
|
||||
|
||||
fun getPillExcludedDirs() = project.findPillExtensionMirror()?.excludedDirs ?: emptyList()
|
||||
|
||||
return getPillExcludedDirs() + getJavaExcludedDirs() + project.buildDir +
|
||||
(if (project == project.rootProject) excludedProjects.map { it.buildDir } else emptyList())
|
||||
}
|
||||
|
||||
private fun parseSourceSets(project: Project): List<PSourceSet> {
|
||||
if (!project.plugins.hasPlugin(JavaPlugin::class.java)) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val kotlinTasksBySourceSet = project.tasks.names
|
||||
.filter { it.startsWith("compile") && it.endsWith("Kotlin") }
|
||||
.map { project.tasks.getByName(it) }
|
||||
.associateBy { it.invokeInternal("getSourceSetName") }
|
||||
|
||||
val gradleSourceSets = project.sourceSets?.toList() ?: emptyList()
|
||||
val sourceSets = mutableListOf<PSourceSet>()
|
||||
|
||||
for (sourceSet in gradleSourceSets) {
|
||||
val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name]
|
||||
|
||||
fun Any.getKotlin(): SourceDirectorySet {
|
||||
val kotlinMethod = javaClass.getMethod("getKotlin")
|
||||
kotlinMethod.isAccessible = true
|
||||
return kotlinMethod(this) as SourceDirectorySet
|
||||
}
|
||||
|
||||
val kotlinSourceDirectories = (sourceSet as HasConvention).convention
|
||||
.plugins["kotlin"]?.getKotlin()?.srcDirs ?: emptySet()
|
||||
|
||||
val sourceDirectories = (sourceSet.java.sourceDirectories.files + kotlinSourceDirectories).toList()
|
||||
|
||||
val resourceDirectoriesFromSourceSet = sourceSet.resources.sourceDirectories.files
|
||||
val resourceDirectoriesFromTask = parseResourceRootsProcessedByProcessResourcesTask(project, sourceSet)
|
||||
|
||||
val resourceDirectories = (resourceDirectoriesFromSourceSet + resourceDirectoriesFromTask)
|
||||
.distinct().filter { it !in sourceDirectories }
|
||||
|
||||
sourceSets += PSourceSet(
|
||||
name = sourceSet.name,
|
||||
forTests = sourceSet.isTestSourceSet,
|
||||
sourceDirectories = sourceDirectories,
|
||||
resourceDirectories = resourceDirectories,
|
||||
kotlinOptions = kotlinCompileTask?.let { getKotlinOptions(it) },
|
||||
compileClasspathConfigurationName = sourceSet.compileClasspathConfigurationName,
|
||||
runtimeClasspathConfigurationName = sourceSet.runtimeClasspathConfigurationName
|
||||
)
|
||||
}
|
||||
|
||||
return sourceSets
|
||||
}
|
||||
|
||||
private fun parseResourceRootsProcessedByProcessResourcesTask(project: Project, sourceSet: SourceSet): List<File> {
|
||||
val isMainSourceSet = sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME
|
||||
|
||||
val taskNameBase = "processResources"
|
||||
val taskName = if (isMainSourceSet) taskNameBase else sourceSet.name + taskNameBase.capitalize()
|
||||
val task = project.tasks.findByName(taskName) as? ProcessResources ?: return emptyList()
|
||||
|
||||
val roots = mutableListOf<File>()
|
||||
fun collectRoots(spec: CopySpecInternal) {
|
||||
if (spec is SingleParentCopySpec && spec.children.none()) {
|
||||
roots += spec.sourcePaths.map { File(project.projectDir, it.toString()) }.filter { it.exists() }
|
||||
return
|
||||
}
|
||||
|
||||
spec.children.forEach(::collectRoots)
|
||||
}
|
||||
collectRoots(task.rootSpec)
|
||||
return roots
|
||||
}
|
||||
|
||||
private val SourceSet.isTestSourceSet: Boolean
|
||||
get() = name == SourceSet.TEST_SOURCE_SET_NAME
|
||||
|| name.endsWith("Test")
|
||||
|| name.endsWith("Tests")
|
||||
|
||||
private fun getKotlinOptions(kotlinCompileTask: Any): PSourceRootKotlinOptions? {
|
||||
val compileArguments = run {
|
||||
val method = kotlinCompileTask::class.java.getMethod("getSerializedCompilerArguments")
|
||||
method.isAccessible = true
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
method.invoke(kotlinCompileTask) as List<String>
|
||||
}
|
||||
|
||||
fun parseBoolean(name: String) = compileArguments.contains("-$name")
|
||||
fun parseString(name: String) = compileArguments.dropWhile { it != "-$name" }.drop(1).firstOrNull()
|
||||
|
||||
fun isOptionForScriptingCompilerPlugin(option: String): Boolean {
|
||||
return option.startsWith("-Xplugin=") && option.contains("kotlin-scripting-compiler")
|
||||
}
|
||||
|
||||
val extraArguments = compileArguments.filter {
|
||||
it.startsWith("-X") && !isOptionForScriptingCompilerPlugin(it)
|
||||
}
|
||||
|
||||
return PSourceRootKotlinOptions(
|
||||
parseBoolean("no-stdlib"),
|
||||
parseBoolean("no-reflect"),
|
||||
parseString("module-name"),
|
||||
parseString("api-version"),
|
||||
parseString("language-version"),
|
||||
parseString("jvm-target"),
|
||||
extraArguments
|
||||
)
|
||||
}
|
||||
|
||||
private fun Any.invokeInternal(name: String, instance: Any = this): Any? {
|
||||
val method = javaClass.methods.single { it.name.startsWith(name) && it.parameterTypes.isEmpty() }
|
||||
method.isAccessible = true
|
||||
return method.invoke(instance)
|
||||
}
|
||||
|
||||
private fun parseDependencies(project: Project, sourceSet: PSourceSet): List<POrderRoot> {
|
||||
val roots = mutableListOf<POrderRoot>()
|
||||
|
||||
fun process(name: String, scope: Scope) {
|
||||
val configuration = project.configurations.findByName(name) ?: return
|
||||
for (file in configuration.resolve()) {
|
||||
val library = PLibrary(file.name, listOf(file))
|
||||
val dependency = PDependency.ModuleLibrary(library)
|
||||
roots += POrderRoot(dependency, scope)
|
||||
}
|
||||
}
|
||||
|
||||
process(sourceSet.compileClasspathConfigurationName, Scope.PROVIDED)
|
||||
process(sourceSet.runtimeClasspathConfigurationName, Scope.RUNTIME)
|
||||
|
||||
if (sourceSet.forTests) {
|
||||
process("jpsTest", Scope.TEST)
|
||||
}
|
||||
|
||||
return roots
|
||||
}
|
||||
|
||||
val Project.pillModuleName: String
|
||||
get() = path.removePrefix(":").replace(':', '.')
|
||||
|
||||
val Project.sourceSets: SourceSetContainer?
|
||||
get() {
|
||||
val convention = project.convention.findPlugin(JavaPluginConvention::class.java) ?: return null
|
||||
return convention.sourceSets
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import java.io.File
|
||||
|
||||
class PFile(val path: File, private val text: String) {
|
||||
constructor(path: File, xml: XmlNode) : this(path, xml.toString())
|
||||
|
||||
fun write() {
|
||||
path.parentFile.mkdirs()
|
||||
path.writeText(text)
|
||||
}
|
||||
}
|
||||
|
||||
fun render(project: PProject): List<PFile> {
|
||||
val files = mutableListOf<PFile>()
|
||||
|
||||
files += renderModulesFile(project)
|
||||
project.modules.forEach { files += renderModule(project, it) }
|
||||
project.libraries.forEach { files += renderLibrary(project, it) }
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
private fun renderModulesFile(project: PProject) = PFile(
|
||||
File(project.rootDirectory, ".idea/modules.xml"),
|
||||
xml("project", "version" to 4) {
|
||||
xml("component", "name" to "ProjectModuleManager") {
|
||||
xml("modules") {
|
||||
val pathContext = ProjectContext(project)
|
||||
|
||||
for (module in project.modules) {
|
||||
val moduleFilePath = pathContext(module.moduleFile)
|
||||
xml("module", "fileurl" to "file://$moduleFilePath", "filepath" to moduleFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun renderModule(project: PProject, module: PModule) = PFile(
|
||||
module.moduleFile,
|
||||
xml(
|
||||
"module",
|
||||
"type" to "JAVA_MODULE",
|
||||
"version" to 4
|
||||
) {
|
||||
val moduleForProductionSources = module.moduleForProductionSources
|
||||
if (moduleForProductionSources != null) {
|
||||
xml("component", "name" to "TestModuleProperties", "production-module" to moduleForProductionSources.name)
|
||||
}
|
||||
|
||||
val kotlinCompileOptions = module.kotlinOptions
|
||||
val pathContext = ModuleContext(project, module)
|
||||
|
||||
val platformVersion = (kotlinCompileOptions?.jvmTarget ?: "1.8")
|
||||
val classesDirectory = File(project.rootDirectory, "out/production/${module.name}")
|
||||
|
||||
if (kotlinCompileOptions != null) {
|
||||
xml("component", "name" to "FacetManager") {
|
||||
xml("facet", "type" to "kotlin-language", "name" to "Kotlin") {
|
||||
xml("configuration", "version" to 3, "platform" to "JVM $platformVersion", "useProjectSettings" to "false") {
|
||||
xml("compilerSettings") {
|
||||
xml(
|
||||
"option",
|
||||
"name" to "additionalArguments",
|
||||
"value" to kotlinCompileOptions.extraArguments.joinToString(" ")
|
||||
)
|
||||
}
|
||||
xml("compilerArguments") {
|
||||
xml("option", "name" to "destination", "value" to pathContext(classesDirectory))
|
||||
|
||||
fun Any?.option(name: String) {
|
||||
if (this != null) xml("option", "name" to name, "value" to this.toString())
|
||||
}
|
||||
|
||||
kotlinCompileOptions.noStdlib.option("noStdlib")
|
||||
kotlinCompileOptions.noReflect.option("noReflect")
|
||||
module.name.option("moduleName")
|
||||
xml("option", "name" to "jvmTarget", "value" to platformVersion)
|
||||
kotlinCompileOptions.languageVersion.option("languageVersion")
|
||||
kotlinCompileOptions.apiVersion.option("apiVersion")
|
||||
|
||||
xml("option", "name" to "pluginOptions") { xml("array") }
|
||||
xml("option", "name" to "pluginClasspaths") { xml("array") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xml(
|
||||
"component",
|
||||
"name" to "NewModuleRootManager",
|
||||
"LANGUAGE_LEVEL" to "JDK_${platformVersion.replace('.', '_')}",
|
||||
"inherit-compiler-output" to "true"
|
||||
) {
|
||||
xml("exclude-output")
|
||||
|
||||
for (contentRoot in module.contentRoots.filter { it.path.exists() }) {
|
||||
xml("content", pathContext.url(contentRoot.path)) {
|
||||
for (sourceRoot in contentRoot.sourceRoots) {
|
||||
var args = arrayOf(pathContext.url(sourceRoot.directory))
|
||||
|
||||
args += when (sourceRoot.kind) {
|
||||
PSourceRoot.Kind.PRODUCTION -> ("isTestSource" to "false")
|
||||
PSourceRoot.Kind.TEST -> ("isTestSource" to "true")
|
||||
PSourceRoot.Kind.RESOURCES -> ("type" to "java-resource")
|
||||
PSourceRoot.Kind.TEST_RESOURCES -> ("type" to "java-test-resource")
|
||||
}
|
||||
|
||||
xml("sourceFolder", *args)
|
||||
}
|
||||
|
||||
for (excludedDir in contentRoot.excludedDirectories) {
|
||||
xml("excludeFolder", pathContext.url(excludedDir))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xml("orderEntry", "type" to "inheritedJdk")
|
||||
|
||||
xml("orderEntry", "type" to "sourceFolder", "forTests" to "false")
|
||||
|
||||
for (orderRoot in module.orderRoots) {
|
||||
val dependency = orderRoot.dependency
|
||||
|
||||
val args = when (dependency) {
|
||||
is PDependency.ModuleLibrary -> mutableListOf(
|
||||
"type" to "module-library"
|
||||
)
|
||||
is PDependency.Module -> mutableListOf(
|
||||
"type" to "module",
|
||||
"module-name" to dependency.name
|
||||
)
|
||||
is PDependency.Library -> mutableListOf(
|
||||
"type" to "library",
|
||||
"name" to dependency.name,
|
||||
"level" to "project"
|
||||
)
|
||||
}
|
||||
|
||||
if (orderRoot.scope != POrderRoot.Scope.COMPILE) {
|
||||
args.add(1, "scope" to orderRoot.scope.toString())
|
||||
}
|
||||
|
||||
if (dependency is PDependency.Module && orderRoot.isProductionOnTestDependency) {
|
||||
args += ("production-on-test" to "")
|
||||
}
|
||||
|
||||
if (orderRoot.isExported) {
|
||||
args += ("exported" to "")
|
||||
}
|
||||
|
||||
xml("orderEntry", *args.toTypedArray()) {
|
||||
if (dependency is PDependency.ModuleLibrary) {
|
||||
add(renderLibraryToXml(dependency.library, pathContext, named = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun renderLibrary(project: PProject, library: PLibrary): PFile {
|
||||
val pathContext = ProjectContext(project)
|
||||
|
||||
// TODO find how IDEA escapes library names
|
||||
val escapedName = library.renderName().replace(" ", "_").replace(".", "_").replace("-", "_")
|
||||
|
||||
return PFile(
|
||||
File(project.rootDirectory, ".idea/libraries/$escapedName.xml"),
|
||||
|
||||
xml("component", "name" to "libraryTable") {
|
||||
add(renderLibraryToXml(library, pathContext))
|
||||
})
|
||||
}
|
||||
|
||||
private fun renderLibraryToXml(library: PLibrary, pathContext: PathContext, named: Boolean = true): XmlNode {
|
||||
val args = if (named) arrayOf("name" to library.renderName()) else emptyArray()
|
||||
|
||||
return xml("library", *args) {
|
||||
xml("CLASSES") {
|
||||
library.classes.forEach { xml("root", pathContext.url(it)) }
|
||||
}
|
||||
|
||||
xml("JAVADOC") {
|
||||
library.javadoc.forEach { xml("root", pathContext.url(it)) }
|
||||
}
|
||||
|
||||
xml("SOURCES") {
|
||||
library.sources.forEach { xml("root", pathContext.url(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PLibrary.renderName() = name.takeIf { it != "unspecified" } ?: classes.first().nameWithoutExtension
|
||||
Reference in New Issue
Block a user