Pill: Support all Gradle source sets, not just main/test

This commit is contained in:
Yan Zhulanow
2020-01-24 22:19:43 +09:00
parent 3acf7a4679
commit 73813aef23
24 changed files with 446 additions and 551 deletions
-4
View File
@@ -46,10 +46,6 @@ plugins {
gradlePlugin { gradlePlugin {
plugins { plugins {
register("pill-configurable") {
id = "pill-configurable"
implementationClass = "org.jetbrains.kotlin.pill.PillConfigurablePlugin"
}
register("jps-compatible") { register("jps-compatible") {
id = "jps-compatible" id = "jps-compatible"
implementationClass = "org.jetbrains.kotlin.pill.JpsCompatiblePlugin" implementationClass = "org.jetbrains.kotlin.pill.JpsCompatiblePlugin"
-33
View File
@@ -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)
}
+18 -12
View File
@@ -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") @file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill package org.jetbrains.kotlin.pill
@@ -7,33 +12,34 @@ import org.gradle.api.Project
open class PillExtension { open class PillExtension {
enum class Variant { enum class Variant {
// Default variant (./gradlew pill) // Default variant (./gradlew pill)
BASE() { override val includes = setOf(BASE) }, BASE() {
override val includes = setOf(BASE)
},
// Full variant (./gradlew pill -Dpill.variant=full) // 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 // 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 // '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> abstract val includes: Set<Variant>
} }
open var variant: Variant = Variant.DEFAULT open var variant: Variant = Variant.DEFAULT
open var importAsLibrary: Boolean = false
open var excludedDirs: List<File> = emptyList() open var excludedDirs: List<File> = emptyList()
@Suppress("unused")
fun Project.excludedDirs(vararg dirs: String) { fun Project.excludedDirs(vararg dirs: String) {
excludedDirs = excludedDirs + dirs.map { File(projectDir, it) } 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") @file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill package org.jetbrains.kotlin.pill
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration 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.gradle.kotlin.dsl.extra
import org.jetbrains.kotlin.pill.ArtifactElement.* import org.jetbrains.kotlin.pill.ArtifactElement.*
import org.jetbrains.kotlin.pill.POrderRoot.*
import java.io.File 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") { fun render(context: PathContext) = xml("component", "name" to "ArtifactManager") {
xml("artifact", "name" to artifactName) { xml("artifact", "name" to artifactName) {
xml("output-path") { 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 { sealed class ArtifactElement {
private val myChildren = mutableListOf<ArtifactElement>() private val myChildren = mutableListOf<ArtifactElement>()
val children get() = myChildren 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() { class Root : ArtifactElement() {
override fun render(context: PathContext) = xml("root", "id" to "root") 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() val root = Root()
fun Project.getProject(name: String) = findProject(name) ?: error("Cannot find project $name") 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 { root.add(Directory("lib").apply {
val librariesConfiguration = prepareIdeaPluginProject.configurations.getByName("libraries") val librariesConfiguration = prepareIdeaPluginProject.configurations.getByName("libraries")
add(getArtifactElements(rootProject, librariesConfiguration, dependencyMappers, false)) add(getArtifactElements(rootProject, librariesConfiguration, dependencyMapper, false))
add(Directory("jps").apply { add(Directory("jps").apply {
val prepareJpsPluginProject = rootProject.getProject(":kotlin-jps-plugin") val prepareJpsPluginProject = rootProject.getProject(":kotlin-jps-plugin")
add(Archive(prepareJpsPluginProject.name + ".jar").apply { add(Archive(prepareJpsPluginProject.name + ".jar").apply {
val jpsPluginConfiguration = prepareIdeaPluginProject.configurations.getByName("jpsPlugin") 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"))) add(FileCopy(File(rootProject.projectDir, "resources/kotlinManifest.properties")))
val embeddedConfiguration = prepareIdeaPluginProject.configurations.getByName(EmbeddedComponents.CONFIGURATION_NAME) 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( private fun getArtifactElements(
rootProject: Project, rootProject: Project,
configuration: Configuration, configuration: Configuration,
dependencyMappers: List<DependencyMapper>, dependencyMapper: OpaqueDependencyMapper,
extractDependencies: Boolean extractDependencies: Boolean
): List<ArtifactElement> { ): List<ArtifactElement> {
val dependencies = rootProject.resolveDependencies(configuration, false, dependencyMappers, withEmbedded = true).join() val dependencies = parseDependencies(configuration, dependencyMapper)
val artifacts = mutableListOf<ArtifactElement>() val artifacts = mutableListOf<ArtifactElement>()
@@ -180,3 +163,12 @@ private fun getArtifactElements(
return artifacts 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
}
+196 -212
View File
@@ -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. * 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 package org.jetbrains.kotlin.pill
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.kotlin.dsl.configure
import org.gradle.plugins.ide.idea.IdeaPlugin import org.gradle.plugins.ide.idea.IdeaPlugin
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.HasConvention import org.gradle.api.internal.HasConvention
import org.gradle.api.internal.file.copy.CopySpecInternal import org.gradle.api.internal.file.copy.CopySpecInternal
import org.gradle.api.internal.file.copy.SingleParentCopySpec import org.gradle.api.internal.file.copy.SingleParentCopySpec
import org.gradle.jvm.tasks.Jar
import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.pill.POrderRoot.* import org.jetbrains.kotlin.pill.POrderRoot.*
import org.jetbrains.kotlin.pill.PSourceRoot.* import org.jetbrains.kotlin.pill.PSourceRoot.*
import org.jetbrains.kotlin.pill.PillExtension.* import org.jetbrains.kotlin.pill.PillExtension.*
import java.io.File import java.io.File
import java.util.LinkedList
class ParserContext(val variant: Variant)
typealias OutputDir = String
typealias GradleProjectPath = String
data class PProject( data class PProject(
val name: String, val name: String,
val rootDirectory: File, val rootDirectory: File,
val modules: List<PModule>, val modules: List<PModule>,
val libraries: List<PLibrary> val libraries: List<PLibrary>,
val artifacts: Map<OutputDir, List<GradleProjectPath>>
) )
data class PModule( data class PModule(
val name: String, val name: String,
val path: GradleProjectPath,
val forTests: Boolean,
val rootDirectory: File, val rootDirectory: File,
val moduleFile: File, val moduleFile: File,
val contentRoots: List<PContentRoot>, val contentRoots: List<PContentRoot>,
val orderRoots: List<POrderRoot>, val orderRoots: List<POrderRoot>,
val kotlinOptions: PSourceRootKotlinOptions?,
val moduleForProductionSources: PModule? = null val moduleForProductionSources: PModule? = null
) )
data class PContentRoot( data class PContentRoot(
val path: File, val path: File,
val forTests: Boolean,
val sourceRoots: List<PSourceRoot>, val sourceRoots: List<PSourceRoot>,
val excludedDirectories: List<File> val excludedDirectories: List<File>
) )
data class PSourceRoot( data class PSourceSet(
val path: File, val name: String,
val kind: Kind, val forTests: Boolean,
val kotlinOptions: PSourceRootKotlinOptions? 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 } enum class Kind { PRODUCTION, TEST, RESOURCES, TEST_RESOURCES }
} }
@@ -63,17 +75,7 @@ data class PSourceRootKotlinOptions(
val languageVersion: String?, val languageVersion: String?,
val jvmTarget: String?, val jvmTarget: String?,
val extraArguments: List<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( data class POrderRoot(
val dependency: PDependency, 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) { if (project != project.rootProject) {
error("$project is not a root project") 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() } .partition { it.plugins.hasPlugin(JpsCompatiblePlugin::class.java) && it.matchesSelectedVariant() }
val modules = includedProjects.flatMap { parseModules(it, excludedProjects) } 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)
} }
/* private fun parseArtifacts(rootProject: Project): Map<String, List<GradleProjectPath>> {
Ordering here and below is significant. val artifacts = HashMap<OutputDir, List<GradleProjectPath>>()
Placing 'runtime' configuration dependencies on the top make 'idea' tests to run normally. val additionalOutputs = HashMap<OutputDir, List<OutputDir>>()
('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 val TEST_CONFIGURATION_MAPPING = mapOf( for (project in rootProject.allprojects) {
listOf("runtimeClasspath", "testRuntimeClasspath") to Scope.RUNTIME, val sourceSets = project.sourceSets?.toList() ?: emptyList()
listOf("compileClasspath", "testCompileClasspath", "compileOnly", "testCompileOnly") to Scope.PROVIDED,
listOf("jpsTest") to Scope.TEST
)
private fun ParserContext.parseModules(project: Project, excludedProjects: List<Project>): List<PModule> { for (sourceSet in sourceSets) {
val (productionContentRoots, testContentRoots) = parseContentRoots(project).partition { !it.forTests } 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>() val modules = mutableListOf<PModule>()
fun getJavaExcludedDirs() = project.plugins.findPlugin(IdeaPlugin::class.java) fun getModuleFile(name: String): File {
?.model?.module?.excludeDirs?.toList() ?: emptyList() val relativePath = File(project.projectDir, "$name.iml").toRelativeString(project.rootProject.projectDir)
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)
return File(project.rootProject.projectDir, ".idea/modules/$relativePath") return File(project.rootProject.projectDir, ".idea/modules/$relativePath")
} }
for ((nameSuffix, roots) in mapOf(".src" to productionContentRoots, ".test" to testContentRoots)) { val sourceSets = parseSourceSets(project).sortedBy { it.forTests }
if (roots.isEmpty()) { 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 continue
} }
val mainRoot = roots.first() val productionModule = if (sourceSet.forTests) modules.firstOrNull { !it.forTests } else null
var dependencies = parseDependencies(project, mainRoot.forTests) val contentRoots = sourceRoots.map { PContentRoot(it.directory, listOf(it), emptyList()) }
if (productionContentRoots.isNotEmpty() && mainRoot.forTests) {
val productionModuleDependency = PDependency.Module(project.pillModuleName + ".src") var orderRoots = parseDependencies(project, sourceSet)
dependencies += POrderRoot(productionModuleDependency, Scope.COMPILE, true) if (productionModule != null) {
val productionModuleDependency = PDependency.Module(productionModule.name)
orderRoots = listOf(POrderRoot(productionModuleDependency, Scope.COMPILE, true)) + orderRoots
} }
val module = PModule( val name = project.pillModuleName + "." + sourceSet.name
project.pillModuleName + nameSuffix,
mainRoot.path, modules += PModule(
getModuleFile(nameSuffix), name = name,
roots, path = makePath(project, sourceSet.name),
dependencies, forTests = sourceSet.forTests,
productionSourcesModule 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) { val mainModuleFileRelativePath = when (project) {
project.rootProject -> File(project.rootProject.projectDir, project.rootProject.name + ".iml") project.rootProject -> File(project.rootProject.projectDir, project.rootProject.name + ".iml")
else -> getModuleFile() else -> getModuleFile(project.pillModuleName)
} }
modules += PModule( modules += PModule(
project.pillModuleName, name = project.pillModuleName,
project.projectDir, path = project.path,
mainModuleFileRelativePath, forTests = false,
listOf(PContentRoot(project.projectDir, false, emptyList(), allExcludedDirs)), rootDirectory = project.projectDir,
if (modules.isEmpty()) parseDependencies(project, false) else emptyList() moduleFile = mainModuleFileRelativePath,
contentRoots = listOf(PContentRoot(project.projectDir, listOf(), getExcludedDirs(project, excludedProjects))),
orderRoots = emptyList(),
kotlinOptions = null,
moduleForProductionSources = null
) )
return modules return modules
} }
private fun parseContentRoots(project: Project): List<PContentRoot> { private fun getExcludedDirs(project: Project, excludedProjects: List<Project>): List<File> {
val sourceRoots = parseSourceRoots(project).groupBy { it.kind } fun getJavaExcludedDirs() = project.plugins.findPlugin(IdeaPlugin::class.java)
fun getRoots(kind: PSourceRoot.Kind) = sourceRoots[kind] ?: emptyList() ?.model?.module?.excludeDirs?.toList() ?: emptyList()
val productionSourceRoots = getRoots(Kind.PRODUCTION) + getRoots(Kind.RESOURCES) fun getPillExcludedDirs() = project.extensions.getByType(PillExtension::class.java).excludedDirs
val testSourceRoots = getRoots(Kind.TEST) + getRoots(Kind.TEST_RESOURCES)
fun createContentRoots(sourceRoots: List<PSourceRoot>, forTests: Boolean): List<PContentRoot> { return getPillExcludedDirs() + getJavaExcludedDirs() + project.buildDir +
return sourceRoots.map { PContentRoot(it.path, forTests, listOf(it), emptyList()) } (if (project == project.rootProject) excludedProjects.map { it.buildDir } else emptyList())
}
return createContentRoots(productionSourceRoots, forTests = false) +
createContentRoots(testSourceRoots, forTests = true)
} }
private fun parseSourceRoots(project: Project): List<PSourceRoot> { private fun parseSourceSets(project: Project): List<PSourceSet> {
if (!project.plugins.hasPlugin(JavaPlugin::class.java)) { if (!project.plugins.hasPlugin(JavaPlugin::class.java)) {
return emptyList() return emptyList()
} }
val kotlinTasksBySourceSet = project.tasks.names val kotlinTasksBySourceSet = project.tasks.names
.filter { it.startsWith("compile") && it.endsWith("Kotlin") } .filter { it.startsWith("compile") && it.endsWith("Kotlin") }
.map { project.tasks.getByName(it) } .map { project.tasks.getByName(it) }
.associateBy { it.invokeInternal("getSourceSetName") } .associateBy { it.invokeInternal("getSourceSetName") }
val sourceRoots = mutableListOf<PSourceRoot>() val gradleSourceSets = project.sourceSets?.toList() ?: emptyList()
val sourceSets = mutableListOf<PSourceSet>()
val sourceSets = project.sourceSets for (sourceSet in gradleSourceSets) {
if (sourceSets != null) { val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name]
for (sourceSet in sourceSets) {
val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name]
val kind = if (sourceSet.isTestSourceSet) Kind.TEST else Kind.PRODUCTION
fun Any.getKotlin(): SourceDirectorySet { fun Any.getKotlin(): SourceDirectorySet {
val kotlinMethod = javaClass.getMethod("getKotlin") val kotlinMethod = javaClass.getMethod("getKotlin")
val oldIsAccessible = kotlinMethod.isAccessible kotlinMethod.isAccessible = true
try { return kotlinMethod(this) as SourceDirectorySet
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
}
}
} }
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 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 taskNameBase = "processResources"
val taskName = if (isMainSourceSet) taskNameBase else sourceSet.name + taskNameBase.capitalize() val taskName = if (isMainSourceSet) taskNameBase else sourceSet.name + taskNameBase.capitalize()
val task = project.tasks.findByName(taskName) as? ProcessResources ?: return emptyList() val task = project.tasks.findByName(taskName) as? ProcessResources ?: return emptyList()
@@ -309,8 +328,7 @@ private fun parseResourceRootsProcessedByProcessResourcesTask(project: Project,
spec.children.forEach(::collectRoots) spec.children.forEach(::collectRoots)
} }
collectRoots(task.rootSpec) collectRoots(task.rootSpec)
return roots
return roots.map { PSourceRoot(it, resourceRootKind, null) }
} }
private val SourceSet.isTestSourceSet: Boolean private val SourceSet.isTestSourceSet: Boolean
@@ -322,92 +340,58 @@ private fun getKotlinOptions(kotlinCompileTask: Any): PSourceRootKotlinOptions?
val compileArguments = run { val compileArguments = run {
val method = kotlinCompileTask::class.java.getMethod("getSerializedCompilerArguments") val method = kotlinCompileTask::class.java.getMethod("getSerializedCompilerArguments")
method.isAccessible = true method.isAccessible = true
@Suppress("UNCHECKED_CAST")
method.invoke(kotlinCompileTask) as List<String> method.invoke(kotlinCompileTask) as List<String>
} }
fun parseBoolean(name: String) = compileArguments.contains("-$name") fun parseBoolean(name: String) = compileArguments.contains("-$name")
fun parseString(name: String) = compileArguments.dropWhile { it != "-$name" }.drop(1).firstOrNull() fun parseString(name: String) = compileArguments.dropWhile { it != "-$name" }.drop(1).firstOrNull()
fun isOptionForScriptingCompilerPlugin(option: String) fun isOptionForScriptingCompilerPlugin(option: String): Boolean {
= option.startsWith("-Xplugin=") && option.contains("kotlin-scripting-compiler") return option.startsWith("-Xplugin=") && option.contains("kotlin-scripting-compiler")
}
val extraArguments = compileArguments.filter { val extraArguments = compileArguments.filter {
it.startsWith("-X") && !isOptionForScriptingCompilerPlugin(it) it.startsWith("-X") && !isOptionForScriptingCompilerPlugin(it)
} }
return PSourceRootKotlinOptions( return PSourceRootKotlinOptions(
parseBoolean("no-stdlib"), parseBoolean("no-stdlib"),
parseBoolean("no-reflect"), parseBoolean("no-reflect"),
parseString("module-name"), parseString("module-name"),
parseString("api-version"), parseString("api-version"),
parseString("language-version"), parseString("language-version"),
parseString("jvm-target"), parseString("jvm-target"),
extraArguments extraArguments
) )
} }
private fun Any.invokeInternal(name: String, instance: Any = this): Any? { private fun Any.invokeInternal(name: String, instance: Any = this): Any? {
val method = javaClass.methods.single { it.name.startsWith(name) && it.parameterTypes.isEmpty() } val method = javaClass.methods.single { it.name.startsWith(name) && it.parameterTypes.isEmpty() }
method.isAccessible = true
val oldIsAccessible = method.isAccessible return method.invoke(instance)
try {
method.isAccessible = true
return method.invoke(instance)
} finally {
method.isAccessible = oldIsAccessible
}
} }
private fun ParserContext.parseDependencies(project: Project, forTests: Boolean): List<POrderRoot> { private fun parseDependencies(project: Project, sourceSet: PSourceSet): List<POrderRoot> {
val configurations = project.configurations val roots = mutableListOf<POrderRoot>()
val configurationMapping = if (forTests) TEST_CONFIGURATION_MAPPING else CONFIGURATION_MAPPING
val mainRoots = mutableListOf<POrderRoot>() fun process(name: String, scope: Scope) {
val deferredRoots = mutableListOf<POrderRoot>() val configuration = project.configurations.findByName(name) ?: return
for (file in configuration.resolve()) {
for ((configurationNames, scope) in configurationMapping) { val library = PLibrary(file.name, listOf(file))
for (configurationName in configurationNames) { val dependency = PDependency.ModuleLibrary(library)
val configuration = configurations.findByName(configurationName) ?: continue roots += POrderRoot(dependency, scope)
val (main, deferred) = project.resolveDependencies(configuration, forTests, dependencyMappers)
mainRoots += main.map { POrderRoot(it, scope) }
deferredRoots += deferred.map { POrderRoot(it, scope) }
} }
} }
return removeDuplicates(mainRoots + deferredRoots) process(sourceSet.compileClasspathConfigurationName, Scope.PROVIDED)
} process(sourceSet.runtimeClasspathConfigurationName, Scope.RUNTIME)
fun removeDuplicates(roots: List<POrderRoot>): List<POrderRoot> { if (sourceSet.forTests) {
val dependenciesByScope = roots.groupBy { it.scope }.mapValues { it.value.mapTo(mutableSetOf()) { it.dependency } } process("jpsTest", Scope.TEST)
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
} }
return result.toList() return roots
} }
val Project.pillModuleName: String val Project.pillModuleName: String
+6 -2
View File
@@ -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") @file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill 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.withSlash() = if (this.endsWith("/")) this else ("$this/")
fun String.withoutSlash() = this.trimEnd('/')
+184 -94
View File
@@ -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") @file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill package org.jetbrains.kotlin.pill
@@ -10,71 +15,50 @@ import shadow.org.jdom2.*
import shadow.org.jdom2.output.Format import shadow.org.jdom2.output.Format
import shadow.org.jdom2.output.XMLOutputter import shadow.org.jdom2.output.XMLOutputter
import java.io.File import java.io.File
import java.util.*
class PillConfigurablePlugin : Plugin<Project> { import kotlin.collections.HashMap
override fun apply(project: Project) {
project.configurations.maybeCreate(EmbeddedComponents.CONFIGURATION_NAME)
project.extensions.create("pill", PillExtension::class.java)
}
}
class JpsCompatiblePlugin : Plugin<Project> { class JpsCompatiblePlugin : Plugin<Project> {
companion object { companion object {
private fun getDependencyMappers(projectLibraries: List<PLibrary>): List<DependencyMapper> { private val DIST_LIBRARIES = listOf(
val mappersForKotlinLibrariesExeptStdlib = projectLibraries ":kotlin-annotations-jvm",
.filter { it.name != "kotlin-stdlib" } ":kotlin-stdlib",
.map { DependencyMapper.forProject(it.originalName) { MappedDependency(PDependency.Library(it.name)) } } ":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( private val IGNORED_LIBRARIES = listOf(
":kotlin-stdlib-common", // Libraries
":core:builtins", ":kotlin-stdlib-common",
":kotlin-compiler", ":kotlin-reflect-api",
":kotlin-compiler-embeddable", ":kotlin-test:kotlin-test-common",
":kotlin-test:kotlin-test-common", ":kotlin-test:kotlin-test-annotations-common",
":kotlin-test:kotlin-test-annotations-common" // Other
).map { DependencyMapper.forProject(it) { null } } ":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( private val MAPPED_LIBRARIES = mapOf(
DependencyMapper.forProject(":kotlin-stdlib") { ":kotlin-reflect-api/java9" to ":kotlin-reflect/main"
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
}
fun getProjectLibraries(rootProject: Project): List<PLibrary> { private val LIB_DIRECTORIES = listOf("dependencies", "dist")
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")))
}
} }
override fun apply(project: Project) { 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 // 'jpsTest' does not require the 'tests-jar' artifact
project.configurations.create("jpsTest") project.configurations.create("jpsTest")
@@ -129,13 +113,14 @@ class JpsCompatiblePlugin : Plugin<Project> {
return return
} }
val projectLibraries = getProjectLibraries(rootProject) val parserContext = ParserContext(variant)
val dependencyMappers = getDependencyMappers(projectLibraries)
val parserContext = ParserContext(dependencyMappers, variant) val dependencyPatcher = DependencyPatcher(rootProject)
val dependencyMappers = listOf(dependencyPatcher, ::attachPlatformSources, ::attachAsmSources)
val jpsProject = parse(rootProject, projectLibraries, parserContext) val jpsProject = parse(rootProject, parserContext)
.mapLibraries(this::attachPlatformSources, this::attachAsmSources) .mapDependencies(dependencyMappers)
.copy(libraries = dependencyPatcher.libraries)
val files = render(jpsProject) val files = render(jpsProject)
@@ -143,7 +128,13 @@ class JpsCompatiblePlugin : Plugin<Project> {
removeJpsAndPillRunConfigurations() removeJpsAndPillRunConfigurations()
removeAllArtifactConfigurations() 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() copyRunConfigurations()
setOptionsForDefaultJunitRunConfiguration(rootProject) setOptionsForDefaultJunitRunConfiguration(rootProject)
@@ -192,7 +183,7 @@ class JpsCompatiblePlugin : Plugin<Project> {
.replace("\$ADDITIONAL_IDEA_ARGS\$", additionalIdeaArgs) .replace("\$ADDITIONAL_IDEA_ARGS\$", additionalIdeaArgs)
} }
runConfigurationsDir.listFiles() (runConfigurationsDir.listFiles() ?: emptyArray())
.filter { it.extension == "xml" } .filter { it.extension == "xml" }
.map { it.name to substitute(it.readText()) } .map { it.name to substitute(it.readText()) }
.forEach { File(targetDir, it.first).writeText(it.second) } .forEach { File(targetDir, it.first).writeText(it.second) }
@@ -260,11 +251,11 @@ class JpsCompatiblePlugin : Plugin<Project> {
} }
if (options.none { it == "-ea" }) { if (options.none { it == "-ea" }) {
options += "-ea" options = options + "-ea"
} }
addOrReplaceOptionValue("idea.home.path", platformDirProjectRelative) 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("use.jps", "true")
addOrReplaceOptionValue("kotlinVersion", project.rootProject.extra["kotlinVersion"].toString()) addOrReplaceOptionValue("kotlinVersion", project.rootProject.extra["kotlinVersion"].toString())
@@ -287,8 +278,9 @@ class JpsCompatiblePlugin : Plugin<Project> {
kotlinJunitConfiguration.applyJUnitTemplate() kotlinJunitConfiguration.applyJUnitTemplate()
val output = XMLOutputter().also { val output = XMLOutputter().also {
@Suppress("UsePropertyAccessSyntax")
it.format = Format.getPrettyFormat().apply { it.format = Format.getPrettyFormat().apply {
setEscapeStrategy { Verifier.isHighSurrogate(it) || it == '"' } setEscapeStrategy { c -> Verifier.isHighSurrogate(c) || c == '"' }
setIndent(" ") setIndent(" ")
setTextMode(Format.TextMode.TRIM) setTextMode(Format.TextMode.TRIM)
setOmitEncoding(false) setOmitEncoding(false)
@@ -304,40 +296,138 @@ class JpsCompatiblePlugin : Plugin<Project> {
workspaceFile.writeText(postProcessedXml) workspaceFile.writeText(postProcessedXml)
} }
private fun attachPlatformSources(library: PLibrary): PLibrary { private class DependencyPatcher(private val rootProject: Project): Function2<PProject, PDependency, List<PDependency>> {
val platformSourcesJar = File(platformDir, "../../../sources/intellij-$platformVersion-sources.jar") 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) }) { fun List<File>.filterExisting() = filter { it.exists() }
return library.attachSource(platformSourcesJar)
}
return library for (path in DIST_LIBRARIES) {
} val project = rootProject.findProject(path) ?: error("Project not found")
val archiveName = project.convention.findPlugin(BasePluginConvention::class.java)!!.archivesBaseName
private fun attachAsmSources(library: PLibrary): PLibrary { val classesJars = listOf(File(distLibDir, "$archiveName.jar")).filterExisting()
val asmSourcesJar = File(platformDir, "../asm-shaded-sources/asm-src-$platformBaseNumber.jar") val sourcesJars = listOf(File(distLibDir, "$archiveName-sources.jar")).filterExisting()
val asmAllJar = File(platformDir, "lib/asm-all.jar") result["$path/main"] = Optional.of(PLibrary(archiveName, classesJars, sourcesJars, originalName = path))
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))
} }
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) 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
}
} }
+9 -9
View File
@@ -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 package org.jetbrains.kotlin.pill
import java.io.File 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) xml("component", "name" to "TestModuleProperties", "production-module" to moduleForProductionSources.name)
} }
val kotlinCompileOptionsList = module.contentRoots.flatMap { it.sourceRoots }.mapNotNull { it.kotlinOptions } val kotlinCompileOptions = module.kotlinOptions
var kotlinCompileOptions = kotlinCompileOptionsList.firstOrNull()
for (otherOptions in kotlinCompileOptionsList.drop(1)) {
kotlinCompileOptions = kotlinCompileOptions?.intersect(otherOptions)
}
val pathContext = ModuleContext(project, module) val pathContext = ModuleContext(project, module)
val platformVersion = (kotlinCompileOptions?.jvmTarget ?: "1.8") val platformVersion = (kotlinCompileOptions?.jvmTarget ?: "1.8")
@@ -100,10 +100,10 @@ private fun renderModule(project: PProject, module: PModule) = PFile(
) { ) {
xml("exclude-output") xml("exclude-output")
for (contentRoot in module.contentRoots) { for (contentRoot in module.contentRoots.filter { it.path.exists() }) {
xml("content", pathContext.url(contentRoot.path)) { xml("content", pathContext.url(contentRoot.path)) {
for (sourceRoot in contentRoot.sourceRoots) { for (sourceRoot in contentRoot.sourceRoots) {
var args = arrayOf(pathContext.url(sourceRoot.path)) var args = arrayOf(pathContext.url(sourceRoot.directory))
args += when (sourceRoot.kind) { args += when (sourceRoot.kind) {
PSourceRoot.Kind.PRODUCTION -> ("isTestSource" to "false") PSourceRoot.Kind.PRODUCTION -> ("isTestSource" to "false")
+6 -1
View File
@@ -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 package org.jetbrains.kotlin.pill
import shadow.org.jdom2.Document import shadow.org.jdom2.Document
@@ -90,7 +90,7 @@ public class JUnit3RunnerWithInnersForJPS extends Runner implements Filterable,
String compilerXmlSourcePath = "compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml"; String compilerXmlSourcePath = "compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml";
String jpsTargetDirectory = "out/production/kotlin.idea.main"; 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 baseDir = Files.exists(Paths.get(jpsTargetDirectory)) ? jpsTargetDirectory : pillTargetDirectory;
String compilerXmlTargetPath = baseDir + "/META-INF/extensions/compiler.xml"; String compilerXmlTargetPath = baseDir + "/META-INF/extensions/compiler.xml";
@@ -1,7 +1,6 @@
description = 'Kotlin Test Annotations Common' description = 'Kotlin Test Annotations Common'
apply plugin: 'kotlin-platform-common' apply plugin: 'kotlin-platform-common'
apply plugin: 'pill-configurable'
configurePublishing(project) configurePublishing(project)
@@ -10,10 +9,6 @@ dependencies {
testCompile project(":kotlin-test:kotlin-test-common") testCompile project(":kotlin-test:kotlin-test-common")
} }
pill {
importAsLibrary = true
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile) { tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile) {
kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package" kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package"
} }
@@ -1,7 +1,6 @@
description = 'Kotlin Test Common' description = 'Kotlin Test Common'
apply plugin: 'kotlin-platform-common' apply plugin: 'kotlin-platform-common'
apply plugin: 'pill-configurable'
configurePublishing(project) configurePublishing(project)
@@ -10,10 +9,6 @@ dependencies {
testCompile project(":kotlin-test:kotlin-test-annotations-common") testCompile project(":kotlin-test:kotlin-test-annotations-common")
} }
pill {
importAsLibrary = true
}
jar { jar {
manifestAttributes(manifest, project, 'Test') manifestAttributes(manifest, project, 'Test')
} }
-5
View File
@@ -1,15 +1,10 @@
description = 'Kotlin Test JUnit' description = 'Kotlin Test JUnit'
apply plugin: 'kotlin-platform-jvm' apply plugin: 'kotlin-platform-jvm'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
pill {
importAsLibrary = true
}
dependencies { dependencies {
expectedBy project(':kotlin-test:kotlin-test-annotations-common') expectedBy project(':kotlin-test:kotlin-test-annotations-common')
compile project(':kotlin-test:kotlin-test-jvm') compile project(':kotlin-test:kotlin-test-jvm')
-5
View File
@@ -1,15 +1,10 @@
description = 'Kotlin Test' description = 'Kotlin Test'
apply plugin: 'kotlin-platform-jvm' apply plugin: 'kotlin-platform-jvm'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
pill {
importAsLibrary = true
}
def includeJava9 = BuildPropertiesKt.getKotlinBuildProperties(project).includeJava9 def includeJava9 = BuildPropertiesKt.getKotlinBuildProperties(project).includeJava9
sourceSets { sourceSets {
-5
View File
@@ -19,17 +19,12 @@ buildscript {
plugins { plugins {
java java
id("pill-configurable")
} }
callGroovy("configureJavaOnlyJvm6Project", project) callGroovy("configureJavaOnlyJvm6Project", project)
publish() publish()
pill {
importAsLibrary = true
}
val core = "$rootDir/core" val core = "$rootDir/core"
val relocatedCoreSrc = "$buildDir/core-relocated" val relocatedCoreSrc = "$buildDir/core-relocated"
val libsDir = property("libsDir") val libsDir = property("libsDir")
-5
View File
@@ -1,17 +1,12 @@
description = 'Kotlin Common Standard Library' description = 'Kotlin Common Standard Library'
apply plugin: 'kotlin-platform-common' apply plugin: 'kotlin-platform-common'
apply plugin: 'pill-configurable'
configurePublishing(project) configurePublishing(project)
def commonSrcDir = "../src" def commonSrcDir = "../src"
def commonTestSrcDir = "../test" def commonTestSrcDir = "../test"
pill {
importAsLibrary = true
}
sourceSets { sourceSets {
main { main {
kotlin { kotlin {
-5
View File
@@ -1,16 +1,11 @@
description = 'Kotlin Standard Library JDK 7 extension' description = 'Kotlin Standard Library JDK 7 extension'
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
ext.javaHome = JDK_17 ext.javaHome = JDK_17
pill {
importAsLibrary = true
}
sourceSets { sourceSets {
main { main {
kotlin { kotlin {
-5
View File
@@ -1,17 +1,12 @@
description = 'Kotlin Standard Library JDK 8 extension' description = 'Kotlin Standard Library JDK 8 extension'
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
ext.javaHome = JDK_18 ext.javaHome = JDK_18
ext.jvmTarget = "1.8" ext.jvmTarget = "1.8"
pill {
importAsLibrary = true
}
dependencies { dependencies {
compile project(':kotlin-stdlib') compile project(':kotlin-stdlib')
compile project(':kotlin-stdlib-jdk7') compile project(':kotlin-stdlib-jdk7')
-5
View File
@@ -1,7 +1,6 @@
description = 'Kotlin Standard Library for JVM' description = 'Kotlin Standard Library for JVM'
apply plugin: 'kotlin-platform-jvm' apply plugin: 'kotlin-platform-jvm'
apply plugin: 'pill-configurable'
archivesBaseName = 'kotlin-stdlib' archivesBaseName = 'kotlin-stdlib'
@@ -12,10 +11,6 @@ configurations {
distSources distSources
} }
pill {
importAsLibrary = true
}
sourceSets { sourceSets {
main { main {
java { java {
@@ -1,15 +1,10 @@
description = 'Kotlin annotations for JVM' description = 'Kotlin annotations for JVM'
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
pill {
importAsLibrary = true
}
sourceSets { sourceSets {
main { main {
java { java {
@@ -2,6 +2,7 @@ description = "kotlin-gradle-statistics"
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("jps-compatible")
} }
dependencies { dependencies {
@@ -1,15 +1,10 @@
description 'Kotlin Script Runtime' description 'Kotlin Script Runtime'
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'pill-configurable'
configureJvm6Project(project) configureJvm6Project(project)
configurePublishing(project) configurePublishing(project)
pill {
importAsLibrary = true
}
dependencies { dependencies {
compileOnly kotlinStdlib() compileOnly kotlinStdlib()
} }
-1
View File
@@ -2,7 +2,6 @@ description = "Kotlin JPS plugin"
plugins { plugins {
java java
id("pill-configurable")
} }
val projectsToShadow = listOf( val projectsToShadow = listOf(