Pill: Support all Gradle source sets, not just main/test
This commit is contained in:
@@ -46,10 +46,6 @@ plugins {
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("pill-configurable") {
|
||||
id = "pill-configurable"
|
||||
implementationClass = "org.jetbrains.kotlin.pill.PillConfigurablePlugin"
|
||||
}
|
||||
register("jps-compatible") {
|
||||
id = "jps-compatible"
|
||||
implementationClass = "org.jetbrains.kotlin.pill.JpsCompatiblePlugin"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.component.*
|
||||
|
||||
class DependencyMapper(val predicate: (ResolvedArtifact) -> Boolean, val mapping: (ResolvedArtifact) -> MappedDependency?) {
|
||||
companion object {
|
||||
fun forProject(path: String, mapping: (ResolvedArtifact) -> MappedDependency?): DependencyMapper {
|
||||
return DependencyMapper(
|
||||
predicate = { artifact ->
|
||||
val identifier = artifact.id.componentIdentifier as? ProjectComponentIdentifier
|
||||
identifier?.projectPath == path
|
||||
},
|
||||
mapping = mapping
|
||||
)
|
||||
}
|
||||
|
||||
fun forModule(group: String, module: String, version: String?, mapping: (ResolvedArtifact) -> MappedDependency?): DependencyMapper {
|
||||
return DependencyMapper(
|
||||
predicate = { artifact ->
|
||||
val identifier = artifact.id.componentIdentifier as? ModuleComponentIdentifier
|
||||
identifier?.group == group && identifier?.module == module && (version == null || identifier?.version == version)
|
||||
},
|
||||
mapping = mapping
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MappedDependency(val main: PDependency?, val deferred: List<PDependency> = emptyList())
|
||||
|
||||
class ParserContext(val dependencyMappers: List<DependencyMapper>, val variant: PillExtension.Variant)
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import java.io.File
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.component.*
|
||||
|
||||
data class PDependencies(val main: List<PDependency>, val deferred: List<PDependency>) {
|
||||
fun join(): List<PDependency> {
|
||||
return main + deferred
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.resolveDependencies(
|
||||
configuration: Configuration,
|
||||
forTests: Boolean,
|
||||
dependencyMappers: List<DependencyMapper>,
|
||||
withEmbedded: Boolean = false
|
||||
): PDependencies {
|
||||
val dependencies = mutableListOf<PDependency>()
|
||||
val deferred = mutableListOf<PDependency>()
|
||||
|
||||
nextArtifact@ for (artifact in configuration.resolvedConfiguration.resolvedArtifacts) {
|
||||
val identifier = artifact.id.componentIdentifier
|
||||
|
||||
for (mapper in dependencyMappers) {
|
||||
if (mapper.predicate(artifact)) {
|
||||
val mapped = mapper.mapping(artifact)
|
||||
|
||||
if (mapped != null) {
|
||||
mapped.main?.let { dependencies += it }
|
||||
deferred += mapped.deferred
|
||||
}
|
||||
|
||||
continue@nextArtifact
|
||||
}
|
||||
}
|
||||
|
||||
fun addProjectDependency(projectPath: String) {
|
||||
val project = rootProject.findProject(projectPath) ?: error("Cannot find project $projectPath")
|
||||
|
||||
fun addSourceSet(name: String, suffix: String): Boolean {
|
||||
val sourceSet = project.sourceSets?.findByName(name)?.takeIf { !it.allSource.isEmpty() } ?: return false
|
||||
dependencies += PDependency.Module(project.pillModuleName + '.' + suffix)
|
||||
return true
|
||||
}
|
||||
|
||||
if (forTests && artifact.classifier == "tests") {
|
||||
addSourceSet(SourceSet.TEST_SOURCE_SET_NAME, "test") || addSourceSet(SourceSet.MAIN_SOURCE_SET_NAME, "src")
|
||||
} else {
|
||||
addSourceSet(SourceSet.MAIN_SOURCE_SET_NAME, "src")
|
||||
}
|
||||
|
||||
if (withEmbedded) {
|
||||
val embeddedConfiguration = project.configurations.findByName(EmbeddedComponents.CONFIGURATION_NAME)
|
||||
if (embeddedConfiguration != null) {
|
||||
dependencies += resolveDependencies(embeddedConfiguration, forTests, dependencyMappers, withEmbedded).join()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (identifier) {
|
||||
is ProjectComponentIdentifier -> addProjectDependency(identifier.projectPath)
|
||||
is LibraryBinaryIdentifier -> addProjectDependency(identifier.projectPath)
|
||||
is ModuleComponentIdentifier -> {
|
||||
val file = artifact.file
|
||||
val library = PLibrary(file.name, classes = listOf(file))
|
||||
dependencies += PDependency.ModuleLibrary(library)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val existingFiles = mutableSetOf<File>()
|
||||
|
||||
for (dependency in configuration.dependencies) {
|
||||
if (dependency !is SelfResolvingDependency) {
|
||||
continue
|
||||
}
|
||||
|
||||
val files = dependency.resolve().filter { it !in existingFiles }.takeIf { it.isNotEmpty() } ?: continue
|
||||
existingFiles.addAll(files)
|
||||
val library = PLibrary(dependency.name, classes = files.toList())
|
||||
deferred += PDependency.ModuleLibrary(library)
|
||||
}
|
||||
|
||||
return PDependencies(dependencies, deferred)
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
@@ -7,33 +12,34 @@ import org.gradle.api.Project
|
||||
open class PillExtension {
|
||||
enum class Variant {
|
||||
// Default variant (./gradlew pill)
|
||||
BASE() { override val includes = setOf(BASE) },
|
||||
BASE() {
|
||||
override val includes = setOf(BASE)
|
||||
},
|
||||
|
||||
// Full variant (./gradlew pill -Dpill.variant=full)
|
||||
FULL() { override val includes = setOf(BASE, 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>() },
|
||||
NONE() {
|
||||
override val includes = emptySet<Variant>()
|
||||
},
|
||||
|
||||
// 'BASE' if the "jps-compatible" plugin is applied, 'NONE' otherwise
|
||||
DEFAULT() { override val includes = emptySet<Variant>() };
|
||||
DEFAULT() {
|
||||
override val includes = emptySet<Variant>()
|
||||
};
|
||||
|
||||
abstract val includes: Set<Variant>
|
||||
}
|
||||
|
||||
open var variant: Variant = Variant.DEFAULT
|
||||
|
||||
open var importAsLibrary: Boolean = false
|
||||
|
||||
open var excludedDirs: List<File> = emptyList()
|
||||
|
||||
@Suppress("unused")
|
||||
fun Project.excludedDirs(vararg dirs: String) {
|
||||
excludedDirs = excludedDirs + dirs.map { File(projectDir, it) }
|
||||
}
|
||||
|
||||
open var libraryPath: File? = null
|
||||
set(v) {
|
||||
importAsLibrary = true
|
||||
field = v
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@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.artifacts.ProjectDependency
|
||||
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) {
|
||||
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") {
|
||||
@@ -27,6 +25,10 @@ class PArtifact(val artifactName: String, val outputDir: File, private val conte
|
||||
}
|
||||
}
|
||||
|
||||
interface OpaqueDependencyMapper {
|
||||
fun map(dependency: PDependency): List<PDependency>
|
||||
}
|
||||
|
||||
sealed class ArtifactElement {
|
||||
private val myChildren = mutableListOf<ArtifactElement>()
|
||||
val children get() = myChildren
|
||||
@@ -47,25 +49,6 @@ sealed class ArtifactElement {
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -107,7 +90,7 @@ sealed class ArtifactElement {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: List<DependencyMapper>): PFile {
|
||||
fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMapper: OpaqueDependencyMapper): PFile {
|
||||
val root = Root()
|
||||
|
||||
fun Project.getProject(name: String) = findProject(name) ?: error("Cannot find project $name")
|
||||
@@ -121,13 +104,13 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li
|
||||
|
||||
root.add(Directory("lib").apply {
|
||||
val librariesConfiguration = prepareIdeaPluginProject.configurations.getByName("libraries")
|
||||
add(getArtifactElements(rootProject, librariesConfiguration, dependencyMappers, false))
|
||||
add(getArtifactElements(rootProject, 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(rootProject, jpsPluginConfiguration, dependencyMappers, true))
|
||||
add(getArtifactElements(rootProject, jpsPluginConfiguration, dependencyMapper, true))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -135,7 +118,7 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li
|
||||
add(FileCopy(File(rootProject.projectDir, "resources/kotlinManifest.properties")))
|
||||
|
||||
val embeddedConfiguration = prepareIdeaPluginProject.configurations.getByName(EmbeddedComponents.CONFIGURATION_NAME)
|
||||
add(getArtifactElements(rootProject, embeddedConfiguration, dependencyMappers, true))
|
||||
add(getArtifactElements(rootProject, embeddedConfiguration, dependencyMapper, true))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -149,10 +132,10 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li
|
||||
private fun getArtifactElements(
|
||||
rootProject: Project,
|
||||
configuration: Configuration,
|
||||
dependencyMappers: List<DependencyMapper>,
|
||||
dependencyMapper: OpaqueDependencyMapper,
|
||||
extractDependencies: Boolean
|
||||
): List<ArtifactElement> {
|
||||
val dependencies = rootProject.resolveDependencies(configuration, false, dependencyMappers, withEmbedded = true).join()
|
||||
val dependencies = parseDependencies(configuration, dependencyMapper)
|
||||
|
||||
val artifacts = mutableListOf<ArtifactElement>()
|
||||
|
||||
@@ -179,4 +162,13 @@ private fun getArtifactElements(
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -7,51 +7,63 @@
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
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.PillExtension.*
|
||||
import java.io.File
|
||||
import java.util.LinkedList
|
||||
|
||||
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 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 forTests: Boolean,
|
||||
val sourceRoots: List<PSourceRoot>,
|
||||
val excludedDirectories: List<File>
|
||||
)
|
||||
|
||||
data class PSourceRoot(
|
||||
val path: File,
|
||||
val kind: Kind,
|
||||
val kotlinOptions: PSourceRootKotlinOptions?
|
||||
) {
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -63,17 +75,7 @@ data class PSourceRootKotlinOptions(
|
||||
val languageVersion: String?,
|
||||
val jvmTarget: String?,
|
||||
val extraArguments: List<String>
|
||||
) {
|
||||
fun intersect(other: PSourceRootKotlinOptions) = PSourceRootKotlinOptions(
|
||||
if (noStdlib == other.noStdlib) noStdlib else null,
|
||||
if (noReflect == other.noReflect) noReflect else null,
|
||||
if (moduleName == other.moduleName) moduleName else null,
|
||||
if (apiVersion == other.apiVersion) apiVersion else null,
|
||||
if (languageVersion == other.languageVersion) languageVersion else null,
|
||||
if (jvmTarget == other.jvmTarget) jvmTarget else null,
|
||||
extraArguments.intersect(other.extraArguments).toList()
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
data class POrderRoot(
|
||||
val dependency: PDependency,
|
||||
@@ -104,7 +106,7 @@ data class PLibrary(
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(project: Project, libraries: List<PLibrary>, context: ParserContext): PProject = with (context) {
|
||||
fun parse(project: Project, context: ParserContext): PProject = with(context) {
|
||||
if (project != project.rootProject) {
|
||||
error("$project is not a root project")
|
||||
}
|
||||
@@ -119,182 +121,199 @@ fun parse(project: Project, libraries: List<PLibrary>, context: ParserContext):
|
||||
.partition { it.plugins.hasPlugin(JpsCompatiblePlugin::class.java) && it.matchesSelectedVariant() }
|
||||
|
||||
val modules = includedProjects.flatMap { parseModules(it, excludedProjects) }
|
||||
val artifacts = parseArtifacts(project)
|
||||
|
||||
return PProject("Kotlin", project.projectDir, modules, libraries)
|
||||
return PProject("Kotlin", project.projectDir, modules, emptyList(), artifacts)
|
||||
}
|
||||
|
||||
/*
|
||||
Ordering here and below is significant.
|
||||
Placing 'runtime' configuration dependencies on the top make 'idea' tests to run normally.
|
||||
('idea' module has 'intellij-core' as transitive dependency, and we really need to get rid of it.)
|
||||
*/
|
||||
private val CONFIGURATION_MAPPING = mapOf(
|
||||
listOf("runtimeClasspath") to Scope.RUNTIME,
|
||||
listOf("compileClasspath", "compileOnly") to Scope.PROVIDED,
|
||||
listOf("embedded") to Scope.COMPILE
|
||||
)
|
||||
private fun parseArtifacts(rootProject: Project): Map<String, List<GradleProjectPath>> {
|
||||
val artifacts = HashMap<OutputDir, List<GradleProjectPath>>()
|
||||
val additionalOutputs = HashMap<OutputDir, List<OutputDir>>()
|
||||
|
||||
private val TEST_CONFIGURATION_MAPPING = mapOf(
|
||||
listOf("runtimeClasspath", "testRuntimeClasspath") to Scope.RUNTIME,
|
||||
listOf("compileClasspath", "testCompileClasspath", "compileOnly", "testCompileOnly") to Scope.PROVIDED,
|
||||
listOf("jpsTest") to Scope.TEST
|
||||
)
|
||||
for (project in rootProject.allprojects) {
|
||||
val sourceSets = project.sourceSets?.toList() ?: emptyList()
|
||||
|
||||
private fun ParserContext.parseModules(project: Project, excludedProjects: List<Project>): List<PModule> {
|
||||
val (productionContentRoots, testContentRoots) = parseContentRoots(project).partition { !it.forTests }
|
||||
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 getJavaExcludedDirs() = project.plugins.findPlugin(IdeaPlugin::class.java)
|
||||
?.model?.module?.excludeDirs?.toList() ?: emptyList()
|
||||
|
||||
fun getPillExcludedDirs() = project.extensions.getByType(PillExtension::class.java).excludedDirs
|
||||
|
||||
val allExcludedDirs = getPillExcludedDirs() + getJavaExcludedDirs() + project.buildDir +
|
||||
(if (project == project.rootProject) excludedProjects.map { it.buildDir } else emptyList())
|
||||
|
||||
var productionSourcesModule: PModule? = null
|
||||
|
||||
fun getModuleFile(suffix: String = ""): File {
|
||||
val relativePath = File(project.projectDir, project.pillModuleName + suffix + ".iml")
|
||||
.toRelativeString(project.rootProject.projectDir)
|
||||
|
||||
fun getModuleFile(name: String): File {
|
||||
val relativePath = File(project.projectDir, "$name.iml").toRelativeString(project.rootProject.projectDir)
|
||||
return File(project.rootProject.projectDir, ".idea/modules/$relativePath")
|
||||
}
|
||||
|
||||
for ((nameSuffix, roots) in mapOf(".src" to productionContentRoots, ".test" to testContentRoots)) {
|
||||
if (roots.isEmpty()) {
|
||||
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 mainRoot = roots.first()
|
||||
val productionModule = if (sourceSet.forTests) modules.firstOrNull { !it.forTests } else null
|
||||
|
||||
var dependencies = parseDependencies(project, mainRoot.forTests)
|
||||
if (productionContentRoots.isNotEmpty() && mainRoot.forTests) {
|
||||
val productionModuleDependency = PDependency.Module(project.pillModuleName + ".src")
|
||||
dependencies += POrderRoot(productionModuleDependency, Scope.COMPILE, true)
|
||||
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 module = PModule(
|
||||
project.pillModuleName + nameSuffix,
|
||||
mainRoot.path,
|
||||
getModuleFile(nameSuffix),
|
||||
roots,
|
||||
dependencies,
|
||||
productionSourcesModule
|
||||
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
|
||||
)
|
||||
|
||||
modules += module
|
||||
|
||||
if (!mainRoot.forTests) {
|
||||
productionSourcesModule = module
|
||||
}
|
||||
}
|
||||
|
||||
val mainModuleFileRelativePath = when (project) {
|
||||
project.rootProject -> File(project.rootProject.projectDir, project.rootProject.name + ".iml")
|
||||
else -> getModuleFile()
|
||||
else -> getModuleFile(project.pillModuleName)
|
||||
}
|
||||
|
||||
modules += PModule(
|
||||
project.pillModuleName,
|
||||
project.projectDir,
|
||||
mainModuleFileRelativePath,
|
||||
listOf(PContentRoot(project.projectDir, false, emptyList(), allExcludedDirs)),
|
||||
if (modules.isEmpty()) parseDependencies(project, false) else emptyList()
|
||||
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 parseContentRoots(project: Project): List<PContentRoot> {
|
||||
val sourceRoots = parseSourceRoots(project).groupBy { it.kind }
|
||||
fun getRoots(kind: PSourceRoot.Kind) = sourceRoots[kind] ?: emptyList()
|
||||
private fun getExcludedDirs(project: Project, excludedProjects: List<Project>): List<File> {
|
||||
fun getJavaExcludedDirs() = project.plugins.findPlugin(IdeaPlugin::class.java)
|
||||
?.model?.module?.excludeDirs?.toList() ?: emptyList()
|
||||
|
||||
val productionSourceRoots = getRoots(Kind.PRODUCTION) + getRoots(Kind.RESOURCES)
|
||||
val testSourceRoots = getRoots(Kind.TEST) + getRoots(Kind.TEST_RESOURCES)
|
||||
fun getPillExcludedDirs() = project.extensions.getByType(PillExtension::class.java).excludedDirs
|
||||
|
||||
fun createContentRoots(sourceRoots: List<PSourceRoot>, forTests: Boolean): List<PContentRoot> {
|
||||
return sourceRoots.map { PContentRoot(it.path, forTests, listOf(it), emptyList()) }
|
||||
}
|
||||
|
||||
return createContentRoots(productionSourceRoots, forTests = false) +
|
||||
createContentRoots(testSourceRoots, forTests = true)
|
||||
return getPillExcludedDirs() + getJavaExcludedDirs() + project.buildDir +
|
||||
(if (project == project.rootProject) excludedProjects.map { it.buildDir } else emptyList())
|
||||
}
|
||||
|
||||
private fun parseSourceRoots(project: Project): List<PSourceRoot> {
|
||||
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") }
|
||||
.filter { it.startsWith("compile") && it.endsWith("Kotlin") }
|
||||
.map { project.tasks.getByName(it) }
|
||||
.associateBy { it.invokeInternal("getSourceSetName") }
|
||||
|
||||
val sourceRoots = mutableListOf<PSourceRoot>()
|
||||
val gradleSourceSets = project.sourceSets?.toList() ?: emptyList()
|
||||
val sourceSets = mutableListOf<PSourceSet>()
|
||||
|
||||
val sourceSets = project.sourceSets
|
||||
if (sourceSets != null) {
|
||||
for (sourceSet in sourceSets) {
|
||||
val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name]
|
||||
val kind = if (sourceSet.isTestSourceSet) Kind.TEST else Kind.PRODUCTION
|
||||
for (sourceSet in gradleSourceSets) {
|
||||
val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name]
|
||||
|
||||
fun Any.getKotlin(): SourceDirectorySet {
|
||||
val kotlinMethod = javaClass.getMethod("getKotlin")
|
||||
val oldIsAccessible = kotlinMethod.isAccessible
|
||||
try {
|
||||
kotlinMethod.isAccessible = true
|
||||
return kotlinMethod(this) as SourceDirectorySet
|
||||
} finally {
|
||||
kotlinMethod.isAccessible = oldIsAccessible
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinSourceDirectories = (sourceSet as HasConvention).convention
|
||||
.plugins["kotlin"]?.getKotlin()?.srcDirs
|
||||
?: emptySet()
|
||||
|
||||
val directories = (sourceSet.java.sourceDirectories.files + kotlinSourceDirectories).toList()
|
||||
.filter { it.exists() }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: continue
|
||||
|
||||
val kotlinOptions = kotlinCompileTask?.let { getKotlinOptions(it) }
|
||||
|
||||
for (resourceRoot in sourceSet.resources.sourceDirectories.files) {
|
||||
if (!resourceRoot.exists() || resourceRoot in directories) {
|
||||
continue
|
||||
}
|
||||
|
||||
val resourceRootKind = when (kind) {
|
||||
Kind.PRODUCTION -> Kind.RESOURCES
|
||||
Kind.TEST -> Kind.TEST_RESOURCES
|
||||
else -> error("Invalid source root kind $kind")
|
||||
}
|
||||
|
||||
sourceRoots += PSourceRoot(resourceRoot, resourceRootKind, kotlinOptions)
|
||||
}
|
||||
|
||||
for (directory in directories) {
|
||||
sourceRoots += PSourceRoot(directory, kind, kotlinOptions)
|
||||
}
|
||||
|
||||
for (root in parseResourceRootsProcessedByProcessResourcesTask(project, sourceSet)) {
|
||||
if (sourceRoots.none { it.path == root.path }) {
|
||||
sourceRoots += root
|
||||
}
|
||||
}
|
||||
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 sourceRoots
|
||||
return sourceSets
|
||||
}
|
||||
|
||||
private fun parseResourceRootsProcessedByProcessResourcesTask(project: Project, sourceSet: SourceSet): List<PSourceRoot> {
|
||||
private fun parseResourceRootsProcessedByProcessResourcesTask(project: Project, sourceSet: SourceSet): List<File> {
|
||||
val isMainSourceSet = sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME
|
||||
|
||||
val resourceRootKind = if (sourceSet.isTestSourceSet) PSourceRoot.Kind.TEST_RESOURCES else PSourceRoot.Kind.RESOURCES
|
||||
val taskNameBase = "processResources"
|
||||
val taskName = if (isMainSourceSet) taskNameBase else sourceSet.name + taskNameBase.capitalize()
|
||||
val task = project.tasks.findByName(taskName) as? ProcessResources ?: return emptyList()
|
||||
@@ -309,8 +328,7 @@ private fun parseResourceRootsProcessedByProcessResourcesTask(project: Project,
|
||||
spec.children.forEach(::collectRoots)
|
||||
}
|
||||
collectRoots(task.rootSpec)
|
||||
|
||||
return roots.map { PSourceRoot(it, resourceRootKind, null) }
|
||||
return roots
|
||||
}
|
||||
|
||||
private val SourceSet.isTestSourceSet: Boolean
|
||||
@@ -322,92 +340,58 @@ 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)
|
||||
= option.startsWith("-Xplugin=") && option.contains("kotlin-scripting-compiler")
|
||||
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
|
||||
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() }
|
||||
|
||||
val oldIsAccessible = method.isAccessible
|
||||
try {
|
||||
method.isAccessible = true
|
||||
return method.invoke(instance)
|
||||
} finally {
|
||||
method.isAccessible = oldIsAccessible
|
||||
}
|
||||
method.isAccessible = true
|
||||
return method.invoke(instance)
|
||||
}
|
||||
|
||||
private fun ParserContext.parseDependencies(project: Project, forTests: Boolean): List<POrderRoot> {
|
||||
val configurations = project.configurations
|
||||
val configurationMapping = if (forTests) TEST_CONFIGURATION_MAPPING else CONFIGURATION_MAPPING
|
||||
private fun parseDependencies(project: Project, sourceSet: PSourceSet): List<POrderRoot> {
|
||||
val roots = mutableListOf<POrderRoot>()
|
||||
|
||||
val mainRoots = mutableListOf<POrderRoot>()
|
||||
val deferredRoots = mutableListOf<POrderRoot>()
|
||||
|
||||
for ((configurationNames, scope) in configurationMapping) {
|
||||
for (configurationName in configurationNames) {
|
||||
val configuration = configurations.findByName(configurationName) ?: continue
|
||||
val (main, deferred) = project.resolveDependencies(configuration, forTests, dependencyMappers)
|
||||
mainRoots += main.map { POrderRoot(it, scope) }
|
||||
deferredRoots += deferred.map { POrderRoot(it, scope) }
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return removeDuplicates(mainRoots + deferredRoots)
|
||||
}
|
||||
process(sourceSet.compileClasspathConfigurationName, Scope.PROVIDED)
|
||||
process(sourceSet.runtimeClasspathConfigurationName, Scope.RUNTIME)
|
||||
|
||||
fun removeDuplicates(roots: List<POrderRoot>): List<POrderRoot> {
|
||||
val dependenciesByScope = roots.groupBy { it.scope }.mapValues { it.value.mapTo(mutableSetOf()) { it.dependency } }
|
||||
fun dependenciesFor(scope: Scope) = dependenciesByScope[scope] ?: emptySet<PDependency>()
|
||||
|
||||
val result = mutableSetOf<POrderRoot>()
|
||||
for (root in roots.distinct()) {
|
||||
val scope = root.scope
|
||||
val dependency = root.dependency
|
||||
|
||||
if (root in result) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ((scope == Scope.PROVIDED || scope == Scope.RUNTIME) && dependency in dependenciesFor(Scope.COMPILE)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (scope == Scope.PROVIDED && dependency in dependenciesFor(Scope.RUNTIME)) {
|
||||
result += POrderRoot(dependency, Scope.COMPILE)
|
||||
continue
|
||||
}
|
||||
|
||||
if (scope == Scope.RUNTIME && dependency in dependenciesFor(Scope.PROVIDED)) {
|
||||
result += POrderRoot(dependency, Scope.COMPILE)
|
||||
continue
|
||||
}
|
||||
|
||||
result += root
|
||||
if (sourceSet.forTests) {
|
||||
process("jpsTest", Scope.TEST)
|
||||
}
|
||||
|
||||
return result.toList()
|
||||
return roots
|
||||
}
|
||||
|
||||
val Project.pillModuleName: String
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
@@ -50,5 +55,4 @@ class ModuleContext(val project: PProject, val module: PModule) : PathContext {
|
||||
}
|
||||
}
|
||||
|
||||
fun String.withSlash() = if (this.endsWith("/")) this else (this + "/")
|
||||
fun String.withoutSlash() = this.trimEnd('/')
|
||||
fun String.withSlash() = if (this.endsWith("/")) this else ("$this/")
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
@@ -10,71 +15,50 @@ import shadow.org.jdom2.*
|
||||
import shadow.org.jdom2.output.Format
|
||||
import shadow.org.jdom2.output.XMLOutputter
|
||||
import java.io.File
|
||||
|
||||
class PillConfigurablePlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.configurations.maybeCreate(EmbeddedComponents.CONFIGURATION_NAME)
|
||||
project.extensions.create("pill", PillExtension::class.java)
|
||||
}
|
||||
}
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class JpsCompatiblePlugin : Plugin<Project> {
|
||||
companion object {
|
||||
private fun getDependencyMappers(projectLibraries: List<PLibrary>): List<DependencyMapper> {
|
||||
val mappersForKotlinLibrariesExeptStdlib = projectLibraries
|
||||
.filter { it.name != "kotlin-stdlib" }
|
||||
.map { DependencyMapper.forProject(it.originalName) { MappedDependency(PDependency.Library(it.name)) } }
|
||||
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"
|
||||
)
|
||||
|
||||
val disabledModuleMappers = listOf(
|
||||
":kotlin-stdlib-common",
|
||||
":core:builtins",
|
||||
":kotlin-compiler",
|
||||
":kotlin-compiler-embeddable",
|
||||
":kotlin-test:kotlin-test-common",
|
||||
":kotlin-test:kotlin-test-annotations-common"
|
||||
).map { DependencyMapper.forProject(it) { null } }
|
||||
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"
|
||||
)
|
||||
|
||||
return listOf(
|
||||
DependencyMapper.forProject(":kotlin-stdlib") {
|
||||
MappedDependency(
|
||||
PDependency.Library("kotlin-stdlib"),
|
||||
listOf(PDependency.Library("annotations-13.0"))
|
||||
)
|
||||
},
|
||||
DependencyMapper.forProject(":kotlin-test:kotlin-test-jvm") { MappedDependency(PDependency.Library("kotlin-test-jvm")) },
|
||||
DependencyMapper.forProject(":kotlin-reflect-api") { MappedDependency(PDependency.Library("kotlin-reflect")) }
|
||||
) + mappersForKotlinLibrariesExeptStdlib + disabledModuleMappers
|
||||
}
|
||||
private val MAPPED_LIBRARIES = mapOf(
|
||||
":kotlin-reflect-api/java9" to ":kotlin-reflect/main"
|
||||
)
|
||||
|
||||
fun getProjectLibraries(rootProject: Project): List<PLibrary> {
|
||||
val distLibDir = File(rootProject.extra["distLibDir"].toString())
|
||||
fun distJar(name: String) = File(rootProject.projectDir, "dist/kotlinc/lib/$name.jar")
|
||||
|
||||
val libraries = rootProject.allprojects
|
||||
.mapNotNull { library ->
|
||||
val libraryExtension = library.extensions.findByType(PillExtension::class.java)
|
||||
?.takeIf { it.importAsLibrary }
|
||||
?: return@mapNotNull null
|
||||
|
||||
val libraryPath = libraryExtension.libraryPath ?: distLibDir
|
||||
val archivesBaseName = library.convention.findPlugin(BasePluginConvention::class.java)?.archivesBaseName ?: library.name
|
||||
|
||||
fun List<File>.filterExisting() = filter { it.exists() }
|
||||
|
||||
PLibrary(
|
||||
library.name,
|
||||
classes = listOf(File(libraryPath, "$archivesBaseName.jar")).filterExisting(),
|
||||
sources = listOf(File(libraryPath, "$archivesBaseName-sources.jar")).filterExisting(),
|
||||
originalName = library.path
|
||||
)
|
||||
}
|
||||
|
||||
return libraries + PLibrary("annotations-13.0", classes = listOf(distJar("annotations-13.0")))
|
||||
}
|
||||
private val LIB_DIRECTORIES = listOf("dependencies", "dist")
|
||||
}
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.plugins.apply(PillConfigurablePlugin::class.java)
|
||||
project.configurations.maybeCreate(EmbeddedComponents.CONFIGURATION_NAME)
|
||||
project.extensions.create("pill", PillExtension::class.java)
|
||||
|
||||
// 'jpsTest' does not require the 'tests-jar' artifact
|
||||
project.configurations.create("jpsTest")
|
||||
|
||||
@@ -129,13 +113,14 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
return
|
||||
}
|
||||
|
||||
val projectLibraries = getProjectLibraries(rootProject)
|
||||
val dependencyMappers = getDependencyMappers(projectLibraries)
|
||||
val parserContext = ParserContext(variant)
|
||||
|
||||
val parserContext = ParserContext(dependencyMappers, variant)
|
||||
val dependencyPatcher = DependencyPatcher(rootProject)
|
||||
val dependencyMappers = listOf(dependencyPatcher, ::attachPlatformSources, ::attachAsmSources)
|
||||
|
||||
val jpsProject = parse(rootProject, projectLibraries, parserContext)
|
||||
.mapLibraries(this::attachPlatformSources, this::attachAsmSources)
|
||||
val jpsProject = parse(rootProject, parserContext)
|
||||
.mapDependencies(dependencyMappers)
|
||||
.copy(libraries = dependencyPatcher.libraries)
|
||||
|
||||
val files = render(jpsProject)
|
||||
|
||||
@@ -143,7 +128,13 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
removeJpsAndPillRunConfigurations()
|
||||
removeAllArtifactConfigurations()
|
||||
|
||||
generateKotlinPluginArtifactFile(rootProject, dependencyMappers).write()
|
||||
val artifactDependencyMapper = object : OpaqueDependencyMapper {
|
||||
override fun map(dependency: PDependency): List<PDependency> {
|
||||
return jpsProject.mapDependency(dependency, dependencyMappers)
|
||||
}
|
||||
}
|
||||
|
||||
generateKotlinPluginArtifactFile(rootProject, artifactDependencyMapper).write()
|
||||
|
||||
copyRunConfigurations()
|
||||
setOptionsForDefaultJunitRunConfiguration(rootProject)
|
||||
@@ -192,7 +183,7 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
.replace("\$ADDITIONAL_IDEA_ARGS\$", additionalIdeaArgs)
|
||||
}
|
||||
|
||||
runConfigurationsDir.listFiles()
|
||||
(runConfigurationsDir.listFiles() ?: emptyArray())
|
||||
.filter { it.extension == "xml" }
|
||||
.map { it.name to substitute(it.readText()) }
|
||||
.forEach { File(targetDir, it.first).writeText(it.second) }
|
||||
@@ -260,11 +251,11 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
if (options.none { it == "-ea" }) {
|
||||
options += "-ea"
|
||||
options = options + "-ea"
|
||||
}
|
||||
|
||||
addOrReplaceOptionValue("idea.home.path", platformDirProjectRelative)
|
||||
addOrReplaceOptionValue("ideaSdk.androidPlugin.path", platformDirProjectRelative + "/plugins/android/lib")
|
||||
addOrReplaceOptionValue("ideaSdk.androidPlugin.path", "$platformDirProjectRelative/plugins/android/lib")
|
||||
addOrReplaceOptionValue("use.jps", "true")
|
||||
addOrReplaceOptionValue("kotlinVersion", project.rootProject.extra["kotlinVersion"].toString())
|
||||
|
||||
@@ -287,8 +278,9 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
kotlinJunitConfiguration.applyJUnitTemplate()
|
||||
|
||||
val output = XMLOutputter().also {
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
it.format = Format.getPrettyFormat().apply {
|
||||
setEscapeStrategy { Verifier.isHighSurrogate(it) || it == '"' }
|
||||
setEscapeStrategy { c -> Verifier.isHighSurrogate(c) || c == '"' }
|
||||
setIndent(" ")
|
||||
setTextMode(Format.TextMode.TRIM)
|
||||
setOmitEncoding(false)
|
||||
@@ -304,40 +296,138 @@ class JpsCompatiblePlugin : Plugin<Project> {
|
||||
workspaceFile.writeText(postProcessedXml)
|
||||
}
|
||||
|
||||
private fun attachPlatformSources(library: PLibrary): PLibrary {
|
||||
val platformSourcesJar = File(platformDir, "../../../sources/intellij-$platformVersion-sources.jar")
|
||||
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>>()
|
||||
|
||||
if (library.classes.any { it.startsWith(platformDir) || it.startsWith(intellijCoreDir) }) {
|
||||
return library.attachSource(platformSourcesJar)
|
||||
}
|
||||
fun List<File>.filterExisting() = filter { it.exists() }
|
||||
|
||||
return library
|
||||
}
|
||||
|
||||
private fun attachAsmSources(library: PLibrary): PLibrary {
|
||||
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 library.attachSource(asmSourcesJar)
|
||||
}
|
||||
|
||||
return library
|
||||
}
|
||||
|
||||
private fun PProject.mapLibraries(vararg mappers: (PLibrary) -> PLibrary): PProject {
|
||||
fun mapLibrary(root: POrderRoot): POrderRoot {
|
||||
val dependency = root.dependency
|
||||
|
||||
if (dependency is PDependency.ModuleLibrary) {
|
||||
val newLibrary = mappers.fold(dependency.library) { lib, mapper -> mapper(lib) }
|
||||
return root.copy(dependency = dependency.copy(library = newLibrary))
|
||||
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))
|
||||
}
|
||||
|
||||
return root
|
||||
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)
|
||||
}
|
||||
|
||||
val modules = this.modules.map { it.copy(orderRoots = it.orderRoots.map(::mapLibrary)) }
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch", "FunctionName")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import java.io.File
|
||||
@@ -49,12 +54,7 @@ private fun renderModule(project: PProject, module: PModule) = PFile(
|
||||
xml("component", "name" to "TestModuleProperties", "production-module" to moduleForProductionSources.name)
|
||||
}
|
||||
|
||||
val kotlinCompileOptionsList = module.contentRoots.flatMap { it.sourceRoots }.mapNotNull { it.kotlinOptions }
|
||||
var kotlinCompileOptions = kotlinCompileOptionsList.firstOrNull()
|
||||
for (otherOptions in kotlinCompileOptionsList.drop(1)) {
|
||||
kotlinCompileOptions = kotlinCompileOptions?.intersect(otherOptions)
|
||||
}
|
||||
|
||||
val kotlinCompileOptions = module.kotlinOptions
|
||||
val pathContext = ModuleContext(project, module)
|
||||
|
||||
val platformVersion = (kotlinCompileOptions?.jvmTarget ?: "1.8")
|
||||
@@ -100,10 +100,10 @@ private fun renderModule(project: PProject, module: PModule) = PFile(
|
||||
) {
|
||||
xml("exclude-output")
|
||||
|
||||
for (contentRoot in module.contentRoots) {
|
||||
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.path))
|
||||
var args = arrayOf(pathContext.url(sourceRoot.directory))
|
||||
|
||||
args += when (sourceRoot.kind) {
|
||||
PSourceRoot.Kind.PRODUCTION -> ("isTestSource" to "false")
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("PackageDirectoryMismatch", "ClassName")
|
||||
package org.jetbrains.kotlin.pill
|
||||
|
||||
import shadow.org.jdom2.Document
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class JUnit3RunnerWithInnersForJPS extends Runner implements Filterable,
|
||||
String compilerXmlSourcePath = "compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml";
|
||||
|
||||
String jpsTargetDirectory = "out/production/kotlin.idea.main";
|
||||
String pillTargetDirectory = "out/production/idea.src";
|
||||
String pillTargetDirectory = "out/production/idea.main";
|
||||
|
||||
String baseDir = Files.exists(Paths.get(jpsTargetDirectory)) ? jpsTargetDirectory : pillTargetDirectory;
|
||||
String compilerXmlTargetPath = baseDir + "/META-INF/extensions/compiler.xml";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
description = 'Kotlin Test Annotations Common'
|
||||
|
||||
apply plugin: 'kotlin-platform-common'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configurePublishing(project)
|
||||
|
||||
@@ -10,10 +9,6 @@ dependencies {
|
||||
testCompile project(":kotlin-test:kotlin-test-common")
|
||||
}
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile) {
|
||||
kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
description = 'Kotlin Test Common'
|
||||
|
||||
apply plugin: 'kotlin-platform-common'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configurePublishing(project)
|
||||
|
||||
@@ -10,10 +9,6 @@ dependencies {
|
||||
testCompile project(":kotlin-test:kotlin-test-annotations-common")
|
||||
}
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
jar {
|
||||
manifestAttributes(manifest, project, 'Test')
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
description = 'Kotlin Test JUnit'
|
||||
|
||||
apply plugin: 'kotlin-platform-jvm'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
expectedBy project(':kotlin-test:kotlin-test-annotations-common')
|
||||
compile project(':kotlin-test:kotlin-test-jvm')
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
description = 'Kotlin Test'
|
||||
|
||||
apply plugin: 'kotlin-platform-jvm'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
def includeJava9 = BuildPropertiesKt.getKotlinBuildProperties(project).includeJava9
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -19,17 +19,12 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("pill-configurable")
|
||||
}
|
||||
|
||||
callGroovy("configureJavaOnlyJvm6Project", project)
|
||||
|
||||
publish()
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
val core = "$rootDir/core"
|
||||
val relocatedCoreSrc = "$buildDir/core-relocated"
|
||||
val libsDir = property("libsDir")
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
description = 'Kotlin Common Standard Library'
|
||||
|
||||
apply plugin: 'kotlin-platform-common'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configurePublishing(project)
|
||||
|
||||
def commonSrcDir = "../src"
|
||||
def commonTestSrcDir = "../test"
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin {
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
description = 'Kotlin Standard Library JDK 7 extension'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
ext.javaHome = JDK_17
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin {
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
description = 'Kotlin Standard Library JDK 8 extension'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
ext.javaHome = JDK_18
|
||||
ext.jvmTarget = "1.8"
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':kotlin-stdlib')
|
||||
compile project(':kotlin-stdlib-jdk7')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
description = 'Kotlin Standard Library for JVM'
|
||||
|
||||
apply plugin: 'kotlin-platform-jvm'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
archivesBaseName = 'kotlin-stdlib'
|
||||
|
||||
@@ -12,10 +11,6 @@ configurations {
|
||||
distSources
|
||||
}
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
description = 'Kotlin annotations for JVM'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
|
||||
@@ -2,6 +2,7 @@ description = "kotlin-gradle-statistics"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
description 'Kotlin Script Runtime'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'pill-configurable'
|
||||
|
||||
configureJvm6Project(project)
|
||||
configurePublishing(project)
|
||||
|
||||
pill {
|
||||
importAsLibrary = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly kotlinStdlib()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ description = "Kotlin JPS plugin"
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("pill-configurable")
|
||||
}
|
||||
|
||||
val projectsToShadow = listOf(
|
||||
|
||||
Reference in New Issue
Block a user