diff --git a/buildSrc/src/main/kotlin/pill/context.kt b/buildSrc/src/main/kotlin/pill/context.kt index 756ab67fb2a..3c550a1f6f6 100644 --- a/buildSrc/src/main/kotlin/pill/context.kt +++ b/buildSrc/src/main/kotlin/pill/context.kt @@ -2,27 +2,30 @@ package org.jetbrains.kotlin.pill import org.gradle.api.artifacts.* +import org.gradle.api.artifacts.component.* -class DependencyMapper( - val predicate: (ResolvedDependency) -> Boolean, - vararg val configurations: String, - val mapping: (ResolvedDependency) -> MappedDependency? -) { - constructor( - group: String, - module: String, - vararg configurations: String, - version: String? = null, - mapping: (ResolvedDependency) -> MappedDependency? - ) : this( - { dep -> - dep.moduleGroup == group - && dep.moduleName == module - && (version == null || dep.moduleVersion == version) - }, - configurations = *configurations, - mapping = mapping - ) +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 = emptyList()) diff --git a/buildSrc/src/main/kotlin/pill/dependencies.kt b/buildSrc/src/main/kotlin/pill/dependencies.kt new file mode 100644 index 00000000000..868b0d1380d --- /dev/null +++ b/buildSrc/src/main/kotlin/pill/dependencies.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +@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, val deferred: List) { + fun join(): List { + return main + deferred + } +} + +fun Project.resolveDependencies( + configuration: Configuration, + forTests: Boolean, + dependencyMappers: List, + withEmbedded: Boolean = false +): PDependencies { + val dependencies = mutableListOf() + val deferred = mutableListOf() + + 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) { + 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) + } + } + } + + for (dependency in configuration.dependencies) { + if (dependency !is SelfResolvingDependency) { + continue + } + + val files = dependency.resolve().takeIf { it.isNotEmpty() } ?: continue + val library = PLibrary(dependency.name, classes = files.toList()) + deferred += PDependency.ModuleLibrary(library) + } + + return PDependencies(dependencies, deferred) +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/pill/kotlinPluginArtifact.kt b/buildSrc/src/main/kotlin/pill/kotlinPluginArtifact.kt index 5aa5036570d..42b90f91d6c 100644 --- a/buildSrc/src/main/kotlin/pill/kotlinPluginArtifact.kt +++ b/buildSrc/src/main/kotlin/pill/kotlinPluginArtifact.kt @@ -11,7 +11,6 @@ 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.DependencyInfo.ResolvedDependencyInfo import org.jetbrains.kotlin.pill.ArtifactElement.* import org.jetbrains.kotlin.pill.POrderRoot.* import java.io.File @@ -114,7 +113,6 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li fun Project.getProject(name: String) = findProject(name) ?: error("Cannot find project $name") val prepareIdeaPluginProject = rootProject.getProject(":prepare:idea-plugin") - val prepareJpsPluginProject = rootProject.getProject(":kotlin-jps-plugin") root.add(Directory("kotlinc").apply { val kotlincDirectory = rootProject.extra["distKotlinHomeDir"].toString() @@ -123,20 +121,21 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li root.add(Directory("lib").apply { val librariesConfiguration = prepareIdeaPluginProject.configurations.getByName("libraries") - add(getArtifactElements(librariesConfiguration, dependencyMappers, false)) + add(getArtifactElements(rootProject, librariesConfiguration, dependencyMappers, false)) add(Directory("jps").apply { - add(Archive(prepareJpsPluginProject.name).apply { - val embeddedConfiguration = prepareJpsPluginProject.configurations.getByName(EmbeddedComponents.CONFIGURATION_NAME) - add(getArtifactElements(embeddedConfiguration, dependencyMappers, true)) + 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(Archive(prepareIdeaPluginProject.name + ".jar").apply { + add(Archive("kotlin-plugin.jar").apply { add(FileCopy(File(rootProject.projectDir, "resources/kotlinManifest.properties"))) val embeddedConfiguration = prepareIdeaPluginProject.configurations.getByName(EmbeddedComponents.CONFIGURATION_NAME) - add(getArtifactElements(embeddedConfiguration, dependencyMappers, true)) + add(getArtifactElements(rootProject, embeddedConfiguration, dependencyMappers, true)) }) }) @@ -148,42 +147,28 @@ fun generateKotlinPluginArtifactFile(rootProject: Project, dependencyMappers: Li } private fun getArtifactElements( + rootProject: Project, configuration: Configuration, dependencyMappers: List, extractDependencies: Boolean ): List { - val resolved = configuration.resolvedConfiguration - val collected = CollectedConfiguration(resolved, Scope.COMPILE) - val dependencies = listOf(collected).collectDependencies() - - val mainRoots = mutableListOf() - val deferredRoots = mutableListOf() - - dependencies.forEach { it.processResolvedDependency(mainRoots, deferredRoots, dependencyMappers) } + val dependencies = rootProject.resolveDependencies(configuration, false, dependencyMappers, withEmbedded = true).join() val artifacts = mutableListOf() - for (dependency in configuration.allDependencies) { - if (dependency !is ProjectDependency) continue + for (dependency in dependencies) { + when (dependency) { + is PDependency.Module -> { + val moduleOutput = ModuleOutput(dependency.name) - val project = dependency.dependencyProject - val moduleOutput = ModuleOutput(project.name + ".src") - - if (extractDependencies) { - artifacts += moduleOutput - } else { - artifacts += Archive(project.name + ".jar").apply { - add(moduleOutput) + if (extractDependencies) { + artifacts += moduleOutput + } else { + artifacts += Archive(dependency.name + ".jar").apply { + add(moduleOutput) + } + } } - } - - val embeddedConfiguration = project.configurations.findByName(EmbeddedComponents.CONFIGURATION_NAME) ?: continue - artifacts += getArtifactElements(embeddedConfiguration, dependencyMappers, extractDependencies) - } - - for (orderRoot in listOf(mainRoots, deferredRoots).flatten()) { - when (val dependency = orderRoot.dependency) { - is PDependency.Module -> {} // already added above is PDependency.Library -> artifacts += ProjectLibrary(dependency.name) is PDependency.ModuleLibrary -> { val files = dependency.library.classes diff --git a/buildSrc/src/main/kotlin/pill/parser.kt b/buildSrc/src/main/kotlin/pill/parser.kt index c125338cbed..ef29b8904d8 100644 --- a/buildSrc/src/main/kotlin/pill/parser.kt +++ b/buildSrc/src/main/kotlin/pill/parser.kt @@ -33,13 +33,11 @@ data class PProject( data class PModule( val name: String, - val bundleName: String, val rootDirectory: File, val moduleFile: File, val contentRoots: List, val orderRoots: List, - val moduleForProductionSources: PModule? = null, - val group: String? = null + val moduleForProductionSources: PModule? = null ) data class PContentRoot( @@ -98,7 +96,8 @@ data class PLibrary( val javadoc: List = emptyList(), val sources: List = emptyList(), val annotations: List = emptyList(), - val dependencies: List = emptyList() + val dependencies: List = emptyList(), + val originalName: String = name ) { fun attachSource(file: File): PLibrary { return this.copy(sources = this.sources + listOf(file)) @@ -158,7 +157,7 @@ private fun ParserContext.parseModules(project: Project, excludedProjects: List< var productionSourcesModule: PModule? = null fun getModuleFile(suffix: String = ""): File { - val relativePath = File(project.projectDir, project.name + suffix + ".iml") + val relativePath = File(project.projectDir, project.pillModuleName + suffix + ".iml") .toRelativeString(project.rootProject.projectDir) return File(project.rootProject.projectDir, ".idea/modules/$relativePath") @@ -173,13 +172,12 @@ private fun ParserContext.parseModules(project: Project, excludedProjects: List< var dependencies = parseDependencies(project, mainRoot.forTests) if (productionContentRoots.isNotEmpty() && mainRoot.forTests) { - val productionModuleDependency = PDependency.Module(project.name + ".src") + val productionModuleDependency = PDependency.Module(project.pillModuleName + ".src") dependencies += POrderRoot(productionModuleDependency, Scope.COMPILE, true) } val module = PModule( - project.name + nameSuffix, - project.name, + project.pillModuleName + nameSuffix, mainRoot.path, getModuleFile(nameSuffix), roots, @@ -195,13 +193,12 @@ private fun ParserContext.parseModules(project: Project, excludedProjects: List< } val mainModuleFileRelativePath = when (project) { - project.rootProject -> File(project.rootProject.projectDir, project.name + ".iml") + project.rootProject -> File(project.rootProject.projectDir, project.rootProject.name + ".iml") else -> getModuleFile() } modules += PModule( - project.name, - project.name, + project.pillModuleName, project.projectDir, mainModuleFileRelativePath, listOf(PContentRoot(project.projectDir, false, emptyList(), allExcludedDirs)), @@ -238,7 +235,7 @@ private fun parseSourceRoots(project: Project): List { val sourceRoots = mutableListOf() - for (sourceSet in project.sourceSets) { + for (sourceSet in (project.sourceSets ?: emptyList())) { val kotlinCompileTask = kotlinTasksBySourceSet[sourceSet.name] val kind = if (sourceSet.isTestSourceSet) Kind.TEST else Kind.PRODUCTION @@ -355,101 +352,25 @@ private fun Any.invokeInternal(name: String, instance: Any = this): Any? { } private fun ParserContext.parseDependencies(project: Project, forTests: Boolean): List { + val configurations = project.configurations val configurationMapping = if (forTests) TEST_CONFIGURATION_MAPPING else CONFIGURATION_MAPPING - with(project.configurations) { - val mainRoots = mutableListOf() - val deferredRoots = mutableListOf() + val mainRoots = mutableListOf() + val deferredRoots = mutableListOf() - fun collectConfigurations(): List { - val configurations = mutableListOf() - - for ((configurationNames, scope) in configurationMapping) { - for (configurationName in configurationNames) { - val configuration = findByName(configurationName)?.also { it.resolve() } ?: continue - - val extraDependencies = resolveExtraDependencies(configuration) - configurations += CollectedConfiguration(configuration.resolvedConfiguration, scope, extraDependencies) - } - } - - return configurations - } - - for (dependencyInfo in collectConfigurations().collectDependencies()) { - val scope = dependencyInfo.scope - - if (dependencyInfo is DependencyInfo.CustomDependencyInfo) { - val files = dependencyInfo.files - val library = PLibrary(files.firstOrNull()?.nameWithoutExtension ?: "unnamed", classes = files) - - mainRoots += POrderRoot(PDependency.ModuleLibrary(library), scope) - continue - } - - dependencyInfo.processResolvedDependency(mainRoots, deferredRoots, dependencyMappers) - } - - return removeDuplicates(mainRoots + deferredRoots) - } -} - -fun DependencyInfo.processResolvedDependency( - mainConsumer: MutableList, - deferredConsumer: MutableList, - dependencyMappers: List -) { - val dependency = (this as? DependencyInfo.ResolvedDependencyInfo)?.dependency ?: return - - for (mapper in dependencyMappers) { - if (dependency.configuration in mapper.configurations && mapper.predicate(dependency)) { - val mappedDependency = mapper.mapping(dependency) - - if (mappedDependency != null) { - val mainDependency = mappedDependency.main - if (mainDependency != null) { - mainConsumer += POrderRoot(mainDependency, scope) - } - - for (deferredDep in mappedDependency.deferred) { - deferredConsumer += POrderRoot(deferredDep, scope) - } - } - - return + 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) } } } - mainConsumer += if (dependency.isModuleDependency && scope != Scope.TEST) { - POrderRoot(PDependency.Module(dependency.moduleName + ".src"), scope) - } else if (dependency.configuration == "tests-jar" || dependency.configuration == "jpsTest") { - POrderRoot( - PDependency.Module(dependency.moduleName + ".test"), - scope, - isProductionOnTestDependency = true - ) - } else { - val classes = dependency.moduleArtifacts.map { it.file } - val library = PLibrary(dependency.moduleName, classes) - POrderRoot(PDependency.ModuleLibrary(library), scope) - } + return removeDuplicates(mainRoots + deferredRoots) } -private fun resolveExtraDependencies(configuration: Configuration): List { - return configuration.dependencies - .filterIsInstance() - .map { it.resolve() } - .filter { isGradleApiDependency(it) } - .flatMap { it } -} - -private fun isGradleApiDependency(files: Iterable): Boolean { - return listOf("gradle-api", "groovy-all").all { dep -> - files.any { it.extension == "jar" && it.name.startsWith("$dep-") } - } -} - -private fun removeDuplicates(roots: List): List { +fun removeDuplicates(roots: List): List { val dependenciesByScope = roots.groupBy { it.scope }.mapValues { it.value.mapTo(mutableSetOf()) { it.dependency } } fun dependenciesFor(scope: Scope) = dependenciesByScope[scope] ?: emptySet() @@ -482,59 +403,11 @@ private fun removeDuplicates(roots: List): List { return result.toList() } -data class CollectedConfiguration( - val configuration: ResolvedConfiguration, - val scope: Scope, - val extraDependencies: List = emptyList()) +val Project.pillModuleName: String + get() = path.removePrefix(":").replace(':', '.') -sealed class DependencyInfo(val scope: Scope) { - class ResolvedDependencyInfo(scope: Scope, val dependency: ResolvedDependency) : DependencyInfo(scope) - class CustomDependencyInfo(scope: Scope, val files: List) : DependencyInfo(scope) -} - -val ResolvedDependency.isModuleDependency - get() = configuration in JpsCompatiblePlugin.MODULE_CONFIGURATIONS - -fun List.collectDependencies(): List { - val dependencies = mutableListOf() - - val unprocessed = LinkedList() - val existing = mutableSetOf>() - - for ((configuration, scope, extraDependencies) in this) { - for (dependency in configuration.firstLevelModuleDependencies) { - unprocessed += DependencyInfo.ResolvedDependencyInfo(scope, dependency) - } - - if (!extraDependencies.isEmpty()) { - unprocessed += DependencyInfo.CustomDependencyInfo(scope, extraDependencies) - } - } - - while (unprocessed.isNotEmpty()) { - val info = unprocessed.removeAt(0) - dependencies += info - - info as? DependencyInfo.ResolvedDependencyInfo ?: continue - - val data = Pair(info.scope, info.dependency) - existing += data - - for (child in info.dependency.children) { - if (Pair(info.scope, child) in existing) { - continue - } - - unprocessed += DependencyInfo.ResolvedDependencyInfo(info.scope, child) - } - } - - return dependencies -} - -private val Project.sourceSets: SourceSetContainer +val Project.sourceSets: SourceSetContainer? get() { - lateinit var result: SourceSetContainer - project.configure { result = sourceSets } - return result + val convention = project.convention.findPlugin(JavaPluginConvention::class.java) ?: return null + return convention.sourceSets } diff --git a/buildSrc/src/main/kotlin/pill/plugin.kt b/buildSrc/src/main/kotlin/pill/plugin.kt index ceac59288e8..64886cc00f8 100644 --- a/buildSrc/src/main/kotlin/pill/plugin.kt +++ b/buildSrc/src/main/kotlin/pill/plugin.kt @@ -20,44 +20,30 @@ class PillConfigurablePlugin : Plugin { class JpsCompatiblePlugin : Plugin { companion object { - val MODULE_CONFIGURATIONS = arrayOf("apiElements", "runtimeElements") - - private fun mapper(module: String, vararg configurations: String): DependencyMapper { - return DependencyMapper("org.jetbrains.kotlin", module, *configurations) { MappedDependency(PDependency.Library(module)) } - } - private fun getDependencyMappers(projectLibraries: List): List { val mappersForKotlinLibrariesExeptStdlib = projectLibraries .filter { it.name != "kotlin-stdlib" } - .mapTo(mutableListOf()) { mapper(it.name, "default", "distJar", *MODULE_CONFIGURATIONS) } + .map { DependencyMapper.forProject(it.originalName) { MappedDependency(PDependency.Library(it.name)) } } - return mappersForKotlinLibrariesExeptStdlib + listOf( - DependencyMapper("org.jetbrains.kotlin", "kotlin-stdlib", "distJar", *MODULE_CONFIGURATIONS) { + 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 } } + + return listOf( + DependencyMapper.forProject(":kotlin-stdlib") { MappedDependency( PDependency.Library("kotlin-stdlib"), listOf(PDependency.Library("annotations-13.0")) ) }, - DependencyMapper("org.jetbrains", "annotations", "default", "runtime", version = "13.0") { - MappedDependency( - null, - listOf(PDependency.Library("annotations-13.0")) - ) - }, - DependencyMapper("org.jetbrains.kotlin", "kotlin-reflect-api", *MODULE_CONFIGURATIONS) { - MappedDependency(PDependency.Library("kotlin-reflect")) - }, - DependencyMapper("org.jetbrains.kotlin", "kotlin-compiler-embeddable", "runtimeJar") { null }, - DependencyMapper("org.jetbrains.kotlin", "kotlin-stdlib-js", "distJar") { null }, - DependencyMapper("org.jetbrains.kotlin", "kotlin-compiler", "runtimeJar") { null }, - DependencyMapper("org.jetbrains.kotlin", "compiler", *MODULE_CONFIGURATIONS) { null }, - DependencyMapper("kotlin.build", "android", "default") { dep -> - val (sdkCommon, otherJars) = dep.moduleArtifacts.map { it.file }.partition { it.name == "sdk-common.jar" } - val mainLibrary = PDependency.ModuleLibrary(PLibrary(dep.moduleName, otherJars)) - val deferredLibrary = PDependency.ModuleLibrary(PLibrary(dep.moduleName + "-deferred", sdkCommon)) - MappedDependency(mainLibrary, listOf(deferredLibrary)) - } - ) + 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 { @@ -78,7 +64,8 @@ class JpsCompatiblePlugin : Plugin { PLibrary( library.name, classes = listOf(File(libraryPath, "$archivesBaseName.jar")).filterExisting(), - sources = listOf(File(libraryPath, "$archivesBaseName-sources.jar")).filterExisting() + sources = listOf(File(libraryPath, "$archivesBaseName-sources.jar")).filterExisting(), + originalName = library.path ) } diff --git a/buildSrc/src/main/kotlin/pill/render.kt b/buildSrc/src/main/kotlin/pill/render.kt index 90f2282c4f5..206130caf52 100644 --- a/buildSrc/src/main/kotlin/pill/render.kt +++ b/buildSrc/src/main/kotlin/pill/render.kt @@ -31,12 +31,7 @@ private fun renderModulesFile(project: PProject) = PFile( for (module in project.modules) { val moduleFilePath = pathContext(module.moduleFile) - - if (module.group != null) { - xml("module", "fileurl" to "file://$moduleFilePath", "filepath" to moduleFilePath, "group" to module.group) - } else { - xml("module", "fileurl" to "file://$moduleFilePath", "filepath" to moduleFilePath) - } + xml("module", "fileurl" to "file://$moduleFilePath", "filepath" to moduleFilePath) } } } @@ -85,7 +80,7 @@ private fun renderModule(project: PProject, module: PModule) = PFile( kotlinCompileOptions.noStdlib.option("noStdlib") kotlinCompileOptions.noReflect.option("noReflect") - kotlinCompileOptions.moduleName.option("moduleName") + module.name.option("moduleName") xml("option", "name" to "jvmTarget", "value" to platformVersion) kotlinCompileOptions.languageVersion.option("languageVersion") kotlinCompileOptions.apiVersion.option("apiVersion") @@ -202,4 +197,4 @@ private fun renderLibraryToXml(library: PLibrary, pathContext: PathContext, name } } -fun PLibrary.renderName() = name?.takeIf { it != "unspecified" } ?: classes.first().nameWithoutExtension +fun PLibrary.renderName() = name.takeIf { it != "unspecified" } ?: classes.first().nameWithoutExtension