Pill: Import KotlinPlugin artifact

This commit is contained in:
Yan Zhulanow
2018-02-20 01:47:36 +03:00
parent bc78d2c417
commit 07ede20dc5
17 changed files with 242 additions and 25 deletions

1
.gitignore vendored
View File

@@ -29,6 +29,7 @@ build/
!**/testData/**/*.iml
.idea/libraries/Gradle*.xml
.idea/libraries/Maven*.xml
.idea/artifacts
.idea/modules
.idea/runConfigurations/JPS_*.xml
.idea/libraries

View File

@@ -0,0 +1,183 @@
@file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.internal.file.copy.SingleParentCopySpec
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.Copy
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.extra
import org.jetbrains.kotlin.pill.ArtifactElement.*
import org.jetbrains.kotlin.pill.POrderRoot.*
import java.io.File
class PArtifact(val artifactName: String, val outputDir: File, private val contents: ArtifactElement.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))
}
}
}
sealed class ArtifactElement {
private val myChildren = mutableListOf<ArtifactElement>()
val children get() = myChildren
fun add(child: ArtifactElement) {
myChildren += child
}
abstract fun render(context: PathContext): xml
fun renderRecursively(context: PathContext): xml {
return render(context).apply {
children.forEach { add(it.renderRecursively(context)) }
}
}
fun getDirectory(path: String): ArtifactElement {
if (path.isEmpty()) {
return this
}
var current: ArtifactElement = this
for (segment in path.split("/")) {
val existing = current.children.firstOrNull { it is Directory && it.name == segment }
if (existing != null) {
current = existing
continue
}
current = Directory(segment).also { current.add(it) }
}
return current
}
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) : ArtifactElement() {
override fun render(context: PathContext) = xml("element", "id" to "file-copy", "path" to context(source))
}
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): PFile {
val mainIdeaPluginTask = rootProject.tasks.getByName("ideaPlugin") as Copy
val gradleArtifactDir = mainIdeaPluginTask.destinationDir
val ideaPluginTasks = mainIdeaPluginTask.taskDependencies
.getDependencies(mainIdeaPluginTask)
.filter { it.name == "ideaPlugin" }
.filterIsInstance<Copy>()
val root = Root()
// Copy kotlinc directory
root.add(DirectoryCopy(File(rootProject.extra["distKotlinHomeDir"].toString())))
for (task in ideaPluginTasks) {
val spec = task.rootSpec.children.filterIsInstance<SingleParentCopySpec>().singleOrNull()
?: error("Copy spec is not unique in ${rootProject.name}. Available specs: ${task.rootSpec.children}")
val sourcePaths = spec.sourcePaths
for (sourcePath in sourcePaths) {
if (sourcePath is ShadowJar) {
if (sourcePath.project.path == ":prepare:idea-plugin") {
val kotlinPluginJar = Archive(sourcePath.archiveName).also { root.getDirectory("lib").add(it) }
kotlinPluginJar.add(FileCopy(File(rootProject.projectDir, "resources/kotlinManifest.properties")))
for (jarFile in sourcePath.project.configurations.getByName("packedJars").resolve()) {
kotlinPluginJar.add(ExtractedDirectory(jarFile))
}
@Suppress("UNCHECKED_CAST")
for (projectPath in sourcePath.project.extra["projectsToShadow"] as List<String>) {
val jpsModuleName = rootProject.findProject(projectPath)!!.name + ".src"
kotlinPluginJar.add(ModuleOutput(jpsModuleName))
}
continue
}
}
when (sourcePath) {
is Jar -> {
val targetDir = ("lib/" + task.destinationDir.toRelativeString(gradleArtifactDir)).withoutSlash()
val archiveForJar = Archive(sourcePath.project.name + ".jar").apply {
if (task.project.plugins.hasPlugin(JavaPlugin::class.java)) {
add(ModuleOutput(sourcePath.project.name + ".src"))
}
root.getDirectory(targetDir).add(this)
}
val fatJarContentsConfiguration = sourcePath.project.configurations
.findByName("fatJarContents")?.resolvedConfiguration
if (fatJarContentsConfiguration != null) {
for ((_, _, dependency) in listOf(fatJarContentsConfiguration to Scope.COMPILE).collectDependencies()) {
if (dependency.configuration == "runtimeElements") {
archiveForJar.add(ModuleOutput(dependency.moduleName + ".src"))
} else if (dependency.configuration == "tests-jar" || dependency.configuration == "jpsTest") {
error("Test configurations are not allowed here")
} else {
for (file in dependency.moduleArtifacts.map { it.file }) {
archiveForJar.add(ExtractedDirectory(file))
}
}
}
}
}
is Configuration -> {
require(sourcePath.name == "sideJars") { "Configurations other than 'sideJars' are not supported" }
for (file in sourcePath.resolve()) {
root.getDirectory("lib").add(FileCopy(file))
}
}
else -> error("${task.name} Unexpected task type ${task.javaClass.name}")
}
}
}
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))
)
}

View File

@@ -345,9 +345,9 @@ private fun removeDuplicates(roots: List<POrderRoot>): List<POrderRoot> {
return result.toList()
}
private data class DependencyInfo(val configuration: ResolvedConfiguration, val scope: Scope, val dependency: ResolvedDependency)
data class DependencyInfo(val configuration: ResolvedConfiguration, val scope: Scope, val dependency: ResolvedDependency)
private fun List<Pair<ResolvedConfiguration, Scope>>.collectDependencies(): List<DependencyInfo> {
fun List<Pair<ResolvedConfiguration, Scope>>.collectDependencies(): List<DependencyInfo> {
val dependencies = mutableListOf<DependencyInfo>()
val unprocessed = LinkedList<DependencyInfo>()

View File

@@ -1,6 +1,7 @@
@file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill
import org.gradle.api.Project
import java.io.File
interface PathContext {
@@ -16,10 +17,12 @@ interface PathContext {
}
}
class ProjectContext(val project: PProject) : PathContext {
class ProjectContext private constructor(val projectDir: File) : PathContext {
constructor(project: PProject) : this(project.rootDirectory)
constructor(project: Project) : this(project.projectDir)
override fun invoke(file: File): String {
return file.absolutePath
.replace(project.rootDirectory.absolutePath, "\$PROJECT_DIR\$")
return file.absolutePath.replace(projectDir.absolutePath, "\$PROJECT_DIR\$")
}
}
@@ -29,11 +32,12 @@ class ModuleContext(val project: PProject, val module: PModule) : PathContext {
return file.absolutePath
}
fun String.withSlash() = if (this.endsWith("/")) this else (this + "/")
return "\$MODULE_DIR\$/" +
project.rootDirectory.toRelativeString(module.moduleFile.parentFile).withSlash() +
module.rootDirectory.toRelativeString(project.rootDirectory).withSlash() +
file.toRelativeString(module.rootDirectory)
}
}
}
fun String.withSlash() = if (this.endsWith("/")) this else (this + "/")
fun String.withoutSlash() = this.trimEnd('/')

View File

@@ -4,14 +4,9 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import shadow.org.jdom2.input.SAXBuilder
import shadow.org.jdom2.*
import shadow.org.jdom2.output.DOMOutputter
import shadow.org.jdom2.output.Format
import shadow.org.jdom2.output.XMLOutputter
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.*
import java.io.File
import java.io.FileOutputStream
import javax.xml.transform.stream.StreamResult
class JpsCompatiblePlugin : Plugin<Project> {
override fun apply(project: Project) {
@@ -104,6 +99,7 @@ class JpsCompatibleRootPlugin : Plugin<Project> {
}
}
private lateinit var projectDir: File
private lateinit var platformVersion: String
private lateinit var platformBaseNumber: String
@@ -123,6 +119,8 @@ class JpsCompatibleRootPlugin : Plugin<Project> {
val jpsProject = parse(project, ParserContext(dependencyMappers))
.mapLibraries(this::attachPlatformSources, this::attachAsmSources)
generateKotlinPluginArtifactFile(project).write()
val files = render(jpsProject, getProjectLibraries(jpsProject))
removeExistingIdeaLibrariesAndModules()
@@ -131,11 +129,7 @@ class JpsCompatibleRootPlugin : Plugin<Project> {
copyRunConfigurations()
setOptionsForDefaultJunitRunConfiguration(project)
for (file in files) {
val stubFile = file.path
stubFile.parentFile.mkdirs()
stubFile.writeText(file.text)
}
files.forEach { it.write() }
}
private fun unpill(project: Project) {

View File

@@ -3,7 +3,13 @@ package org.jetbrains.kotlin.pill
import java.io.File
class PFile(val path: File, val text: String)
class PFile(val path: File, val text: String) {
fun write() {
path.parentFile.mkdirs()
path.writeText(text)
}
}
fun PFile(path: File, xml: xml) = PFile(path, xml.toString())
fun render(project: PProject, extraLibraries: List<PLibrary> = emptyList()): List<PFile> {

View File

@@ -3,7 +3,7 @@
<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" />
<option name="VM_PARAMETERS" value="-Xmx1250m -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -ea -Didea.is.internal=true -Didea.debug.mode=true -Didea.system.path=$IDEA_HOME_PATH$/system -Didea.config.path=$IDEA_HOME_PATH$/config -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.io.useCanonCaches=false -Dplugin.path=$PROJECT_DIR$/dist/artifacts/Kotlin -Dkotlin.internal.mode.enabled=true" />
<option name="VM_PARAMETERS" value="-Xmx1250m -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -ea -Didea.is.internal=true -Didea.debug.mode=true -Didea.system.path=$IDEA_HOME_PATH$/system -Didea.config.path=$IDEA_HOME_PATH$/config -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -Dsun.io.useCanonCaches=false -Dplugin.path=$PROJECT_DIR$/out/artifacts/Kotlin -Dkotlin.internal.mode.enabled=true" />
<option name="WORKING_DIRECTORY" value="file://$IDEA_HOME_PATH$" />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="" />

View File

@@ -14,6 +14,7 @@ class xml(val name: String, vararg val args: Pair<String, Any>, block: xml.() ->
}
private val children = mutableListOf<xml>()
private var value: Any? = null
init {
@Suppress("UNUSED_EXPRESSION")
@@ -28,6 +29,10 @@ class xml(val name: String, vararg val args: Pair<String, Any>, block: xml.() ->
children += xml
}
fun raw(text: String) {
value = text
}
private fun toElement(): Element {
val element = Element(name)
@@ -35,6 +40,12 @@ class xml(val name: String, vararg val args: Pair<String, Any>, block: xml.() ->
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())
}

View File

@@ -9,6 +9,7 @@ jvmTarget = "1.6"
val nativePlatformVariants: List<String> by rootProject.extra
// Do not rename, used in JPS importer
val fatJarContents by configurations.creating
dependencies {

View File

@@ -36,7 +36,6 @@ dist(targetName = the<BasePluginConvention>().archivesBaseName.removePrefix("kot
ideaPlugin {
from(jar)
rename("^kotlin-", "")
}
projectTest {

View File

@@ -6,6 +6,9 @@ apply { plugin("jps-compatible") }
val robolectricClasspath by configurations.creating
// Do not rename, used in JPS importer
val fatJarContents by configurations.creating
dependencies {
testCompile(intellijCoreDep()) { includeJars("intellij-core") }
@@ -30,6 +33,8 @@ dependencies {
testRuntime(intellijPluginDep("junit")) { includeJars("idea-junit", "resources_en") }
robolectricClasspath(commonDep("org.robolectric", "robolectric"))
fatJarContents(project(":kotlin-android-extensions-runtime"))
}
sourceSets {

View File

@@ -39,7 +39,6 @@ dist(targetName = the<BasePluginConvention>().archivesBaseName.removePrefix("kot
ideaPlugin {
from(jar)
rename("^kotlin-", "")
}
projectTest {

View File

@@ -37,7 +37,6 @@ dist {
ideaPlugin {
from(jar)
rename("^kotlin-", "")
}
projectTest {

View File

@@ -13,6 +13,15 @@ sourceSets {
"test" {}
}
// Do not rename, used in JPS importer
val fatJarContents by configurations.creating
dependencies {
projectsToShadow.forEach {p ->
fatJarContents(project(p)) { isTransitive = false }
}
}
runtimeJar {
projectsToShadow.forEach {
dependsOn("$it:classes")

View File

@@ -23,7 +23,9 @@ val shrink =
val compilerManifestClassPath =
"kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar"
// Do not rename, used in JPS importer
val fatJarContents by configurations.creating
val fatJarContentsStripMetadata by configurations.creating
val fatJarContentsStripServices by configurations.creating
val fatSourcesJarContents by configurations.creating

View File

@@ -7,7 +7,8 @@ plugins {
`java-base`
}
val projectsToShadow = listOf(
// Do not rename, used in JPS importer
val projectsToShadow by extra(listOf(
":plugins:annotation-based-compiler-plugins-ide-support",
":compiler:backend",
":compiler:backend-common",
@@ -46,9 +47,11 @@ val projectsToShadow = listOf(
":compiler:resolution",
":compiler:serialization",
":compiler:util",
":core:util.runtime")
":core:util.runtime"))
// Do not rename, used in JPS importer
val packedJars by configurations.creating
val sideJars by configurations.creating
dependencies {

View File

@@ -21,6 +21,7 @@ val projectsToShadow = listOf(
":core:util.runtime",
":plugins:android-extensions-jps")
// Do not rename, used in JPS importer
val fatJarContents by configurations.creating
dependencies {