[build scripts] unified handling of boot classpath between launchers (IJPL-174985)

... + centralized setting of routing FS options and more realistic VM options in integration tests

GitOrigin-RevId: 54dad6314198edaf5ce0f99c33c213305d78e322
This commit is contained in:
Roman Shevchenko
2025-01-14 18:48:13 +01:00
committed by intellij-monorepo-bot
parent f951c6bf77
commit 80a4a5e8fa
13 changed files with 136 additions and 102 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.intellij.build
import com.intellij.platform.buildData.productInfo.ProductInfoLayoutItem
@@ -63,13 +63,10 @@ interface BuildContext : CompilationContext {
*/
val systemSelector: String
/**
* Names of JARs inside `IDE_HOME/lib` directory which need to be added to the JVM boot classpath to start the IDE.
*/
val xBootClassPathJarNames: List<String>
/**
* Names of JARs inside `IDE_HOME/lib` directory which need to be added to the JVM classpath to start the IDE.
*
* **Note**: In terms of JVM, these JARs form a regular classpath (`-cp`), not a boot classpath (`-Xbootclasspath`).
*/
var bootClassPathJarNames: List<String>
@@ -137,6 +134,7 @@ interface BuildContext : CompilationContext {
arch: JvmArchitecture,
isScript: Boolean = false,
isPortableDist: Boolean = false,
isQodana: Boolean = false,
): List<String>
fun findApplicationInfoModule(): JpsModule

View File

@@ -26,10 +26,7 @@ abstract class JetBrainsProductProperties : ProductProperties() {
sbomOptions.creator = "Organization: ${Suppliers.JETBRAINS}"
sbomOptions.license = SoftwareBillOfMaterials.Options.DistributionLicense.JETBRAINS
productLayout.addPlatformSpec { layout, _ ->
layout.withModule(IJENT_BOOT_CLASSPATH_MODULE, PLATFORM_CORE_NIO_FS)
xBootClassPathJarNames += PLATFORM_CORE_NIO_FS
}
productLayout.addPlatformSpec { layout, _ -> layout.withModule(IJENT_BOOT_CLASSPATH_MODULE, PLATFORM_CORE_NIO_FS) }
}
protected fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean {

View File

@@ -1,8 +1,9 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet", "ReplaceJavaStaticMethodWithKotlinAnalog")
package org.jetbrains.intellij.build
import com.intellij.platform.ijent.community.buildConstants.isIjentWslFsEnabledByDefaultForProduct
import com.intellij.platform.util.putMoreLikelyPluginJarsFirst
import com.intellij.util.lang.HashMapZipFile
import io.opentelemetry.api.common.AttributeKey
@@ -11,6 +12,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import org.jetbrains.annotations.VisibleForTesting
import org.jetbrains.intellij.build.impl.ModuleOutputPatcher
import org.jetbrains.intellij.build.impl.PlatformJarNames
import org.jetbrains.intellij.build.impl.PlatformJarNames.PLATFORM_CORE_NIO_FS
import org.jetbrains.intellij.build.impl.PluginLayout
import org.jetbrains.intellij.build.impl.projectStructureMapping.DistributionFileEntry
import org.jetbrains.intellij.build.io.INDEX_FILENAME
@@ -58,17 +60,22 @@ suspend fun reorderJar(relativePath: String, file: Path) {
}
}
internal val excludedLibJars: Set<String> = java.util.Set.of(PlatformJarNames.TEST_FRAMEWORK_JAR)
internal fun excludedLibJars(context: BuildContext): Set<String> =
setOf(PlatformJarNames.TEST_FRAMEWORK_JAR) +
if (isIjentWslFsEnabledByDefaultForProduct(context.productProperties.platformPrefix)) setOf(PLATFORM_CORE_NIO_FS) else emptySet()
internal suspend fun generateClasspath(homeDir: Path, libDir: Path): List<String> {
internal suspend fun generateClasspath(context: BuildContext): List<String> {
val homeDir = context.paths.distAllDir
val libDir = homeDir.resolve("lib")
return spanBuilder("generate classpath")
.setAttribute("dir", homeDir.toString())
.use { span ->
val excluded = excludedLibJars(context)
val existing = HashSet<Path>()
addJarsFromDir(dir = libDir) { paths ->
paths.filterTo(existing) { !excludedLibJars.contains(it.fileName.toString()) }
addJarsFromDir(libDir) { paths ->
paths.filterTo(existing) { !excluded.contains(it.fileName.toString()) }
}
val files = computeAppClassPath(libDir = libDir, existing = existing, homeDir = homeDir)
val files = computeAppClassPath(libDir, existing, homeDir)
val result = files.map { libDir.relativize(it).toString() }
span.setAttribute(AttributeKey.stringArrayKey("result"), result)
result

View File

@@ -1,22 +1,25 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment", "LiftReturnOrAssignment")
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.intellij.build.dev
import com.intellij.openapi.util.io.NioFiles
import com.intellij.platform.buildData.productInfo.ProductInfoData
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.JvmArchitecture
import org.jetbrains.intellij.build.VmProperties
import org.jetbrains.intellij.build.closeKtorClient
import kotlinx.serialization.json.decodeFromStream
import org.jetbrains.intellij.build.*
import org.jetbrains.intellij.build.impl.productInfo.PRODUCT_INFO_FILE_NAME
import org.jetbrains.intellij.build.impl.productInfo.jsonEncoder
import org.jetbrains.intellij.build.telemetry.TraceManager
import org.jetbrains.intellij.build.telemetry.use
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.readLines
@Serializable
internal data class Configuration(@JvmField val products: Map<String, ProductConfiguration>)
@@ -31,6 +34,7 @@ private const val PRODUCTS_PROPERTIES_PATH = "build/dev-build.json"
*/
private const val CUSTOM_PRODUCT_PROPERTIES_PATH = "idea.product.properties.path"
@Deprecated("Prefer `readVmOptions` for more accurate result")
@Suppress("SpellCheckingInspection")
fun getIdeSystemProperties(runDir: Path): VmProperties {
val result = LinkedHashMap<String, String>()
@@ -58,6 +62,30 @@ fun getIdeSystemProperties(runDir: Path): VmProperties {
return VmProperties(result)
}
fun readVmOptions(runDir: Path): List<String> {
val result = ArrayList<String>()
val vmOptionsFile = Files.newDirectoryStream(runDir.resolve("bin"), "*.vmoptions").use { it.singleOrNull() }
require(vmOptionsFile != null) {
"No single *.vmoptions file in ${runDir} (${NioFiles.list(runDir).map(Path::getFileName).joinToString()})}"
}
result += vmOptionsFile.readLines()
result += "-Djb.vmOptionsFile=${vmOptionsFile}"
val productInfoFile = runDir.resolve("bin").resolve(PRODUCT_INFO_FILE_NAME)
if (productInfoFile.exists()) {
val productJson = productInfoFile.inputStream().use { jsonEncoder.decodeFromStream<ProductInfoData>(it) }
val macroName = when (OsFamily.currentOs) {
OsFamily.WINDOWS -> "%IDE_HOME%"
OsFamily.MACOS -> "\$APP_PACKAGE/Contents"
OsFamily.LINUX -> "\$IDE_HOME"
}
result += productJson.launch[0].additionalJvmArguments.map { it.replace(macroName, runDir.toString()) }
}
return result
}
/** Returns IDE installation directory */
suspend fun buildProductInProcess(request: BuildRequest): Path {
if (request.tracer != null) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplacePutWithAssignment")
package org.jetbrains.intellij.build.dev
@@ -23,6 +23,7 @@ import org.jetbrains.intellij.build.*
import org.jetbrains.intellij.build.BuildOptions.Companion.PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY
import org.jetbrains.intellij.build.BuildPaths.Companion.COMMUNITY_ROOT
import org.jetbrains.intellij.build.impl.*
import org.jetbrains.intellij.build.impl.productInfo.PRODUCT_INFO_FILE_NAME
import org.jetbrains.intellij.build.impl.projectStructureMapping.DistributionFileEntry
import org.jetbrains.intellij.build.impl.projectStructureMapping.ModuleOutputEntry
import org.jetbrains.intellij.build.jarCache.LocalDiskJarCacheManager
@@ -42,6 +43,8 @@ import java.time.DayOfWeek
import java.time.OffsetDateTime
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
import kotlin.io.path.createDirectories
import kotlin.io.path.moveTo
import kotlin.time.Duration.Companion.seconds
data class BuildRequest(
@@ -55,7 +58,7 @@ data class BuildRequest(
@JvmField val platformClassPathConsumer: ((classPath: Set<Path>, runDir: Path) -> Unit)? = null,
/**
* If `true`, the dev build will include a [runtime module repository](psi_element://com.intellij.platform.runtime.repository).
* It's currently used only to run an instance of JetBrains Client from IDE's installation,
* It's currently used only to run an instance of JetBrains Client from IDE's installation,
* and its generation makes build a little longer, so it should be enabled only if needed.
*/
@JvmField val generateRuntimeModuleRepository: Boolean = false,
@@ -134,11 +137,16 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
launch(Dispatchers.IO) {
// PathManager.getBinPath() is used as a working dir for maven
val binDir = Files.createDirectories(runDir.resolve("bin"))
val oldFiles = Files.newDirectoryStream(binDir).use { it -> it.toCollection(HashSet()) }
val oldFiles = Files.newDirectoryStream(binDir).use { it.toCollection(HashSet()) }
val osDistributionBuilder = getOsDistributionBuilder(os = OsFamily.currentOs, context = context)
if (osDistributionBuilder != null) {
oldFiles.remove(osDistributionBuilder.writeVmOptions(binDir))
// the file cannot be placed right into the distribution as it throws off home dir detection in `PathManager#getHomeDirFor`
val productInfoDir = context.paths.tempDir.resolve("product-info").createDirectories()
val productInfoFile = osDistributionBuilder.writeProductInfoFile(productInfoDir, JvmArchitecture.currentJvmArch)
oldFiles.remove(productInfoFile.moveTo(binDir.resolve(PRODUCT_INFO_FILE_NAME), overwrite = true))
NioFiles.deleteRecursively(productInfoDir)
}
val ideaPropertyFile = binDir.resolve(PathManager.PROPERTIES_FILE_NAME)
@@ -164,9 +172,10 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
if (request.writeCoreClasspath) {
launch(Dispatchers.IO) {
val excluded = excludedLibJars(context)
val classPathString = classPath
.asSequence()
.filter { !excludedLibJars.contains(it.fileName.toString()) }
.filter { it.fileName.toString() !in excluded }
.joinToString(separator = "\n")
Files.writeString(runDir.resolve("core-classpath.txt"), classPathString)
}
@@ -464,6 +473,7 @@ private suspend fun createBuildContext(
BuildOptions.PREBUILD_SHARED_INDEXES,
BuildOptions.GENERATE_JAR_ORDER_STEP,
BuildOptions.FUS_METADATA_BUNDLE_STEP,
BuildOptions.PROVIDED_MODULES_LIST_STEP,
)
if (request.isUnpackedDist && options.enableEmbeddedFrontend) {
@@ -472,6 +482,11 @@ private suspend fun createBuildContext(
options.generateRuntimeModuleRepository = options.generateRuntimeModuleRepository && request.generateRuntimeModuleRepository
buildOptionsTemplate?.let { template ->
options.isInDevelopmentMode = template.isInDevelopmentMode
options.isTestBuild = template.isTestBuild
}
val tempDir = buildDir.resolve("temp")
val result = BuildPaths(
communityHomeDirRoot = COMMUNITY_ROOT,
@@ -663,4 +678,4 @@ private fun getCommunityHomePath(homePath: Path): Path = if (Files.isDirectory(h
fun getAdditionalPluginMainModules(): List<String> {
return System.getProperty("additional.modules")?.splitToSequence(',')?.map { it.trim() }?.filter { it.isNotEmpty() }?.toList() ?: emptyList()
}
}

View File

@@ -1,9 +1,12 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet", "ReplaceJavaStaticMethodWithKotlinAnalog")
package org.jetbrains.intellij.build.impl
import com.dynatrace.hash4j.hashing.HashStream64
import com.intellij.platform.ijent.community.buildConstants.IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY
import com.intellij.platform.ijent.community.buildConstants.MULTI_ROUTING_FILE_SYSTEM_VMOPTIONS
import com.intellij.platform.ijent.community.buildConstants.isIjentWslFsEnabledByDefaultForProduct
import com.intellij.platform.runtime.product.ProductMode
import com.intellij.util.containers.with
import io.opentelemetry.api.common.AttributeKey
@@ -13,6 +16,7 @@ import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.*
import org.jetbrains.intellij.build.*
import org.jetbrains.intellij.build.impl.PlatformJarNames.PLATFORM_CORE_NIO_FS
import org.jetbrains.intellij.build.impl.plugins.PluginAutoPublishList
import org.jetbrains.intellij.build.io.runProcess
import org.jetbrains.intellij.build.jarCache.JarCacheManager
@@ -80,9 +84,6 @@ class BuildContextImpl internal constructor(
jarCacheManager.cleanup()
}
override val xBootClassPathJarNames: List<String>
get() = productProperties.xBootClassPathJarNames
override var bootClassPathJarNames: List<String> = java.util.List.of(PLATFORM_LOADER_JAR)
override val ideMainClassName: String
@@ -316,8 +317,21 @@ class BuildContextImpl internal constructor(
}
@Suppress("SpellCheckingInspection")
override fun getAdditionalJvmArguments(os: OsFamily, arch: JvmArchitecture, isScript: Boolean, isPortableDist: Boolean): List<String> {
override fun getAdditionalJvmArguments(os: OsFamily, arch: JvmArchitecture, isScript: Boolean, isPortableDist: Boolean, isQodana: Boolean): List<String> {
val jvmArgs = ArrayList<String>()
val macroName = when (os) {
OsFamily.WINDOWS -> "%IDE_HOME%"
OsFamily.MACOS -> "\$APP_PACKAGE${if (isPortableDist) "" else "/Contents"}"
OsFamily.LINUX -> "\$IDE_HOME"
}
val useMultiRoutingFs = !isQodana && isIjentWslFsEnabledByDefaultForProduct(productProperties.platformPrefix)
val bcpJarNames = productProperties.xBootClassPathJarNames + if (useMultiRoutingFs) listOf(PLATFORM_CORE_NIO_FS) else emptyList()
if (bcpJarNames.isNotEmpty()) {
val separator = if (os == OsFamily.WINDOWS) ";" else ":"
val bootCp = bcpJarNames.joinToString(separator) { "${macroName}/lib/${it}" }
jvmArgs += "-Xbootclasspath/a:${bootCp}".let { if (isScript) '"' + it + '"' else it }
}
if (productProperties.enableCds) {
val cacheDir = if (os == OsFamily.WINDOWS) "%IDE_CACHE_DIR%\\" else "\$IDE_CACHE_DIR/"
@@ -333,11 +347,6 @@ class BuildContextImpl internal constructor(
jvmArgs.add("-Didea.vendor.name=${applicationInfo.shortCompanyName}")
jvmArgs.add("-Didea.paths.selector=${systemSelector}")
val macroName = when (os) {
OsFamily.WINDOWS -> "%IDE_HOME%"
OsFamily.MACOS -> "\$APP_PACKAGE${if (isPortableDist) "" else "/Contents"}"
OsFamily.LINUX -> "\$IDE_HOME"
}
jvmArgs.add("-Djna.boot.library.path=${macroName}/lib/jna/${arch.dirName}".let { if (isScript) '"' + it + '"' else it })
jvmArgs.add("-Dpty4j.preferred.native.folder=${macroName}/lib/pty4j".let { if (isScript) '"' + it + '"' else it })
// require bundled JNA dispatcher lib
@@ -357,6 +366,13 @@ class BuildContextImpl internal constructor(
jvmArgs.add("-Didea.platform.prefix=${productProperties.platformPrefix}")
}
if (os == OsFamily.WINDOWS) {
jvmArgs += "-D${IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY}=${useMultiRoutingFs}"
if (useMultiRoutingFs) {
jvmArgs += MULTI_ROUTING_FILE_SYSTEM_VMOPTIONS
}
}
jvmArgs.addAll(productProperties.additionalIdeJvmArguments)
jvmArgs.addAll(productProperties.getAdditionalContextDependentIdeJvmArguments(this))

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet")
package org.jetbrains.intellij.build.impl
@@ -88,10 +88,7 @@ internal suspend fun buildDistribution(
if (!isUpdateFromSources && context.productProperties.scrambleMainJar) {
scramble(state.platform, context)
}
val distAllDir = context.paths.distAllDir
val libDir = distAllDir.resolve("lib")
context.bootClassPathJarNames = if (context.useModularLoader) listOf(PLATFORM_LOADER_JAR) else generateClasspath(homeDir = distAllDir, libDir = libDir)
context.bootClassPathJarNames = if (context.useModularLoader) listOf(PLATFORM_LOADER_JAR) else generateClasspath(context)
result
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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
import com.intellij.openapi.util.SystemInfoRt
@@ -26,6 +26,7 @@ import java.nio.file.StandardCopyOption
import kotlin.io.path.exists
import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.readText
import kotlin.time.Duration.Companion.minutes
private const val NO_RUNTIME_SUFFIX = "-no-jbr"
@@ -130,9 +131,7 @@ class LinuxDistributionBuilder(
}
}
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture) {
generateProductJson(targetDir, arch)
}
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture): Path = writeProductJsonFile(targetDir, arch)
override fun writeVmOptions(distBinDir: Path): Path = writeLinuxVmOptions(distBinDir, context)
@@ -173,7 +172,7 @@ class LinuxDistributionBuilder(
}
val productJsonDir = context.paths.tempDir.resolve("linux.dist.product-info.json${suffix}")
generateProductJson(productJsonDir, arch, withRuntime = runtimeDir != null)
writeProductJsonFile(productJsonDir, arch, withRuntime = runtimeDir != null)
dirs.add(productJsonDir)
spanBuilder("build Linux tar.gz")
@@ -270,14 +269,9 @@ class LinuxDistributionBuilder(
""".trimMargin()
)
val productJsonDir = context.paths.tempDir.resolve("linux.dist.snap.product-info.json.$architecture")
val jsonText = generateProductJson(productJsonDir, arch)
validateProductJson(
jsonText,
relativePathToProductJson = "",
installationDirectories = listOf(context.paths.distAllDir, unixDistPath, runtimeDir),
installationArchives = listOf(),
context
)
val productJsonFile = writeProductJsonFile(productJsonDir, arch)
val installationDirectories = listOf(context.paths.distAllDir, unixDistPath, runtimeDir)
validateProductJson(jsonText = productJsonFile.readText(), relativePathToProductJson = "", installationDirectories, installationArchives = emptyList(), context)
val resultDir = snapDir.resolve("result")
Files.createDirectories(resultDir)
@@ -330,7 +324,7 @@ class LinuxDistributionBuilder(
override fun isRuntimeBundled(file: Path): Boolean = !file.name.contains(NO_RUNTIME_SUFFIX)
private suspend fun generateProductJson(targetDir: Path, arch: JvmArchitecture, withRuntime: Boolean = true): String {
private suspend fun writeProductJsonFile(targetDir: Path, arch: JvmArchitecture, withRuntime: Boolean = true): Path {
val embeddedFrontendLaunchData = generateEmbeddedFrontendLaunchData(arch, OsFamily.LINUX, context) {
"bin/${it.productProperties.baseFileName}64.vmoptions"
}
@@ -354,8 +348,9 @@ class LinuxDistributionBuilder(
),
context
)
writeProductInfoJson(targetFile = targetDir.resolve(PRODUCT_INFO_FILE_NAME), json = json, context = context)
return json
val file = targetDir.resolve(PRODUCT_INFO_FILE_NAME)
writeProductInfoJson(file, json, context)
return file
}
private fun generateVersionMarker(unixDistPath: Path) {
@@ -405,17 +400,11 @@ class LinuxDistributionBuilder(
classPath += "\nCLASS_PATH=\"\$CLASS_PATH:\$IDE_HOME/lib/${classPathJars[i]}\""
}
val additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.LINUX, arch, isScript = true).toMutableList()
additionalJvmArguments.addAll(nonCustomizableJvmArgs)
if (!context.xBootClassPathJarNames.isEmpty()) {
val bootCp = context.xBootClassPathJarNames.joinToString(separator = ":") { "\$IDE_HOME/lib/${it}" }
additionalJvmArguments.add("\"-Xbootclasspath/a:$bootCp\"")
}
val additionalJvmArgs = additionalJvmArguments.joinToString(separator = " ")
val additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.LINUX, arch, isScript = true) + nonCustomizableJvmArgs
val additionalTemplateValues = listOf(
Pair("vm_options", context.productProperties.baseFileName),
Pair("system_selector", context.systemSelector),
Pair("ide_jvm_args", additionalJvmArgs),
Pair("ide_jvm_args", additionalJvmArguments.joinToString(separator = " ")),
Pair("ide_default_xmx", defaultXmxParameter.trim()),
Pair("class_path", classPath),
Pair("main_class_name", context.ideMainClassName),

View File

@@ -182,9 +182,11 @@ class MacDistributionBuilder(
}
}
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture) {
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture): Path {
val json = generateProductJson(context, arch, withRuntime = true)
writeProductInfoJson(targetDir.resolve("Resources/${PRODUCT_INFO_FILE_NAME}"), json, context)
val file = targetDir.resolve("Resources/${PRODUCT_INFO_FILE_NAME}")
writeProductInfoJson(file, json, context)
return file
}
private suspend fun signMacBinaries(osAndArchSpecificDistPath: Path, runtimeDist: Path, arch: JvmArchitecture) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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
import com.intellij.openapi.util.SystemInfoRt
@@ -31,7 +31,7 @@ interface OsSpecificDistributionBuilder {
suspend fun buildArtifacts(osAndArchSpecificDistPath: Path, arch: JvmArchitecture)
suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture)
suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture): Path
fun generateExecutableFilesPatterns(includeRuntime: Boolean, arch: JvmArchitecture): Sequence<String> = emptySequence()

View File

@@ -1,13 +1,10 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.openapi.util.io.NioFiles
import com.intellij.openapi.util.text.StringUtilRt
import com.intellij.platform.buildData.productInfo.ProductInfoLaunchData
import com.intellij.platform.ijent.community.buildConstants.IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY
import com.intellij.platform.ijent.community.buildConstants.MULTI_ROUTING_FILE_SYSTEM_VMOPTIONS
import com.intellij.platform.ijent.community.buildConstants.isIjentWslFsEnabledByDefaultForProduct
import com.jetbrains.plugin.structure.base.utils.exists
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.trace.Span
@@ -28,6 +25,7 @@ import java.time.LocalDate
import kotlin.io.path.absolutePathString
import kotlin.io.path.extension
import kotlin.io.path.name
import kotlin.io.path.readText
internal class WindowsDistributionBuilder(
override val context: BuildContext,
@@ -128,9 +126,9 @@ internal class WindowsDistributionBuilder(
context.executeStep(spanBuilder("build Windows installer").setAttribute("arch", arch.dirName), BuildOptions.WINDOWS_EXE_INSTALLER_STEP) {
val productJsonDir = Files.createTempDirectory(context.paths.tempDir, "win-product-info")
val jsonText = generateProductJson(productJsonDir, context, arch)
val productJsonFile = writeProductJsonFile(productJsonDir, arch)
val installationDirectories = listOf(context.paths.distAllDir, osAndArchSpecificDistPath, runtimeDir)
validateProductJson(jsonText, relativePathToProductJson = "", installationDirectories, installationArchives = emptyList(), context)
validateProductJson(jsonText = productJsonFile.readText(), relativePathToProductJson = "", installationDirectories, installationArchives = emptyList(), context)
launch(Dispatchers.IO + CoroutineName("build Windows ${arch.dirName} installer")) {
exePath = buildNsisInstaller(osAndArchSpecificDistPath, additionalDirectoryToInclude = productJsonDir, suffix(arch), customizer, runtimeDir, context)
}
@@ -152,9 +150,7 @@ internal class WindowsDistributionBuilder(
}
}
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture) {
generateProductJson(targetDir = targetDir, context = context, arch = arch)
}
override suspend fun writeProductInfoFile(targetDir: Path, arch: JvmArchitecture): Path = writeProductJsonFile(targetDir, arch)
private fun generateScripts(distBinDir: Path, arch: JvmArchitecture) {
val fullName = context.applicationInfo.fullProductName
@@ -168,13 +164,7 @@ internal class WindowsDistributionBuilder(
classPath += "\nSET \"CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\${classPathJars[i]}\""
}
var additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.WINDOWS, arch, isScript = true)
if (!context.xBootClassPathJarNames.isEmpty()) {
additionalJvmArguments = additionalJvmArguments.toMutableList()
val bootCp = context.xBootClassPathJarNames.joinToString(separator = ";") { "%IDE_HOME%\\lib\\${it}" }
additionalJvmArguments.add("\"-Xbootclasspath/a:$bootCp\"")
}
val additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.WINDOWS, arch, isScript = true)
val winScripts = context.paths.communityHomeDir.resolve("platform/build-scripts/resources/win/scripts")
val actualScriptNames = Files.newDirectoryStream(winScripts).use { dirStream -> dirStream.map { it.fileName.toString() }.sorted() }
@@ -261,7 +251,7 @@ internal class WindowsDistributionBuilder(
}
val productJsonDir = context.paths.tempDir.resolve("win.dist.product-info.json.zip${zipNameSuffix}")
generateProductJson(productJsonDir, context, arch, withRuntime = runtimeDir != null)
writeProductJsonFile(productJsonDir, arch, withRuntime = runtimeDir != null)
dirs.add(productJsonDir)
val zipPrefix = customizer.getRootDirectoryName(context.applicationInfo, context.buildNumber)
@@ -383,22 +373,16 @@ internal class WindowsDistributionBuilder(
private fun writeWindowsVmOptions(distBinDir: Path, context: BuildContext): Path {
val vmOptionsFile = distBinDir.resolve("${context.productProperties.baseFileName}64.exe.vmoptions")
var vmOptions = VmOptionsGenerator.generate(context).asSequence()
val isIjentWslFsEnabled = isIjentWslFsEnabledByDefaultForProduct(context.productProperties.platformPrefix)
vmOptions += "-D${IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY}=${isIjentWslFsEnabled}"
if (isIjentWslFsEnabled) {
vmOptions += MULTI_ROUTING_FILE_SYSTEM_VMOPTIONS
}
val vmOptions = VmOptionsGenerator.generate(context).asSequence()
VmOptionsGenerator.writeVmOptions(vmOptionsFile, vmOptions, separator = "\r\n")
return vmOptionsFile
}
private suspend fun generateProductJson(targetDir: Path, context: BuildContext, arch: JvmArchitecture, withRuntime: Boolean = true): String {
private suspend fun writeProductJsonFile(targetDir: Path, arch: JvmArchitecture, withRuntime: Boolean = true): Path {
val embeddedFrontendLaunchData = generateEmbeddedFrontendLaunchData(arch = arch, os = OsFamily.WINDOWS, ideContext = context) {
"bin/${it.productProperties.baseFileName}64.exe.vmoptions"
}
val qodanaCustomLaunchData = generateQodanaLaunchData(context, arch, OsFamily.WINDOWS)
val json = generateProductInfoJson(
relativePathToBin = "bin",
builtinModules = context.builtinModule,
@@ -416,8 +400,9 @@ internal class WindowsDistributionBuilder(
)
),
context)
writeProductInfoJson(targetDir.resolve(PRODUCT_INFO_FILE_NAME), json, context)
return json
val file = targetDir.resolve(PRODUCT_INFO_FILE_NAME)
writeProductInfoJson(file, json, context)
return file
}
private fun toDosLineEndings(x: String): String = x.replace("\r", "").replace("\n", "\r\n")

View File

@@ -71,7 +71,7 @@ internal fun generateProductInfoJson(
private fun generateGitRevisionProperty(context: BuildContext): CustomProperty? {
val gitRoot = context.paths.projectHome
if (!gitRoot.resolve(".git").isDirectory) {
if (!context.options.isInDevelopmentMode) {
if (!context.options.isInDevelopmentMode && !context.options.isTestBuild) {
context.messages.error("Cannot find Git repository root in '$gitRoot'")
}
return null

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 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.qodana
import org.jetbrains.intellij.build.BuildContext
@@ -13,9 +13,9 @@ internal fun generateQodanaLaunchData(
os: OsFamily,
): CustomCommandLaunchData? {
val qodanaProductProperties = ideContext.productProperties.qodanaProductProperties ?: return null
val vmOptions = ideContext.getAdditionalJvmArguments(os, arch) + qodanaProductProperties.getAdditionalVmOptions(ideContext)
val vmOptions = ideContext.getAdditionalJvmArguments(os, arch, isQodana = true) + qodanaProductProperties.getAdditionalVmOptions(ideContext)
return CustomCommandLaunchData(
commands = listOf("qodana"),
additionalJvmArguments = vmOptions
)
}
}