IJPL-866 separate product modules on a class loader level (part 6 - compilation options)

GitOrigin-RevId: 315f7e1401d07a156402909b93d21ccbce63a60d
This commit is contained in:
Vladimir Krivosheev
2024-03-31 21:09:14 +03:00
committed by intellij-monorepo-bot
parent 91c5d45655
commit a7a3c0af43
9 changed files with 107 additions and 59 deletions
@@ -153,17 +153,23 @@ suspend inline fun BuildContext.executeStep(spanBuilder: SpanBuilder,
@Serializable
class BuiltinModulesFileData(
@JvmField val plugins: MutableList<String> = mutableListOf(),
@JvmField var modules: List<ModuleDescriptor> = emptyList(),
@JvmField var layout: List<ProductInfoLayoutItem> = emptyList(),
@JvmField val fileExtensions: MutableList<String> = mutableListOf(),
)
@Serializable
class ModuleDescriptor(
data class ProductInfoLayoutItem(
@JvmField val name: String,
@JvmField val isAlias: Boolean = false,
@JvmField val kind: ProductInfoLayoutItemKind,
@JvmField val classPath: List<String> = emptyList(),
)
@Suppress("EnumEntryName")
@Serializable
enum class ProductInfoLayoutItemKind {
plugin, pluginAlias, productModuleV2, moduleV2
}
sealed interface DistFileContent {
fun readAsStringForDebug(): String
}
@@ -56,7 +56,7 @@ fun reorderJar(relativePath: String, file: Path) {
}
}
private val excludedLibJars = java.util.Set.of(PlatformJarNames.TEST_FRAMEWORK_JAR, "junit.jar")
internal val excludedLibJars: Set<String> = java.util.Set.of(PlatformJarNames.TEST_FRAMEWORK_JAR, "junit.jar")
fun generateClasspath(homeDir: Path, libDir: Path): List<String> {
spanBuilder("generate classpath")
@@ -144,7 +144,11 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
}
launch(Dispatchers.IO) {
Files.writeString(runDir.resolve("core-classpath.txt"), classPath.joinToString(separator = "\n"))
val cp = classPath
.asSequence()
.filter { !excludedLibJars.contains(it.fileName.toString()) }
.joinToString(separator = "\n")
Files.writeString(runDir.resolve("core-classpath.txt"), cp)
}
request.platformClassPathConsumer?.invoke(classPath, runDir)
@@ -363,6 +367,13 @@ internal suspend fun createBuildContext(
spanBuilder("create build context").useWithScope {
// we cannot inject a proper build time as it is a part of resources, so, set to the first day of the current month
val buildOptionsTemplate = request.buildOptionsTemplate
val useCompiledClassesFromProjectOutput = buildOptionsTemplate?.useCompiledClassesFromProjectOutput ?: true
val classOutDir = if (useCompiledClassesFromProjectOutput) {
request.productionClassOutput.parent.toString()
}
else {
buildOptionsTemplate?.classOutDir ?: System.getProperty(PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY)
}
val options = BuildOptions(
jarCacheDir = jarCacheDir,
buildDateInSeconds = getBuildDateInSeconds(),
@@ -370,10 +381,10 @@ internal suspend fun createBuildContext(
validateImplicitPlatformModule = false,
skipDependencySetup = true,
useCompiledClassesFromProjectOutput = buildOptionsTemplate?.useCompiledClassesFromProjectOutput ?: true,
pathToCompiledClassesArchivesMetadata = buildOptionsTemplate?.pathToCompiledClassesArchivesMetadata,
pathToCompiledClassesArchive = buildOptionsTemplate?.pathToCompiledClassesArchive,
classOutDir = buildOptionsTemplate?.classOutDir ?: System.getProperty(PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY),
useCompiledClassesFromProjectOutput = useCompiledClassesFromProjectOutput,
pathToCompiledClassesArchivesMetadata = buildOptionsTemplate?.pathToCompiledClassesArchivesMetadata?.takeIf { !useCompiledClassesFromProjectOutput },
pathToCompiledClassesArchive = buildOptionsTemplate?.pathToCompiledClassesArchive?.takeIf { !useCompiledClassesFromProjectOutput },
classOutDir = classOutDir,
validateModuleStructure = false,
cleanOutDir = false,
@@ -8,7 +8,7 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.jetbrains.intellij.build.BuiltinModulesFileData
import org.jetbrains.intellij.build.ModuleDescriptor
import org.jetbrains.intellij.build.ProductInfoLayoutItemKind
import java.nio.file.Files
import java.nio.file.Path
@@ -18,24 +18,24 @@ fun readBuiltinModulesFile(file: Path): BuiltinModulesFileData {
fun customizeBuiltinModulesAllowOnlySpecified(
builtinModulesFile: Path,
moduleNames: List<String>?,
pluginAliases: Set<String>?,
pluginNames: List<String>?,
fileExtensions: List<String>?,
) {
Span.current().addEvent("File $builtinModulesFile before modification:\n" + Files.readString(builtinModulesFile))
val root = readBuiltinModulesFile(builtinModulesFile)
if (moduleNames != null) {
val existingValues = root.modules.associateByTo(HashMap(root.modules.size)) { it.name }
val newList = ArrayList<ModuleDescriptor>(moduleNames.size)
for (name in moduleNames) {
if (pluginAliases != null) {
val existingValues = root.layout.associateByTo(HashMap(root.layout.size)) { it.name }
for (name in pluginAliases) {
val item = existingValues.get(name)
requireNotNull(item) {
"Value '$name' in '$moduleNames' was not found across existing values in $builtinModulesFile:\n" + Files.readString(builtinModulesFile)
"Value '$name' in '$pluginAliases' was not found across existing values in $builtinModulesFile:\n" + Files.readString(builtinModulesFile)
}
newList.add(item)
}
root.modules = newList
root.layout = root.layout.filter {
if (it.kind == ProductInfoLayoutItemKind.pluginAlias) pluginAliases.contains(it.name) else true
}
}
if (pluginNames != null) {
@@ -16,10 +16,9 @@ import java.net.URL
import java.nio.file.Path
import java.util.*
fun collectCompatiblePluginsToPublish(builtinModuleData: BuiltinModulesFileData, context: BuildContext, result: MutableSet<PluginLayout>) {
val availableModulesAndPlugins = HashSet<String>(builtinModuleData.modules.size + builtinModuleData.plugins.size)
availableModulesAndPlugins.addAll(builtinModuleData.plugins)
builtinModuleData.modules.mapTo(availableModulesAndPlugins) { it.name }
internal fun collectCompatiblePluginsToPublish(builtinModuleData: BuiltinModulesFileData, context: BuildContext, result: MutableSet<PluginLayout>) {
val availableModulesAndPlugins = HashSet<String>(builtinModuleData.layout.size)
builtinModuleData.layout.mapTo(availableModulesAndPlugins) { it.name }
val descriptorMap = collectPluginDescriptors(skipImplementationDetailPlugins = true,
skipBundledPlugins = true,
@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.intellij.build.impl.compilation
import com.intellij.openapi.util.io.NioFiles
@@ -45,18 +45,27 @@ internal object CompiledClasses {
messages.error("'${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVE}' is specified, " +
"so 'incremental compilation' option cannot be enabled")
}
if (options.pathToCompiledClassesArchive != null && options.useCompiledClassesFromProjectOutput) {
messages.error("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, " +
"so '${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVE}' cannot be used")
if (options.useCompiledClassesFromProjectOutput) {
if (options.pathToCompiledClassesArchive != null) {
messages.error(
"'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, " +
"so '${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVE}' cannot be used"
)
}
if (options.pathToCompiledClassesArchivesMetadata != null) {
messages.error(
"'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, " +
"so '${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVES_METADATA}' cannot be used to fetch compile output"
)
}
}
if (options.pathToCompiledClassesArchivesMetadata != null && options.incrementalCompilation) {
messages.error("'${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVES_METADATA}' is specified, " +
"so 'incremental compilation' option cannot be used")
}
if (options.pathToCompiledClassesArchivesMetadata != null && options.useCompiledClassesFromProjectOutput) {
messages.error("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, " +
"so '${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVES_METADATA}' cannot be used to fetch compile output")
}
if (options.pathToCompiledClassesArchive != null && options.pathToCompiledClassesArchivesMetadata != null) {
messages.error("'${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVE}' is specified, " +
"so '${BuildOptions.INTELLIJ_BUILD_COMPILER_CLASSES_ARCHIVES_METADATA}' cannot be used to fetch compile output")
@@ -7,6 +7,8 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuiltinModulesFileData
import org.jetbrains.intellij.build.ProductInfoLayoutItem
import org.jetbrains.intellij.build.ProductInfoLayoutItemKind
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.attribute.FileTime
@@ -55,10 +57,8 @@ internal fun generateProductInfoJson(
bundledPlugins = builtinModules?.plugins ?: emptyList(),
fileExtensions = builtinModules?.fileExtensions ?: emptyList(),
modules = (builtinModules?.modules?.asSequence() ?: emptySequence()).filter { it.isAlias }.map { it.name }.toList(),
modulesV2 = (builtinModules?.modules?.asSequence() ?: emptySequence()).filter { !it.isAlias }.map {
ProductInfoModuleV2(name = it.name, classPath = it.classPath)
}.toList(),
modules = (builtinModules?.layout?.asSequence() ?: emptySequence()).filter { it.kind == ProductInfoLayoutItemKind.pluginAlias }.map { it.name }.toList(),
layout = builtinModules?.layout ?: emptyList(),
flavors = jbrFlavors + productFlavors,
)
@@ -90,17 +90,11 @@ data class ProductInfoData(
val bundledPlugins: List<String>,
// it is not modules, but plugin aliases
val modules: List<String>,
val modulesV2: List<ProductInfoModuleV2>,
val layout: List<ProductInfoLayoutItem>,
val fileExtensions: List<String>,
val flavors: List<ProductFlavorData> = emptyList(),
)
@Serializable
data class ProductInfoModuleV2(
@JvmField val name: String,
@JvmField val classPath: List<String> = emptyList(),
)
@Serializable
data class ProductFlavorData(@JvmField val id: String)
@@ -187,11 +187,11 @@
"type": "string"
}
},
"modulesV2": {
"layout": {
"description": "IDs of product modules (non-removable bundled plugins)",
"type": "array",
"items": {
"type": "string"
"type": "object"
}
},
"flavors": {
@@ -199,14 +199,27 @@
"type": "array",
"items": {
"type": "object",
"required": ["id"],
"additionalProperties": false,
"minItems": 0,
"properties": {
"id": {
"name": {
"type": "string"
},
"kind": {
"type": "string"
},
"classPath": {
"type": "array",
"items": {
"type": "string",
"minItems": 0
}
}
}
},
"required": [
"name",
"kind"
]
}
}
}
@@ -12,6 +12,7 @@ import com.intellij.util.io.jackson.array
import com.intellij.util.io.jackson.obj
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import java.io.OutputStreamWriter
import java.io.Writer
import java.nio.charset.StandardCharsets
@@ -41,18 +42,29 @@ private class BundledPluginsLister : ModernApplicationStarter() {
}
JsonFactory().createGenerator(out).use { writer ->
val plugins = PluginManagerCore.getPluginSet().enabledPlugins
val modules = HashSet<ModuleDescriptor>()
val layout = HashSet<LayoutItemDescriptor>()
val pluginIds = ArrayList<String>(plugins.size)
val homeDir = Path.of(PathManager.getHomePath())
for (plugin in plugins) {
layout.add(LayoutItemDescriptor(
name = plugin.pluginId.idString,
kind = ProductInfoLayoutItemKind.plugin,
classPath = plugin.jarFiles?.map { it.relativeTo(homeDir).invariantSeparatorsPathString } ?: emptyList()
))
pluginIds.add(plugin.pluginId.idString)
plugin.pluginAliases.mapTo(modules) {
ModuleDescriptor(name = it.idString, isAlias = true, classPath = emptyList())
plugin.pluginAliases.mapTo(layout) {
LayoutItemDescriptor(name = it.idString, kind = ProductInfoLayoutItemKind.pluginAlias, classPath = emptyList())
}
plugin.content.modules.mapTo(modules) {
ModuleDescriptor(
plugin.content.modules.mapTo(layout) {
LayoutItemDescriptor(
name = it.name,
isAlias = false,
kind = if (plugin.pluginId == PluginManagerCore.CORE_ID) {
ProductInfoLayoutItemKind.productModuleV2
}
else {
ProductInfoLayoutItemKind.moduleV2
},
classPath = it.requireDescriptor().jarFiles?.map {
file -> file.relativeTo(homeDir).invariantSeparatorsPathString
} ?: emptyList(),
@@ -72,13 +84,11 @@ private class BundledPluginsLister : ModernApplicationStarter() {
extensions.sort()
writer.obj {
writer.array("modules") {
for (module in modules.sortedBy { it.name }) {
writer.array("layout") {
for (module in layout.sortedBy { it.name }) {
writer.obj {
writer.writeStringField("name", module.name)
if (module.isAlias) {
writer.writeBooleanField("isAlias", module.isAlias)
}
writer.writeStringField("kind", module.kind.name)
if (module.classPath.isNotEmpty()) {
writeList(writer, "classPath", module.classPath)
}
@@ -103,12 +113,18 @@ private class BundledPluginsLister : ModernApplicationStarter() {
}
}
private class ModuleDescriptor(
private class LayoutItemDescriptor(
@JvmField val name: String,
@JvmField val isAlias: Boolean = false,
@JvmField val kind: ProductInfoLayoutItemKind,
@JvmField val classPath: List<String> = emptyList(),
)
@Suppress("EnumEntryName")
@Serializable
private enum class ProductInfoLayoutItemKind {
plugin, pluginAlias, productModuleV2, moduleV2
}
private fun writeList(writer: JsonGenerator, name: String, elements: Collection<String>) {
writer.array(name) {
for (module in elements) {