Cleanup (minor optimization; NIO; quickfixes; typos; formatting)

GitOrigin-RevId: 529bc820f714a9b7918a2c9d25240e44ae929f53
This commit is contained in:
Roman Shevchenko
2025-01-14 20:03:09 +01:00
committed by intellij-monorepo-bot
parent 80a4a5e8fa
commit 4b38f00a72
27 changed files with 442 additions and 750 deletions

View File

@@ -1,6 +1,7 @@
// 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 kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.plus
import org.jetbrains.intellij.build.BuildPaths.Companion.COMMUNITY_ROOT
@@ -14,16 +15,12 @@ import java.nio.file.Path
internal suspend fun createCommunityBuildContext(
options: BuildOptions = BuildOptions(),
projectHome: Path = COMMUNITY_ROOT.communityRoot,
): BuildContext {
return BuildContextImpl.createContext(projectHome = projectHome,
productProperties = IdeaCommunityProperties(COMMUNITY_ROOT.communityRoot),
setupTracer = true,
options = options)
}
): BuildContext = BuildContextImpl.createContext(
projectHome, IdeaCommunityProperties(COMMUNITY_ROOT.communityRoot), setupTracer = true, options = options)
open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIdeaProperties() {
companion object {
val MAVEN_ARTIFACTS_ADDITIONAL_MODULES = persistentListOf(
val MAVEN_ARTIFACTS_ADDITIONAL_MODULES: PersistentList<String> = persistentListOf(
"intellij.tools.jps.build.standalone",
"intellij.devkit.runtimeModuleRepository.jps",
"intellij.devkit.jps",
@@ -37,8 +34,7 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
)
}
override val baseFileName: String
get() = "idea"
override val baseFileName: String = "idea"
init {
platformPrefix = "Idea"
@@ -51,9 +47,11 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
"intellij.platform.starter",
"intellij.idea.community.customization",
)
productLayout.bundledPluginModules = IDEA_BUNDLED_PLUGINS + sequenceOf("intellij.javaFX.community",
"intellij.vcs.github.community",
"intellij.vcs.gitlab.community")
productLayout.bundledPluginModules = IDEA_BUNDLED_PLUGINS + sequenceOf(
"intellij.javaFX.community",
"intellij.vcs.github.community",
"intellij.vcs.gitlab.community"
)
productLayout.prepareCustomPluginRepositoryForPublishedPlugins = false
productLayout.buildAllCompatiblePlugins = false
@@ -79,6 +77,7 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
baseDownloadUrl = "https://download.jetbrains.com/idea/"
buildDocAuthoringAssets = true
@Suppress("SpellCheckingInspection")
qodanaProductProperties = QodanaProductProperties("QDJVMC", "Qodana Community for JVM")
additionalVmOptions = persistentListOf("-Dllm.show.ai.promotion.window.on.start=false")
enableKotlinPluginK2ByDefault()
@@ -94,15 +93,11 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
sourceDir = context.paths.communityHomeDir.resolve("build/conf/ideaCE/common/bin"),
targetDir = targetDir.resolve("bin"),
)
bundleExternalPlugins(context, targetDir)
}
protected open suspend fun bundleExternalPlugins(context: BuildContext, targetDirectory: Path) {
//temporary unbundle VulnerabilitySearch
//ExternalPluginBundler.bundle('VulnerabilitySearch',
// "$buildContext.paths.communityHome/build/dependencies",
// buildContext, targetDirectory)
}
protected open suspend fun bundleExternalPlugins(context: BuildContext, targetDirectory: Path) { }
override fun createWindowsCustomizer(projectHome: String): WindowsDistributionCustomizer = CommunityWindowsDistributionCustomizer()
override fun createLinuxCustomizer(projectHome: String): LinuxDistributionCustomizer = CommunityLinuxDistributionCustomizer()
@@ -116,13 +111,12 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
fileAssociations = listOf("java", "gradle", "groovy", "kt", "kts", "pom")
}
override fun getFullNameIncludingEdition(appInfo: ApplicationInfoProperties) = "IntelliJ IDEA Community Edition"
override fun getFullNameIncludingEdition(appInfo: ApplicationInfoProperties): String = "IntelliJ IDEA Community Edition"
override fun getFullNameIncludingEditionAndVendor(appInfo: ApplicationInfoProperties) = "IntelliJ IDEA Community Edition"
override fun getFullNameIncludingEditionAndVendor(appInfo: ApplicationInfoProperties): String = "IntelliJ IDEA Community Edition"
override fun getUninstallFeedbackPageUrl(appInfo: ApplicationInfoProperties): String {
return "https://www.jetbrains.com/idea/uninstall/?edition=IC-${appInfo.majorVersion}.${appInfo.minorVersion}"
}
override fun getUninstallFeedbackPageUrl(appInfo: ApplicationInfoProperties): String =
"https://www.jetbrains.com/idea/uninstall/?edition=IC-${appInfo.majorVersion}.${appInfo.minorVersion}"
}
protected open inner class CommunityLinuxDistributionCustomizer : LinuxDistributionCustomizer() {
@@ -135,13 +129,12 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
"Together, powerful static code analysis and ergonomic design make development not only productive but also an enjoyable experience."
}
override fun getRootDirectoryName(appInfo: ApplicationInfoProperties, buildNumber: String) = "idea-IC-$buildNumber"
override fun getRootDirectoryName(appInfo: ApplicationInfoProperties, buildNumber: String): String = "idea-IC-$buildNumber"
override fun generateExecutableFilesPatterns(context: BuildContext, includeRuntime: Boolean, arch: JvmArchitecture): Sequence<String> {
return super.generateExecutableFilesPatterns(context, includeRuntime, arch)
override fun generateExecutableFilesPatterns(context: BuildContext, includeRuntime: Boolean, arch: JvmArchitecture): Sequence<String> =
super.generateExecutableFilesPatterns(context, includeRuntime, arch)
.plus(KotlinBinaries.kotlinCompilerExecutables)
.filterNot { it == "plugins/**/*.sh" }
}
}
protected open inner class CommunityMacDistributionCustomizer : MacDistributionCustomizer() {
@@ -155,27 +148,20 @@ open class IdeaCommunityProperties(private val communityHomeDir: Path) : BaseIde
dmgImagePath = "${communityHomeDir}/build/conf/ideaCE/mac/images/dmg_background.tiff"
}
override fun getRootDirectoryName(appInfo: ApplicationInfoProperties, buildNumber: String): String {
return if (appInfo.isEAP) {
"IntelliJ IDEA ${appInfo.majorVersion}.${appInfo.minorVersionMainPart} CE EAP.app"
}
else {
"IntelliJ IDEA CE.app"
}
}
override fun getRootDirectoryName(appInfo: ApplicationInfoProperties, buildNumber: String): String =
if (appInfo.isEAP) "IntelliJ IDEA ${appInfo.majorVersion}.${appInfo.minorVersionMainPart} CE EAP.app"
else "IntelliJ IDEA CE.app"
override fun generateExecutableFilesPatterns(context: BuildContext, includeRuntime: Boolean, arch: JvmArchitecture): Sequence<String> {
return super.generateExecutableFilesPatterns(context, includeRuntime, arch)
override fun generateExecutableFilesPatterns(context: BuildContext, includeRuntime: Boolean, arch: JvmArchitecture): Sequence<String> =
super.generateExecutableFilesPatterns(context, includeRuntime, arch)
.plus(KotlinBinaries.kotlinCompilerExecutables)
.filterNot { it == "plugins/**/*.sh" }
}
}
override fun getSystemSelector(appInfo: ApplicationInfoProperties, buildNumber: String): String {
return "IdeaIC${appInfo.majorVersion}.${appInfo.minorVersionMainPart}"
}
override fun getSystemSelector(appInfo: ApplicationInfoProperties, buildNumber: String): String =
"IdeaIC${appInfo.majorVersion}.${appInfo.minorVersionMainPart}"
override fun getBaseArtifactName(appInfo: ApplicationInfoProperties, buildNumber: String) = "ideaIC-$buildNumber"
override fun getBaseArtifactName(appInfo: ApplicationInfoProperties, buildNumber: String): String = "ideaIC-$buildNumber"
override fun getOutputDirectoryName(appInfo: ApplicationInfoProperties) = "idea-ce"
override fun getOutputDirectoryName(appInfo: ApplicationInfoProperties): String = "idea-ce"
}

View File

@@ -1,5 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("unused", "ReplaceJavaStaticMethodWithKotlinAnalog")
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:JvmName("DevMainImpl")
package org.jetbrains.intellij.build.devServer
@@ -8,17 +7,16 @@ import com.intellij.util.SystemProperties
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.dev.BuildRequest
import org.jetbrains.intellij.build.dev.buildProductInProcess
import org.jetbrains.intellij.build.dev.getAdditionalPluginMainModules
import org.jetbrains.intellij.build.dev.getIdeSystemProperties
import org.jetbrains.intellij.build.telemetry.withTracer
import java.io.File
import java.nio.file.Path
@Suppress("unused", "IO_FILE_USAGE")
fun buildDevMain(): Collection<Path> {
//TracerProviderManager.setOutput(Path.of(System.getProperty("user.home"), "trace.json"))
@Suppress("TestOnlyProblems")
val ideaProjectRoot = Path.of(PathManager.getHomePathFor(PathManager::class.java)!!)
System.setProperty("idea.dev.project.root", ideaProjectRoot.toString().replace(File.separator, "/"))
System.setProperty("idea.dev.project.root", ideaProjectRoot.toString().replace(java.io.File.separator, "/"))
var homePath: String? = null
var newClassPath: Collection<Path>? = null
@@ -31,7 +29,7 @@ fun buildDevMain(): Collection<Path> {
keepHttpClient = false,
platformClassPathConsumer = { classPath, runDir ->
newClassPath = classPath
homePath = runDir.toString().replace(File.separator, "/")
homePath = runDir.toString().replace(java.io.File.separator, "/")
@Suppress("SpellCheckingInspection")
val exceptions = setOf("jna.boot.library.path", "pty4j.preferred.native.folder", "jna.nosys", "jna.noclasspath", "jb.vmOptionsFile")
@@ -51,4 +49,7 @@ fun buildDevMain(): Collection<Path> {
System.setProperty(PathManager.PROPERTY_HOME_PATH, it)
}
return newClassPath!!
}
}
private fun getAdditionalPluginMainModules(): List<String> =
System.getProperty("additional.modules")?.splitToSequence(',')?.map { it.trim() }?.filter { it.isNotEmpty() }?.toList() ?: emptyList()

View File

@@ -1,9 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceJavaStaticMethodWithKotlinAnalog")
// 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.util.containers.withAll
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.plus
@@ -11,26 +8,6 @@ import org.jetbrains.intellij.build.impl.PlatformJarNames.TEST_FRAMEWORK_JAR
import org.jetbrains.intellij.build.impl.PlatformLayout
import org.jetbrains.intellij.build.kotlin.KotlinPluginBuilder
private val BASE_CLASS_VERSIONS: Map<String, String> = java.util.Map.copyOf(hashMapOf(
"" to "17",
"lib/idea_rt.jar" to "1.7",
"lib/forms_rt.jar" to "1.8",
"lib/annotations.jar" to "1.8",
"lib/util_rt.jar" to "1.7",
"lib/util-8.jar" to "1.8",
"lib/external-system-rt.jar" to "1.8",
"plugins/java-coverage/lib/java-coverage-rt.jar" to "1.8",
"plugins/junit/lib/junit-rt.jar" to "1.7",
"plugins/junit/lib/junit5-rt.jar" to "1.8",
"plugins/gradle/lib/gradle-tooling-extension-api.jar" to "1.8",
"plugins/gradle/lib/gradle-tooling-extension-impl.jar" to "1.8",
"plugins/maven-server/lib/maven-server.jar" to "1.8",
"plugins/maven-model/lib/maven-model.jar" to "1.8",
"plugins/maven/lib/maven3-server-common.jar" to "1.8",
"plugins/maven/lib/maven3-server.jar" to "1.8",
"plugins/maven/lib/artifact-resolver-m31.jar" to "1.8",
))
/**
* Default bundled plugins for all editions of IntelliJ IDEA.
* See also [DEFAULT_BUNDLED_PLUGINS].
@@ -94,13 +71,30 @@ val IDEA_BUNDLED_PLUGINS: PersistentList<String> = DEFAULT_BUNDLED_PLUGINS + seq
"intellij.turboComplete",
)
val CE_CLASS_VERSIONS: Map<String, String> = BASE_CLASS_VERSIONS.withAll(hashMapOf(
val CE_CLASS_VERSIONS: Map<String, String> = mapOf(
"" to "17",
"lib/idea_rt.jar" to "1.7",
"lib/forms_rt.jar" to "1.8",
"lib/annotations.jar" to "1.8",
"lib/util_rt.jar" to "1.7",
"lib/util-8.jar" to "1.8",
"lib/external-system-rt.jar" to "1.8",
"plugins/java-coverage/lib/java-coverage-rt.jar" to "1.8",
"plugins/junit/lib/junit-rt.jar" to "1.7",
"plugins/junit/lib/junit5-rt.jar" to "1.8",
"plugins/gradle/lib/gradle-tooling-extension-api.jar" to "1.8",
"plugins/gradle/lib/gradle-tooling-extension-impl.jar" to "1.8",
"plugins/maven-server/lib/maven-server.jar" to "1.8",
"plugins/maven-model/lib/maven-model.jar" to "1.8",
"plugins/maven/lib/maven3-server-common.jar" to "1.8",
"plugins/maven/lib/maven3-server.jar" to "1.8",
"plugins/maven/lib/artifact-resolver-m31.jar" to "1.8",
"plugins/java/lib/jshell-frontend.jar" to "9",
"plugins/java/lib/sa-jdwp" to "", // ignored
"plugins/java/lib/rt/debugger-agent.jar" to "1.7",
"plugins/Groovy/lib/groovy-rt.jar" to "1.7",
"plugins/Groovy/lib/groovy-constants-rt.jar" to "1.7",
))
)
val TEST_FRAMEWORK_LAYOUT_CUSTOMIZER: (PlatformLayout, BuildContext) -> Unit = { layout, _ ->
for (name in listOf(
@@ -159,7 +153,7 @@ abstract class BaseIdeaProperties : JetBrainsProductProperties() {
// TODO should be used as regular project library when the issue will be fixed at the Gradle tooling api side https://github.com/gradle/gradle/issues/8431 and the patched class will be removed
layout.withoutProjectLibrary("Gradle")
// this library is placed into subdirectory of the 'lib' directory in Android plugin layout, so we need to exclude it from the platform layout explicitly
// this library is placed into a subdirectory of the 'lib' directory in the Android plugin layout, so we need to exclude it from the platform layout explicitly
layout.withoutProjectLibrary("layoutlib")
layout.withoutProjectLibrary("jetbrains.qodana.cloud.kotlin.client")

View File

@@ -222,7 +222,7 @@ sealed interface DistFileContent {
}
data class LocalDistFileContent(@JvmField val file: Path, val isExecutable: Boolean = false) : DistFileContent {
override fun readAsStringForDebug() = Files.newInputStream(file).readNBytes(1024).decodeToString()
override fun readAsStringForDebug(): String = Files.newInputStream(file).readNBytes(1024).decodeToString()
override fun toString(): String = "LocalDistFileContent(file=$file, isExecutable=$isExecutable)"
}

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 org.jetbrains.annotations.ApiStatus
@@ -46,12 +46,12 @@ interface CompilationContext {
fun findModule(name: String): JpsModule?
/**
* Could be directory or jar file
* A directory or a .jar file.
*/
suspend fun getModuleOutputDir(module: JpsModule, forTests: Boolean = false): Path
/**
* Could be directory or jar file
* A directory or a .jar file.
*/
suspend fun getModuleTestsOutputDir(module: JpsModule): Path
@@ -102,4 +102,3 @@ interface CompilationTasks {
suspend fun generateRuntimeModuleRepository()
}

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.ijent.community.buildConstants.IJENT_BOOT_CLASSPATH_MODULE
@@ -17,7 +17,7 @@ import java.nio.file.Path
import java.util.function.BiPredicate
/**
* Describes distribution of an in-house IntelliJ-based IDE hosted in IntelliJ repository.
* Describes a distribution of an IntelliJ-based IDE hosted in the IntelliJ repository.
*/
abstract class JetBrainsProductProperties : ProductProperties() {
init {
@@ -29,18 +29,14 @@ abstract class JetBrainsProductProperties : ProductProperties() {
productLayout.addPlatformSpec { layout, _ -> layout.withModule(IJENT_BOOT_CLASSPATH_MODULE, PLATFORM_CORE_NIO_FS) }
}
protected fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean {
return module.contentRootsList.urls.all { url ->
protected fun isCommunityModule(module: JpsModule, context: BuildContext): Boolean =
module.contentRootsList.urls.all { url ->
Path.of(JpsPathUtil.urlToPath(url)).startsWith(context.paths.communityHomeDir)
}
}
override suspend fun copyAdditionalFiles(context: BuildContext, targetDir: Path) {
}
override suspend fun copyAdditionalFiles(context: BuildContext, targetDir: Path) { }
private class InvalidPluginDescriptorError(message: String) : InvalidDescriptorProblem(
detailedMessage = message, descriptorPath = "",
) {
private class InvalidPluginDescriptorError(message: String) : InvalidDescriptorProblem(detailedMessage = message, descriptorPath = "") {
override val level = Level.ERROR
}
@@ -92,9 +88,7 @@ abstract class JetBrainsProductProperties : ProductProperties() {
}
/**
* 🌲
* see KTIJ-30761
* @see org.jetbrains.intellij.build.sharedIndexes.PreSharedIndexesGenerator
* See KTIJ-30761, `org.jetbrains.intellij.build.sharedIndexes.PreSharedIndexesGenerator`.
*/
protected fun enableKotlinPluginK2ByDefault() {
additionalVmOptions += "-Didea.kotlin.plugin.use.k2=true"

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.CustomProperty
@@ -26,10 +26,10 @@ import java.util.function.BiPredicate
abstract class ProductProperties {
/**
* The base name (i.e., a name without the extension and architecture suffix)
* of launcher files (bin/xxx64.exe, bin/xxx.bat, bin/xxx.sh, MacOS/xxx),
* of launcher files ('bin/xxx64.exe', 'bin/xxx.bat', 'bin/xxx.sh', 'Contents/MacOS/xxx'),
* usually a short product name in lower case (`"idea"` for IntelliJ IDEA, `"webstorm"` for WebStorm, etc.).
*
* **Important:** please make sure that this property and the `//names@script` attribute in the product's `*ApplicationInfo.xml` file
* **Important**: please make sure that this property and the `//names@script` attribute in the product's `*ApplicationInfo.xml` file
* have the same value.
*/
abstract val baseFileName: String
@@ -65,7 +65,7 @@ abstract class ProductProperties {
var fastInstanceActivation: Boolean = true
/**
* An entry point into application's Java code, usually [com.intellij.idea.Main].
* An entry point into application's Java code, usually `com.intellij.idea.Main`.
* Use [BuildContext.ideMainClassName] if you need to access this value in the build scripts.
*/
var mainClassName: String = "com.intellij.idea.Main"
@@ -79,8 +79,8 @@ abstract class ProductProperties {
/**
* Name of the command which runs IDE in 'offline inspections' mode
* (returned by [com.intellij.openapi.application.ApplicationStarter.getCommandName]).
* This property will be also used to name sh/bat scripts which execute this command.
* (as returned by `com.intellij.openapi.application.ApplicationStarter.getCommandName`).
* This property will be also used to name .sh/.bat scripts which execute the command.
*/
var inspectCommandName: String = "inspect"
@@ -108,18 +108,21 @@ abstract class ProductProperties {
var enableCds: Boolean = false
/**
* Additional arguments which will be added to JVM command line in IDE launchers for all operating systems.
*/
var additionalIdeJvmArguments: MutableList<String> = mutableListOf()
/**
* Additional arguments which will be added to VM options for all operating systems.
* Difference between this property and [org.jetbrains.intellij.build.ProductProperties.additionalIdeJvmArguments] is this one could be
* used to put options to `*.vmoptions` file while arguments from [org.jetbrains.intellij.build.ProductProperties.additionalIdeJvmArguments]
* are used in command line in `*.sh` scripts or similar
* Additional options to add to a `*.vmoptions` file for all operating systems.
* A user might override these options.
*
* See also [additionalIdeJvmArguments].
*/
var additionalVmOptions: PersistentList<String> = persistentListOf()
/**
* Additional options to append to the JVM command line by IDE launchers for all operating systems.
* These options have the highest precedence and cannot be overridden by a user.
*
* See also [additionalVmOptions].
*/
var additionalIdeJvmArguments: MutableList<String> = mutableListOf()
/**
* The specified options will be used instead of/in addition to the default JVM memory options for all operating systems.
*/
@@ -139,13 +142,13 @@ abstract class ProductProperties {
var reassignAltClickToMultipleCarets: Boolean = false
/**
* Now file containing information about third-party libraries is bundled and shown inside the IDE.
* Now a file containing information about third-party libraries is bundled and shown inside the IDE.
* If `true`, HTML & JSON files of third-party libraries will be placed alongside built artifacts.
*/
var generateLibraryLicensesTable: Boolean = true
/**
* List of licenses information about all libraries which can be used in the product modules.
* List of licenses information about all libraries that can be used in the product modules.
*/
var allLibraryLicenses: List<LibraryLicense> = CommunityLibraryLicenses.LICENSES_LIST
@@ -171,13 +174,14 @@ abstract class ProductProperties {
* It's used by the build scripts for the following:
* * to inject URL of *.manifest file produced by [RepairUtilityBuilder][org.jetbrains.intellij.build.impl.support.RepairUtilityBuilder] and
* in `repair` executable;
* * to specify URL of distributions in [SBOM][SoftwareBillOfMaterials] files.
* * to specify URL of distributions in [SoftwareBillOfMaterials] files.
*/
var baseDownloadUrl: String? = null
/**
* See [SoftwareBillOfMaterials]
*/
@Suppress("SpellCheckingInspection")
val sbomOptions: SoftwareBillOfMaterials.Options = SoftwareBillOfMaterials.Options()
/**
@@ -197,13 +201,13 @@ abstract class ProductProperties {
var embeddedFrontendRootModule: String? = null
/**
* Specifies a factory function for an instance which will be used to generate launchers for the embedded frontend variant (JetBrains Client).
* Specifies a factory function which will be used to generate launchers for the embedded frontend variant (JetBrains Client).
*/
@ApiStatus.Experimental
var embeddedFrontendProperties: (() -> ProductProperties)? = null
/**
* Set to the root product module (the one containing product-modules.xml file) to enable using module-based loader for the product.
* Set to the root product module (the one containing the "product-modules.xml" file) to enable using a module-based loader for the product.
* [BuildOptions.useModularLoader] will be used to determine whether the produced distribution will actually use this way.
*/
@ApiStatus.Experimental
@@ -214,6 +218,7 @@ abstract class ProductProperties {
* [the modular loader][com.intellij.platform.bootstrap.ModuleBasedProductLoadingStrategy].
* This property makes sense only if [rootModuleForModularLoader] is set to a non-null value.
*/
@Suppress("KDocUnresolvedReference")
@ApiStatus.Experimental
var productMode: ProductMode = ProductMode.MONOLITH
@@ -227,7 +232,7 @@ abstract class ProductProperties {
/**
* A config map for [org.jetbrains.intellij.build.impl.ClassFileChecker], when .class file version verification is necessary.
*/
var versionCheckerConfig: Map<String, String> = java.util.Map.of()
var versionCheckerConfig: Map<String, String> = mapOf()
/**
* Strings which are forbidden as a part of the resulting class file path. E.g.:
@@ -236,18 +241,19 @@ abstract class ProductProperties {
var forbiddenClassFileSubPaths: PersistentList<String> = persistentListOf()
/**
* Exceptions from forbiddenClassFileSubPaths. Must contain full string with the offending class, including the jar path. E.g.:
* "plugins/sample/lib/sample.jar!/com/sample/license/ThirdPartyLicensesDialog.class"
* Exceptions from [forbiddenClassFileSubPaths].
* Must contain a full string with the offending class, including the .jar path.
* E.g., "plugins/sample/lib/sample.jar!/com/sample/license/ThirdPartyLicensesDialog.class".
*/
var forbiddenClassFileSubPathExceptions: PersistentList<String> = persistentListOf()
/**
* Paths to properties files the content of which should be appended to idea.properties file.
* Paths to files, the content of which should be appended to the 'idea.properties' file.
*/
var additionalIDEPropertiesFilePaths: List<Path> = emptyList()
/**
* Paths to directories the content of which should be added to 'license' directory of IDE distribution.
* Paths to directories, the content of which should be added to the 'license' directory of IDE distribution.
*/
var additionalDirectoriesWithLicenses: List<Path> = emptyList()
@@ -257,7 +263,7 @@ abstract class ProductProperties {
abstract fun getBaseArtifactName(appInfo: ApplicationInfoProperties, buildNumber: String): String
/**
* `<productName>-<buildNumber>` for any (nightly, EAP or release) build, e.g. ideaIC-232.9999
* `<productName>-<buildNumber>` for any (nightly, EAP, or release) build, e.g., 'ideaIC-232.9999'
*
* See [getBaseArtifactName].
*/
@@ -312,7 +318,7 @@ abstract class ProductProperties {
* to allow users to customize location of the product runtime (`<PRODUCT>_JDK` variable),
* *.vmoptions file (`<PRODUCT>_VM_OPTIONS`), `idea.properties` file (`<PRODUCT>_PROPERTIES`).
*/
open fun getEnvironmentVariableBaseName(appInfo: ApplicationInfoProperties) = appInfo.launcherName.uppercase().replace('-', '_')
open fun getEnvironmentVariableBaseName(appInfo: ApplicationInfoProperties): String = appInfo.launcherName.uppercase().replace('-', '_')
/**
* Override this method to copy additional files to distributions of all operating systems.
@@ -324,7 +330,7 @@ abstract class ProductProperties {
* @return the name of a subdirectory under `projectHome/out` where build artifacts will be placed,
* must be unique among all products built from the same sources.
*/
open fun getOutputDirectoryName(appInfo: ApplicationInfoProperties) = appInfo.fullProductName.lowercase(Locale.ROOT)
open fun getOutputDirectoryName(appInfo: ApplicationInfoProperties): String = appInfo.fullProductName.lowercase(Locale.ROOT)
/**
* Paths to externally built plugins to be included in the IDE.
@@ -341,6 +347,7 @@ abstract class ProductProperties {
/**
* @return custom properties for [org.jetbrains.intellij.build.impl.productInfo.ProductInfoData].
*/
@Suppress("KDocUnresolvedReference")
open fun generateCustomPropertiesForProductInfo(): List<CustomProperty> = emptyList()
/**
@@ -358,7 +365,7 @@ abstract class ProductProperties {
open fun validateLayout(platformLayout: PlatformLayout, context: BuildContext) {}
/**
* Copies additional localization resources to the plugin generated localization resources directory.
* Copies additional localization resources to the plugin-generated localization resources directory.
*/
@ApiStatus.Internal
open suspend fun copyAdditionalLocalizationResourcesToPlugin(context: BuildContext, lang: String, targetDir: Path) {}
@@ -384,7 +391,7 @@ abstract class ProductProperties {
/**
* When set to true, invokes keymap and inspections description generators during build.
* These generators produce artifacts utilized by documentation authoring tools and builds.
* These generators produce artifacts used by documentation-authoring tools and builds.
*/
var buildDocAuthoringAssets: Boolean = false
@@ -393,6 +400,7 @@ abstract class ProductProperties {
* todo: remove me when platform is ready
*/
@Deprecated("Do not use it. Needed only for JetBrains Client per-ide customisation + it's temporary")
@Suppress("DEPRECATION")
open fun applicationInfoOverride(project: JpsProject): ApplicationInfoOverrides? = null
@Deprecated("Do not use it. Needed only for JetBrains Client per-ide customisation + it's temporary")

View File

@@ -1,6 +1,4 @@
// 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
@@ -51,7 +49,7 @@ private val sourceToNames: Map<String, MutableList<String>> by lazy {
}
suspend fun reorderJar(relativePath: String, file: Path) {
val orderedNames = sourceToNames.get(relativePath) ?: return
val orderedNames = sourceToNames[relativePath] ?: return
spanBuilder("reorder jar")
.setAttribute("relativePath", relativePath)
.setAttribute("file", file.toString())
@@ -186,8 +184,9 @@ internal fun writePluginClassPathHeader(out: DataOutputStream, isJarOnly: Boolea
out.write(if (isJarOnly) 1 else 0)
// main plugin
val mainDescriptor = moduleOutputPatcher.getPatchedContent(context.productProperties.applicationInfoModule)
.let { it.get("META-INF/plugin.xml") ?: it.get("META-INF/${context.productProperties.platformPrefix}Plugin.xml") }
val mainDescriptor = moduleOutputPatcher.getPatchedContent(context.productProperties.applicationInfoModule).let {
it["META-INF/plugin.xml"] ?: it["META-INF/${context.productProperties.platformPrefix}Plugin.xml"]
}
val mainPluginDescriptorContent = requireNotNull(mainDescriptor) {
"Cannot find core plugin descriptor (module=${context.productProperties.applicationInfoModule})"
@@ -278,7 +277,7 @@ internal fun generatePluginClassPathFromPrebuiltPluginFiles(pluginEntries: List<
putMoreLikelyPluginJarsFirst(pluginDir.fileName.toString(), filesInLibUnderPluginDir = files)
}
// move dir with plugin.xml to top (it may not exist if for some reason the main module dir still being packed into JAR)
// move a dir with "plugin.xml" to the top (it may not exist if for some reason the main module dir still being packed into JAR)
writeEntry(out = out, files = files, pluginDir = pluginDir, pluginDescriptorContent = reorderPluginClassPath(files))
}

View File

@@ -123,19 +123,13 @@ private fun createConfiguration(productionClassOutput: Path, homePath: Path): Co
return Json.decodeFromString(Configuration.serializer(), Files.readString(projectPropertiesPath))
}
internal fun getProductPropertiesPath(homePath: Path): Path {
internal fun getProductPropertiesPath(homePath: Path): Path =
// handle a custom product properties path
val customPath = System.getProperty(CUSTOM_PRODUCT_PROPERTIES_PATH)?.let { homePath.resolve(it) }
if (customPath != null && Files.exists(customPath)) {
return customPath
}
return homePath.resolve(PRODUCTS_PROPERTIES_PATH)
}
System.getProperty(CUSTOM_PRODUCT_PROPERTIES_PATH)?.let { homePath.resolve(it) }?.takeIf { Files.exists(it) }
?: homePath.resolve(PRODUCTS_PROPERTIES_PATH)
private fun getProductConfiguration(configuration: Configuration, platformPrefix: String): ProductConfiguration {
return configuration.products.get(platformPrefix) ?: throw ConfigurationException(
"No production configuration for platform prefix `$platformPrefix` please add to `$PRODUCTS_PROPERTIES_PATH` if needed"
)
}
private fun getProductConfiguration(configuration: Configuration, platformPrefix: String): ProductConfiguration =
configuration.products[platformPrefix]
?: throw ConfigurationException("No production configuration for platform prefix `${platformPrefix}`; please add to `${PRODUCTS_PROPERTIES_PATH}` if needed")
internal class ConfigurationException(message: String) : RuntimeException(message)

View File

@@ -1,6 +1,4 @@
// 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
import com.dynatrace.hash4j.hashing.HashFunnel
@@ -58,8 +56,8 @@ 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,
* and its generation makes build a little longer, so it should be enabled only if needed.
* It is currently used only to run an instance of JetBrains Client from IDE's installation,
* and its generation makes the project build a little longer, so it should be enabled only if needed.
*/
@JvmField val generateRuntimeModuleRepository: Boolean = false,
@@ -84,7 +82,7 @@ data class BuildRequest(
internal suspend fun buildProduct(request: BuildRequest, createProductProperties: suspend (CompilationContext) -> ProductProperties): Path {
val rootDir = withContext(Dispatchers.IO) {
val rootDir = request.devRootDir
// if symlinked to ram disk, use a real path for performance reasons and avoid any issues in ant/other code
// if symlinked to RAM disk, use a real path for performance reasons and avoid any issues in ant/other code
if (Files.exists(rootDir)) {
// toRealPath must be called only on an existing file
rootDir.toRealPath()
@@ -121,14 +119,14 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
}
val runDir = buildDir
val context = createBuildContext(createProductProperties = createProductProperties, request = request, runDir = runDir, jarCacheDir = request.jarCacheDir, buildDir = buildDir)
val context = createBuildContext(createProductProperties, request, runDir, request.jarCacheDir, buildDir)
compileIfNeeded(context)
coroutineScope {
val moduleOutputPatcher = ModuleOutputPatcher()
val platformLayout = async(CoroutineName("create platform layout")) {
createPlatformLayout(context = context)
createPlatformLayout(context)
}
val searchableOptionSet = getSearchableOptionSet(context)
@@ -139,7 +137,7 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
val binDir = Files.createDirectories(runDir.resolve("bin"))
val oldFiles = Files.newDirectoryStream(binDir).use { it.toCollection(HashSet()) }
val osDistributionBuilder = getOsDistributionBuilder(os = OsFamily.currentOs, context = context)
val osDistributionBuilder = getOsDistributionBuilder(OsFamily.currentOs, 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`
@@ -153,21 +151,13 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
Files.writeString(ideaPropertyFile, createIdeaPropertyFile(context))
oldFiles.remove(ideaPropertyFile)
if (oldFiles.isNotEmpty()) {
for (oldFile in oldFiles) {
NioFiles.deleteRecursively(oldFile)
}
for (oldFile in oldFiles) {
NioFiles.deleteRecursively(oldFile)
}
}
val (platformDistributionEntries, classPath) = spanBuilder("layout platform").use {
layoutPlatform(
runDir = runDir,
platformLayout = platformLayout.await(),
searchableOptionSet = searchableOptionSet,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
layoutPlatform(runDir, platformLayout.await(), searchableOptionSet, context, moduleOutputPatcher)
}
if (request.writeCoreClasspath) {
@@ -193,29 +183,20 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
}
val pluginDistributionEntriesDeferred = async(CoroutineName("build plugins")) {
buildPlugins(
request = request,
runDir = runDir,
platformLayout = platformLayout,
artifactTask = artifactTask,
searchableOptionSet = searchableOptionSet,
buildPlatformJob = platformDistributionEntriesDeferred,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
buildPlugins(request, context, runDir, platformLayout, artifactTask, searchableOptionSet, platformDistributionEntriesDeferred, moduleOutputPatcher)
}
launch {
val (pluginEntries, additionalEntries) = pluginDistributionEntriesDeferred.await()
spanBuilder("generate plugin classpath").use(Dispatchers.IO) {
val mainData = generatePluginClassPath(pluginEntries = pluginEntries, moduleOutputPatcher = moduleOutputPatcher)
val mainData = generatePluginClassPath(pluginEntries, moduleOutputPatcher)
val additionalData = additionalEntries?.let { generatePluginClassPathFromPrebuiltPluginFiles(it) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = pluginEntries.size + (additionalEntries?.size ?: 0)
platformDistributionEntriesDeferred.join()
writePluginClassPathHeader(out = out, isJarOnly = !request.isUnpackedDist, pluginCount = pluginCount, moduleOutputPatcher = moduleOutputPatcher, context = context)
writePluginClassPathHeader(out, isJarOnly = !request.isUnpackedDist, pluginCount, moduleOutputPatcher, context)
out.write(mainData)
additionalData?.let { out.write(it) }
out.close()
@@ -227,41 +208,33 @@ internal suspend fun buildProduct(request: BuildRequest, createProductProperties
launch {
val allDistributionEntries = platformDistributionEntriesDeferred.await().asSequence() + pluginDistributionEntriesDeferred.await().first.asSequence().flatMap { it.second }
spanBuilder("generate runtime repository").use(Dispatchers.IO) {
generateRuntimeModuleRepositoryForDevBuild(entries = allDistributionEntries, targetDirectory = runDir, context = context)
generateRuntimeModuleRepositoryForDevBuild(allDistributionEntries, runDir, context)
}
}
}
launch {
computeIdeFingerprint(
platformDistributionEntriesDeferred = platformDistributionEntriesDeferred,
pluginDistributionEntriesDeferred = pluginDistributionEntriesDeferred,
runDir = runDir,
homePath = request.projectDir,
)
computeIdeFingerprint(platformDistributionEntriesDeferred, pluginDistributionEntriesDeferred, runDir, request.projectDir)
}
launch(Dispatchers.IO) {
platformDistributionEntriesDeferred.await() // ensure platform dist files added to the list
pluginDistributionEntriesDeferred.await() // ensure plugins dist files added to the list
copyDistFiles(context = context, newDir = runDir, os = OsFamily.currentOs, arch = JvmArchitecture.currentJvmArch)
copyDistFiles(context, runDir, OsFamily.currentOs, JvmArchitecture.currentJvmArch)
}
}.invokeOnCompletion {
// close debug logging to prevent locking of the output directory on Windows
context.messages.close()
}
.invokeOnCompletion {
// close debug logging to prevent locking of output directory on Windows
context.messages.close()
}
return runDir
}
private suspend fun getSearchableOptionSet(context: BuildContext): SearchableOptionSetDescriptor? {
return withContext(Dispatchers.IO) {
try {
readSearchableOptionIndex(context.paths.searchableOptionDir)
}
catch (_: NoSuchFileException) {
null
}
private suspend fun getSearchableOptionSet(context: BuildContext): SearchableOptionSetDescriptor? = withContext(Dispatchers.IO) {
try {
readSearchableOptionIndex(context.paths.searchableOptionDir)
}
catch (_: NoSuchFileException) {
null
}
}
@@ -402,7 +375,7 @@ private suspend fun buildPlugins(
val pluginRootDir = runDir.resolve("plugins")
val plugins = getPluginLayoutsByJpsModuleNames(bundledMainModuleNames, context.productProperties.productLayout)
.filter { isPluginApplicable(bundledMainModuleNames = bundledMainModuleNames, plugin = it, context = context) }
.filter { isPluginApplicable(bundledMainModuleNames, plugin = it, context) }
withContext(Dispatchers.IO) {
Files.createDirectories(pluginRootDir)
@@ -410,15 +383,7 @@ private suspend fun buildPlugins(
artifactTask.join()
val pluginEntries = buildPlugins(
plugins = plugins,
platformLayout = platformLayout.await(),
searchableOptionSet = searchableOptionSet,
pluginRootDir = pluginRootDir,
buildPlatformJob = buildPlatformJob,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
val pluginEntries = buildPlugins(plugins, platformLayout.await(), searchableOptionSet, context, pluginRootDir, buildPlatformJob, moduleOutputPatcher)
val additionalPlugins = copyAdditionalPlugins(context, pluginRootDir)
return pluginEntries to additionalPlugins
}
@@ -448,7 +413,7 @@ private suspend fun createBuildContext(
spanBuilder("create build context").use {
// 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 options = BuildOptions(
jarCacheDir = jarCacheDir,
jarCacheDir,
buildDateInSeconds = getBuildDateInSeconds(),
printFreeSpace = false,
validateImplicitPlatformModule = false,
@@ -503,8 +468,7 @@ private suspend fun createBuildContext(
projectHome = request.projectDir,
buildOutputRootEvaluator = { _ -> runDir },
setupTracer = false,
// will be enabled later in [com.intellij.platform.ide.bootstrap.enableJstack] instead
enableCoroutinesDump = false,
enableCoroutinesDump = false, // will be enabled later in [com.intellij.platform.ide.bootstrap.enableJstack] instead
options = options,
customBuildPaths = result,
)
@@ -519,7 +483,6 @@ private suspend fun createBuildContext(
val compilationContext = compilationContextDeferred.await()
// ~1 second
val productProperties = async(CoroutineName("create product properties")) {
createProductProperties(compilationContext)
}
@@ -566,8 +529,8 @@ private fun isPluginApplicable(bundledMainModuleNames: Set<String>, plugin: Plug
return true
}
return satisfiesBundlingRequirements(plugin = plugin, osFamily = OsFamily.currentOs, arch = JvmArchitecture.currentJvmArch, context = context) ||
satisfiesBundlingRequirements(plugin = plugin, osFamily = null, arch = JvmArchitecture.currentJvmArch, context = context)
return satisfiesBundlingRequirements(plugin, OsFamily.currentOs, JvmArchitecture.currentJvmArch, context) ||
satisfiesBundlingRequirements(plugin, osFamily = null, JvmArchitecture.currentJvmArch, context)
}
internal suspend fun createProductProperties(productConfiguration: ProductConfiguration, compilationContext: CompilationContext, request: BuildRequest): ProductProperties {
@@ -618,14 +581,7 @@ private suspend fun layoutPlatform(
context: BuildContext,
moduleOutputPatcher: ModuleOutputPatcher,
): Pair<List<DistributionFileEntry>, Set<Path>> {
val entries = layoutPlatformDistribution(
moduleOutputPatcher = moduleOutputPatcher,
targetDirectory = runDir,
platform = platformLayout,
searchableOptionSet = searchableOptionSet,
copyFiles = true,
context = context,
)
val entries = layoutPlatformDistribution(moduleOutputPatcher, runDir, platformLayout, searchableOptionSet, copyFiles = true, context)
lateinit var sortedClassPath: Set<Path>
coroutineScope {
launch {
@@ -674,8 +630,5 @@ private fun computeAdditionalModulesFingerprint(additionalModules: List<String>)
}
}
private fun getCommunityHomePath(homePath: Path): Path = if (Files.isDirectory(homePath.resolve("community"))) homePath.resolve("community") else homePath
fun getAdditionalPluginMainModules(): List<String> {
return System.getProperty("additional.modules")?.splitToSequence(',')?.map { it.trim() }?.filter { it.isNotEmpty() }?.toList() ?: emptyList()
}
private fun getCommunityHomePath(homePath: Path): Path =
if (Files.isDirectory(homePath.resolve("community"))) homePath.resolve("community") else homePath

View File

@@ -1,6 +1,4 @@
// 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
@@ -43,9 +41,7 @@ class BuildContextImpl internal constructor(
override val macDistributionCustomizer: MacDistributionCustomizer?,
override val proprietaryBuildTools: ProprietaryBuildTools,
override val applicationInfo: ApplicationInfoProperties = ApplicationInfoPropertiesImpl(
project = compilationContext.project,
productProperties = productProperties,
buildOptions = compilationContext.options,
compilationContext.project, productProperties, compilationContext.options
),
@JvmField internal val jarCacheManager: JarCacheManager,
) : BuildContext, CompilationContext by compilationContext {
@@ -84,7 +80,7 @@ class BuildContextImpl internal constructor(
jarCacheManager.cleanup()
}
override var bootClassPathJarNames: List<String> = java.util.List.of(PLATFORM_LOADER_JAR)
override var bootClassPathJarNames: List<String> = listOf(PLATFORM_LOADER_JAR)
override val ideMainClassName: String
get() = if (useModularLoader) "com.intellij.platform.runtime.loader.IntellijLoader" else productProperties.mainClassName
@@ -118,9 +114,8 @@ class BuildContextImpl internal constructor(
options.buildStepsToSkip += productProperties.incompatibleBuildSteps
if (!options.buildStepsToSkip.isEmpty()) {
Span.current().addEvent(
"build steps to be skipped", Attributes.of(
AttributeKey.stringArrayKey("stepsToSkip"), java.util.List.copyOf(options.buildStepsToSkip)
)
"build steps to be skipped",
Attributes.of(AttributeKey.stringArrayKey("stepsToSkip"), java.util.List.copyOf(options.buildStepsToSkip))
)
}
if (!options.compatiblePluginsToIgnore.isEmpty()) {
@@ -141,12 +136,9 @@ class BuildContextImpl internal constructor(
options: BuildOptions = BuildOptions(),
): BuildContext {
val compilationContext = CompilationContextImpl.createCompilationContext(
projectHome = projectHome,
setupTracer = setupTracer,
buildOutputRootEvaluator = createBuildOutputRootEvaluator(projectHome = projectHome, productProperties = productProperties, buildOptions = options),
options = options,
projectHome, createBuildOutputRootEvaluator(projectHome, productProperties, options), options, setupTracer
)
return createContext(compilationContext = compilationContext, projectHome = projectHome, productProperties = productProperties, proprietaryBuildTools = proprietaryBuildTools)
return createContext(compilationContext, projectHome, productProperties, proprietaryBuildTools)
}
fun createContext(
@@ -278,11 +270,7 @@ class BuildContextImpl internal constructor(
options = options,
paths = computeBuildPaths(
options = options,
buildOut = options.outRootDir ?: createBuildOutputRootEvaluator(
projectHome = paths.projectHome,
productProperties = productProperties,
buildOptions = options,
)(project),
buildOut = options.outRootDir ?: createBuildOutputRootEvaluator(paths.projectHome, productProperties, options)(project),
projectHome = paths.projectHome,
artifactDir = if (prepareForBuild) {
@Suppress("DEPRECATION")
@@ -309,7 +297,7 @@ class BuildContextImpl internal constructor(
return copy
}
override suspend fun includeBreakGenLibraries() = getBundledPluginModules().contains(JavaPluginLayout.MAIN_MODULE_NAME)
override suspend fun includeBreakGenLibraries(): Boolean = getBundledPluginModules().contains(JavaPluginLayout.MAIN_MODULE_NAME)
override fun patchInspectScript(path: Path) {
//todo use placeholder in inspect.sh/inspect.bat file instead
@@ -335,35 +323,35 @@ class BuildContextImpl internal constructor(
if (productProperties.enableCds) {
val cacheDir = if (os == OsFamily.WINDOWS) "%IDE_CACHE_DIR%\\" else "\$IDE_CACHE_DIR/"
jvmArgs.add("-XX:SharedArchiveFile=${cacheDir}${productProperties.baseFileName}${buildNumber}.jsa")
jvmArgs.add("-XX:+AutoCreateSharedArchive")
jvmArgs += "-XX:SharedArchiveFile=${cacheDir}${productProperties.baseFileName}${buildNumber}.jsa"
jvmArgs += "-XX:+AutoCreateSharedArchive"
}
else {
productProperties.classLoader?.let {
jvmArgs.add("-Djava.system.class.loader=${it}")
jvmArgs += "-Djava.system.class.loader=${it}"
}
}
jvmArgs.add("-Didea.vendor.name=${applicationInfo.shortCompanyName}")
jvmArgs.add("-Didea.paths.selector=${systemSelector}")
jvmArgs += "-Didea.vendor.name=${applicationInfo.shortCompanyName}"
jvmArgs += "-Didea.paths.selector=${systemSelector}"
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
jvmArgs.add("-Djna.nosys=true")
jvmArgs.add("-Djna.noclasspath=true")
jvmArgs.add("-Dio.netty.allocator.type=pooled")
jvmArgs += "-Djna.boot.library.path=${macroName}/lib/jna/${arch.dirName}".let { if (isScript) '"' + it + '"' else it }
jvmArgs += "-Djna.nosys=true"
jvmArgs += "-Djna.noclasspath=true"
jvmArgs += "-Dpty4j.preferred.native.folder=${macroName}/lib/pty4j".let { if (isScript) '"' + it + '"' else it }
jvmArgs += "-Dio.netty.allocator.type=pooled"
if (useModularLoader || generateRuntimeModuleRepository) {
jvmArgs.add("-Dintellij.platform.runtime.repository.path=${macroName}/${MODULE_DESCRIPTORS_JAR_PATH}".let { if (isScript) '"' + it + '"' else it })
jvmArgs += "-Dintellij.platform.runtime.repository.path=${macroName}/${MODULE_DESCRIPTORS_JAR_PATH}".let { if (isScript) '"' + it + '"' else it }
}
if (useModularLoader) {
jvmArgs.add("-Dintellij.platform.root.module=${productProperties.rootModuleForModularLoader!!}")
jvmArgs.add("-Dintellij.platform.product.mode=${productProperties.productMode.id}")
jvmArgs += "-Dintellij.platform.root.module=${productProperties.rootModuleForModularLoader!!}"
jvmArgs += "-Dintellij.platform.product.mode=${productProperties.productMode.id}"
}
if (productProperties.platformPrefix != null) {
jvmArgs.add("-Didea.platform.prefix=${productProperties.platformPrefix}")
jvmArgs += "-Didea.platform.prefix=${productProperties.platformPrefix}"
}
if (os == OsFamily.WINDOWS) {
@@ -373,31 +361,31 @@ class BuildContextImpl internal constructor(
}
}
jvmArgs.addAll(productProperties.additionalIdeJvmArguments)
jvmArgs.addAll(productProperties.getAdditionalContextDependentIdeJvmArguments(this))
jvmArgs += productProperties.additionalIdeJvmArguments
jvmArgs += productProperties.getAdditionalContextDependentIdeJvmArguments(this)
if (productProperties.useSplash) {
@Suppress("SpellCheckingInspection", "RedundantSuppression")
jvmArgs.add("-Dsplash=true")
jvmArgs += ("-Dsplash=true")
}
// https://youtrack.jetbrains.com/issue/IDEA-269280
jvmArgs.add("-Daether.connector.resumeDownloads=false")
jvmArgs += "-Daether.connector.resumeDownloads=false"
jvmArgs.add("-Dcompose.swing.render.on.graphics=true")
jvmArgs += "-Dcompose.swing.render.on.graphics=true"
jvmArgs.addAll(getCommandLineArgumentsForOpenPackages(this, os))
jvmArgs += getCommandLineArgumentsForOpenPackages(context = this, os)
return jvmArgs
}
override fun addExtraExecutablePattern(os: OsFamily, pattern: String) {
extraExecutablePatterns.updateAndGet { prev ->
prev.with(os, (prev.get(os) ?: persistentListOf()).add(pattern))
prev.with(os, (prev[os] ?: persistentListOf()).add(pattern))
}
}
override fun getExtraExecutablePattern(os: OsFamily): List<String> = extraExecutablePatterns.get().get(os) ?: java.util.List.of()
override fun getExtraExecutablePattern(os: OsFamily): List<String> = extraExecutablePatterns.get()[os] ?: listOf()
override suspend fun buildJar(targetFile: Path, sources: List<Source>, compress: Boolean) {
jarCacheManager.computeIfAbsent(
@@ -420,8 +408,8 @@ class BuildContextImpl internal constructor(
)
}
override val appInfoXml by lazy {
return@lazy computeAppInfoXml(context = this, appInfo = applicationInfo)
override val appInfoXml: String by lazy {
computeAppInfoXml(context = this, appInfo = applicationInfo)
}
@OptIn(DelicateCoroutinesApi::class)
@@ -462,7 +450,7 @@ class BuildContextImpl internal constructor(
private fun createBuildOutputRootEvaluator(projectHome: Path, productProperties: ProductProperties, buildOptions: BuildOptions): (JpsProject) -> Path {
return { project ->
val appInfo = ApplicationInfoPropertiesImpl(project = project, productProperties = productProperties, buildOptions = buildOptions)
val appInfo = ApplicationInfoPropertiesImpl(project, productProperties, buildOptions)
projectHome.resolve("out/${productProperties.getOutputDirectoryName(appInfo)}")
}
}

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
@@ -17,8 +17,8 @@ import org.jetbrains.intellij.build.impl.maven.MavenArtifactData
import org.jetbrains.intellij.build.impl.maven.MavenArtifactsBuilder
import org.jetbrains.intellij.build.impl.moduleBased.findProductModulesFile
import org.jetbrains.intellij.build.impl.productInfo.PRODUCT_INFO_FILE_NAME
import org.jetbrains.intellij.build.impl.productInfo.checkInArchive
import org.jetbrains.intellij.build.impl.productInfo.generateProductInfoJson
import org.jetbrains.intellij.build.impl.productInfo.validateProductJson
import org.jetbrains.intellij.build.impl.projectStructureMapping.ContentReport
import org.jetbrains.intellij.build.impl.projectStructureMapping.getIncludedModules
import org.jetbrains.intellij.build.impl.sbom.SoftwareBillOfMaterialsImpl
@@ -83,7 +83,7 @@ internal class BuildTasksImpl(private val context: BuildContextImpl) : BuildTask
layoutShared(context)
if (includeBinAndRuntime) {
val propertiesFile = createIdeaPropertyFile(context)
val builder = getOsDistributionBuilder(currentOs, propertiesFile, context)!!
val builder = getOsDistributionBuilder(currentOs, context, propertiesFile)!!
builder.copyFilesForOsDistribution(targetDirectory, arch)
context.bundledRuntime.extractTo(currentOs, targetDirectory.resolve("jbr"), arch)
updateExecutablePermissions(targetDirectory, builder.generateExecutableFilesMatchers(includeRuntime = true, arch).keys)
@@ -243,7 +243,7 @@ private suspend fun buildOsSpecificDistributions(context: BuildContext): List<Di
spanBuilder("Adjust executable permissions on common dist").use {
val matchers = SUPPORTED_DISTRIBUTIONS.mapNotNull {
getOsDistributionBuilder(it.os, null, context)
getOsDistributionBuilder(it.os, context)
}.flatMap { builder ->
JvmArchitecture.entries.flatMap { arch ->
builder.generateExecutableFilesMatchers(includeRuntime = true, arch).keys
@@ -258,7 +258,7 @@ private suspend fun buildOsSpecificDistributions(context: BuildContext): List<Di
return@mapNotNull null
}
val builder = getOsDistributionBuilder(os, ideaPropertyFileContent, context) ?: return@mapNotNull null
val builder = getOsDistributionBuilder(os, context, ideaPropertyFileContent) ?: return@mapNotNull null
val stepId = "${os.osId} ${arch.name}"
if (context.options.buildStepsToSkip.contains(stepId)) {
@@ -808,7 +808,7 @@ private suspend fun buildCrossPlatformZip(distResults: List<DistributionForOsTas
val extraFiles = mapOf("dependencies.txt" to copyDependenciesFile(context))
crossPlatformZip(context, distResults, targetFile, executableName, productJson, extraFiles, runtimeModuleRepositoryPath)
checkInArchive(targetFile, pathInArchive = "", context)
validateProductJson(targetFile, pathInArchive = "", context)
context.notifyArtifactBuilt(targetFile)
return targetFile
@@ -850,7 +850,7 @@ private fun checkPlatformSpecificPluginResources(pluginLayouts: List<PluginLayou
}
}
fun getOsDistributionBuilder(os: OsFamily, ideaProperties: CharSequence? = null, context: BuildContext): OsSpecificDistributionBuilder? = when (os) {
fun getOsDistributionBuilder(os: OsFamily, context: BuildContext, ideaProperties: CharSequence? = null): OsSpecificDistributionBuilder? = when (os) {
OsFamily.WINDOWS -> context.windowsDistributionCustomizer?.let {
WindowsDistributionBuilder(context, customizer = it, ideaProperties)
}

View File

@@ -1,7 +1,4 @@
// 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", "ReplaceNegatedIsEmptyWithIsNotEmpty")
@file:OptIn(ExperimentalPathApi::class)
// 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.diagnostic.COROUTINE_DUMP_HEADER
@@ -61,10 +58,8 @@ fun createCompilationContextBlocking(
projectHome: Path,
defaultOutputRoot: Path,
options: BuildOptions = BuildOptions(),
): CompilationContextImpl {
return runBlocking(Dispatchers.Default) {
createCompilationContext(projectHome = projectHome, defaultOutputRoot = defaultOutputRoot, options = options)
}
): CompilationContextImpl = runBlocking(Dispatchers.Default) {
createCompilationContext(projectHome, defaultOutputRoot, options)
}
suspend fun createCompilationContext(
@@ -74,15 +69,10 @@ suspend fun createCompilationContext(
): CompilationContextImpl {
val logDir = options.logDir ?: (options.outRootDir ?: defaultOutputRoot).resolve("log")
JaegerJsonSpanExporterManager.setOutput(logDir.toAbsolutePath().normalize().resolve("trace.json"))
return CompilationContextImpl.createCompilationContext(
projectHome = projectHome,
setupTracer = false,
buildOutputRootEvaluator = { defaultOutputRoot },
options = options,
)
return CompilationContextImpl.createCompilationContext(projectHome, { defaultOutputRoot }, options, setupTracer = false)
}
internal fun computeBuildPaths(options: BuildOptions, buildOut: Path, artifactDir: Path? = null, projectHome: Path): BuildPaths {
internal fun computeBuildPaths(options: BuildOptions, buildOut: Path, projectHome: Path, artifactDir: Path? = null): BuildPaths {
val tempDir = buildOut.resolve("temp")
val result = BuildPaths(
communityHomeDirRoot = COMMUNITY_ROOT,
@@ -193,13 +183,9 @@ class CompilationContextImpl private constructor(
logFreeDiskSpace(dir = projectHome, phase = "before downloading dependencies")
}
val model = loadProject(
projectHome = projectHome,
kotlinBinaries = KotlinBinaries(COMMUNITY_ROOT),
isCompilationRequired = isCompilationRequired(options),
)
val model = loadProject(projectHome, KotlinBinaries(COMMUNITY_ROOT), isCompilationRequired(options))
val buildPaths = customBuildPaths ?: computeBuildPaths(buildOut = options.outRootDir ?: buildOutputRootEvaluator(model.project), options = options, projectHome = projectHome)
val buildPaths = customBuildPaths ?: computeBuildPaths(options, options.outRootDir ?: buildOutputRootEvaluator(model.project), projectHome)
// not as part of prepareForBuild because prepareForBuild may be called several times per each product or another flavor
// (see createCopyForProduct)
@@ -232,17 +218,8 @@ class CompilationContextImpl private constructor(
}
}
override fun createCopy(
messages: BuildMessages,
options: BuildOptions,
paths: BuildPaths,
): CompilationContext {
val copy = CompilationContextImpl(
model = projectModel,
messages = messages,
paths = paths,
options = options,
)
override fun createCopy(messages: BuildMessages, options: BuildOptions, paths: BuildPaths): CompilationContext {
val copy = CompilationContextImpl(projectModel, messages, paths, options)
copy.compilationData = compilationData
return copy
}
@@ -256,7 +233,7 @@ class CompilationContextImpl private constructor(
Files.newDirectoryStream(logDir).use { stream ->
for (file in stream) {
if (!file.endsWith("trace.json")) {
file.deleteRecursively()
NioFiles.deleteRecursively(file)
}
}
}
@@ -281,7 +258,7 @@ class CompilationContextImpl private constructor(
suppressWarnings(project)
ConsoleSpanExporter.setPathRoot(paths.buildOutputDir)
if (options.cleanOutDir || options.forceRebuild) {
cleanOutput(this@CompilationContextImpl, keepCompilationState = keepCompilationState(options))
cleanOutput(context = this@CompilationContextImpl, keepCompilationState(options))
}
else {
Span.current().addEvent("skip output cleaning", Attributes.of(
@@ -300,7 +277,7 @@ class CompilationContextImpl private constructor(
spanBuilder("resolve dependencies and compile modules").use { span ->
compileMutex.withReentrantLock {
resolveProjectDependencies(this@CompilationContextImpl)
reuseOrCompile(context = this@CompilationContextImpl, moduleNames = moduleNames, includingTestsInModules = includingTestsInModules, span = span)
reuseOrCompile(context = this@CompilationContextImpl, moduleNames, includingTestsInModules, span)
}
}
}
@@ -325,7 +302,7 @@ class CompilationContextImpl private constructor(
return module
}
override fun findModule(name: String): JpsModule? = nameToModule.get(name.removeSuffix("._test"))
override fun findModule(name: String): JpsModule? = nameToModule[name.removeSuffix("._test")]
override suspend fun getModuleOutputDir(module: JpsModule, forTests: Boolean): Path {
val url = JpsJavaExtensionService.getInstance().getOutputUrl(/* module = */ module, /* forTests = */ forTests)
@@ -345,7 +322,7 @@ class CompilationContextImpl private constructor(
override suspend fun getModuleRuntimeClasspath(module: JpsModule, forTests: Boolean): List<String> {
val enumerator = JpsJavaExtensionService.dependencies(module).recursively()
// if a project requires different SDKs, they all shouldn't be added to test classpath
// if a project requires different SDKs, they all shouldn't be added to the test classpath
.also { if (forTests) it.withoutSdk() }
.includedIn(JpsJavaClasspathKind.runtime(forTests))
return enumerator.classes().paths.map { it.toString() }
@@ -517,16 +494,16 @@ internal suspend fun cleanOutput(context: CompilationContext, keepCompilationSta
}
private fun printEnvironmentDebugInfo() {
// print it to the stdout since TeamCity will remove any sensitive fields from build log automatically
// print it to the stdout since TeamCity will remove any sensitive fields from the build log automatically
// don't write it to a debug log file!
val env = System.getenv()
for (key in env.keys.sorted()) {
println("ENV $key = ${env.get(key)}")
println("ENV $key = ${env[key]}")
}
val properties = System.getProperties()
for (propertyName in properties.keys.sortedBy { it as String }) {
println("PROPERTY $propertyName = ${properties.get(propertyName).toString()}")
println("PROPERTY $propertyName = ${properties[propertyName].toString()}")
}
}

View File

@@ -1,6 +1,4 @@
// 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
import com.fasterxml.jackson.jr.ob.JSON
@@ -66,7 +64,7 @@ internal suspend fun buildDistribution(
val productRunner = context.createProductRunner()
if (context.productProperties.buildDocAuthoringAssets) {
launch(CoroutineName("build authoring assets")) {
buildAdditionalAuthoringArtifacts(productRunner = productRunner, context = context)
buildAdditionalAuthoringArtifacts(productRunner, context)
}
}
@@ -74,7 +72,7 @@ internal suspend fun buildDistribution(
val contentReport = coroutineScope {
// must be completed before plugin building
val searchableOptionSet = context.executeStep(spanBuilder("build searchable options index"), BuildOptions.SEARCHABLE_OPTIONS_INDEX_STEP) {
buildSearchableOptions(productRunner = productRunner, context = context)
buildSearchableOptions(productRunner, context)
}
val pluginLayouts = getPluginLayoutsByJpsModuleNames(
@@ -84,7 +82,7 @@ internal suspend fun buildDistribution(
val moduleOutputPatcher = ModuleOutputPatcher()
val buildPlatformJob: Deferred<List<DistributionFileEntry>> = async(traceContext + CoroutineName("build platform lib")) {
spanBuilder("build platform lib").use {
val result = buildLib(moduleOutputPatcher = moduleOutputPatcher, platform = state.platform, searchableOptionSetDescriptor = searchableOptionSet, context = context)
val result = buildLib(moduleOutputPatcher, state.platform, searchableOptionSet, context)
if (!isUpdateFromSources && context.productProperties.scrambleMainJar) {
scramble(state.platform, context)
}
@@ -94,31 +92,15 @@ internal suspend fun buildDistribution(
}
val buildNonBundledPlugins = async(CoroutineName("build non-bundled plugins")) {
buildNonBundledPlugins(
pluginsToPublish = state.pluginsToPublish,
compressPluginArchive = !isUpdateFromSources && context.options.compressZipFiles,
buildPlatformLibJob = buildPlatformJob,
state = state,
searchableOptionSet = searchableOptionSet,
context = context,
)
val compressPluginArchive = !isUpdateFromSources && context.options.compressZipFiles
buildNonBundledPlugins(state.pluginsToPublish, compressPluginArchive, buildPlatformJob, state, searchableOptionSet, context)
}
val bundledPluginItems = buildBundledPluginsForAllPlatforms(
state = state,
pluginLayouts = pluginLayouts,
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
searchableOptionSetDescriptor = searchableOptionSet,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
state, pluginLayouts, isUpdateFromSources, buildPlatformJob, searchableOptionSet, moduleOutputPatcher, context
)
ContentReport(
platform = buildPlatformJob.await(),
bundledPlugins = bundledPluginItems,
nonBundledPlugins = buildNonBundledPlugins.await(),
)
ContentReport(buildPlatformJob.await(), bundledPluginItems, buildNonBundledPlugins.await())
}
coroutineScope {
@@ -127,7 +109,7 @@ internal suspend fun buildDistribution(
Files.createDirectories(context.paths.artifactDir)
val contentReportFile = context.paths.artifactDir.resolve("content-report.zip")
writeNewZipWithoutIndex(contentReportFile) { zipFileWriter ->
buildJarContentReport(contentReport = contentReport, zipFileWriter = zipFileWriter, buildPaths = context.paths, context = context)
buildJarContentReport(contentReport, zipFileWriter, context.paths, context)
}
context.notifyArtifactBuilt(contentReportFile)
}
@@ -152,51 +134,25 @@ private suspend fun buildBundledPluginsForAllPlatforms(
searchableOptionSetDescriptor: SearchableOptionSetDescriptor?,
moduleOutputPatcher: ModuleOutputPatcher,
context: BuildContext,
): List<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>> {
return coroutineScope {
val commonDeferred = async(CoroutineName("build bundled plugins")) {
doBuildBundledPlugins(
state = state,
plugins = pluginLayouts,
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
searchableOptionSet = searchableOptionSetDescriptor,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
}
): List<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>> = coroutineScope {
val commonDeferred = async(CoroutineName("build bundled plugins")) {
doBuildBundledPlugins(state, pluginLayouts, isUpdateFromSources, buildPlatformJob, searchableOptionSetDescriptor, moduleOutputPatcher, context)
}
val additionalDeferred = async(CoroutineName("build additional plugins")) {
copyAdditionalPlugins(context = context, pluginDir = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
}
val additionalDeferred = async(CoroutineName("build additional plugins")) {
copyAdditionalPlugins(context, pluginDir = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
}
val pluginDirs = getPluginDirs(context = context, isUpdateFromSources = isUpdateFromSources)
val specificDeferred = async(CoroutineName("build OS-specific bundled plugins")) {
buildOsSpecificBundledPlugins(
state = state,
plugins = pluginLayouts,
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
context = context,
searchableOptionSet = searchableOptionSetDescriptor,
pluginDirs = pluginDirs,
moduleOutputPatcher = moduleOutputPatcher,
)
}
val pluginDirs = getPluginDirs(context, isUpdateFromSources)
val specificDeferred = async(CoroutineName("build OS-specific bundled plugins")) {
buildOsSpecificBundledPlugins(state, pluginLayouts, isUpdateFromSources, buildPlatformJob, context, searchableOptionSetDescriptor, pluginDirs, moduleOutputPatcher)
}
val common = commonDeferred.await()
val specific = specificDeferred.await()
buildPlatformJob.join()
writePluginInfo(
moduleOutputPatcher = moduleOutputPatcher,
pluginDirs = pluginDirs,
common = common,
specific = specific,
additional = additionalDeferred.await(),
context = context,
)
listOf(common, specific.values.flatten())
}.flatten()
val common = commonDeferred.await()
val specific = specificDeferred.await()
buildPlatformJob.join()
writePluginInfo(moduleOutputPatcher, pluginDirs, common, specific, additionalDeferred.await(), context)
common + specific.values.flatten()
}
private fun writePluginInfo(
@@ -207,23 +163,23 @@ private fun writePluginInfo(
additional: List<Pair<Path, List<Path>>>?,
context: BuildContext,
) {
val commonClassPath = generatePluginClassPath(pluginEntries = common, moduleOutputPatcher = moduleOutputPatcher)
val commonClassPath = generatePluginClassPath(pluginEntries = common, moduleOutputPatcher)
val additionalClassPath = additional?.let { generatePluginClassPathFromPrebuiltPluginFiles(it) }
for ((supportedDist) in pluginDirs) {
val specificList = specific.get(supportedDist)
val specificClasspath = specificList?.let { generatePluginClassPath(pluginEntries = it, moduleOutputPatcher = moduleOutputPatcher) }
val specificList = specific[supportedDist]
val specificClasspath = specificList?.let { generatePluginClassPath(pluginEntries = it, moduleOutputPatcher) }
val byteOut = ByteArrayOutputStream()
val out = DataOutputStream(byteOut)
val pluginCount = common.size + (additional?.size ?: 0) + (specificList?.size ?: 0)
writePluginClassPathHeader(out = out, isJarOnly = true, pluginCount = pluginCount, moduleOutputPatcher = moduleOutputPatcher, context = context)
writePluginClassPathHeader(out, isJarOnly = true, pluginCount, moduleOutputPatcher, context)
out.write(commonClassPath)
additionalClassPath?.let { out.write(it) }
specificClasspath?.let { out.write(it) }
out.close()
context.addDistFile(DistFile(relativePath = PLUGIN_CLASSPATH, content = InMemoryDistFileContent(byteOut.toByteArray()), os = supportedDist.os, arch = supportedDist.arch))
context.addDistFile(DistFile(InMemoryDistFileContent(byteOut.toByteArray()), PLUGIN_CLASSPATH, supportedDist.os, supportedDist.arch))
}
}
@@ -236,16 +192,15 @@ fun validateModuleStructure(platform: PlatformLayout, context: BuildContext) {
}
}
private fun getPluginDirs(context: BuildContext, isUpdateFromSources: Boolean): List<Pair<SupportedDistribution, Path>> {
private fun getPluginDirs(context: BuildContext, isUpdateFromSources: Boolean): List<Pair<SupportedDistribution, Path>> =
if (isUpdateFromSources) {
return listOf(SupportedDistribution(OsFamily.currentOs, JvmArchitecture.currentJvmArch) to context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
listOf(SupportedDistribution(OsFamily.currentOs, JvmArchitecture.currentJvmArch) to context.paths.distAllDir.resolve(PLUGINS_DIRECTORY))
}
else {
return SUPPORTED_DISTRIBUTIONS.map {
it to getOsAndArchSpecificDistDirectory(osFamily = it.os, arch = it.arch, context = context).resolve(PLUGINS_DIRECTORY)
SUPPORTED_DISTRIBUTIONS.map {
it to getOsAndArchSpecificDistDirectory(it.os, it.arch, context).resolve(PLUGINS_DIRECTORY)
}
}
}
suspend fun buildBundledPlugins(
state: DistributionBuilderState,
@@ -256,15 +211,7 @@ suspend fun buildBundledPlugins(
moduleOutputPatcher: ModuleOutputPatcher,
context: BuildContext,
) {
doBuildBundledPlugins(
state = state,
plugins = plugins,
isUpdateFromSources = isUpdateFromSources,
buildPlatformJob = buildPlatformJob,
searchableOptionSet = searchableOptionSetDescriptor,
moduleOutputPatcher = moduleOutputPatcher,
context = context,
)
doBuildBundledPlugins(state, plugins, isUpdateFromSources, buildPlatformJob, searchableOptionSetDescriptor, moduleOutputPatcher, context)
}
private suspend fun doBuildBundledPlugins(
@@ -288,21 +235,12 @@ private suspend fun doBuildBundledPlugins(
// doesn't make sense to require passing here a list with a stable order (unnecessary complication, sorting by main module is enough)
pluginsToBundle.sortWith(PLUGIN_LAYOUT_COMPARATOR_BY_MAIN_MODULE)
val targetDir = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY)
val entries = buildPlugins(
moduleOutputPatcher = moduleOutputPatcher,
plugins = pluginsToBundle,
targetDir = targetDir,
state = state,
context = context,
buildPlatformJob = buildPlatformJob,
os = null,
searchableOptionSet = searchableOptionSet,
)
val entries = buildPlugins(moduleOutputPatcher, pluginsToBundle, os = null, targetDir, state, context, buildPlatformJob, searchableOptionSet)
buildPlatformSpecificPluginResources(
plugins = pluginsToBundle.filter { it.platformResourceGenerators.isNotEmpty() },
targetDirs = getPluginDirs(context, isUpdateFromSources),
context = context,
context,
)
entries
@@ -327,12 +265,12 @@ private suspend fun buildOsSpecificBundledPlugins(
.use {
pluginDirs.mapNotNull { (dist, targetDir) ->
val (os, arch) = dist
if (arch != currentArch || !context.shouldBuildDistributionForOS(os = os, arch = arch)) {
if (arch != currentArch || !context.shouldBuildDistributionForOS(os, arch)) {
return@mapNotNull null
}
val osSpecificPlugins = plugins.filter {
satisfiesBundlingRequirements(plugin = it, osFamily = os, arch = arch, context = context)
satisfiesBundlingRequirements(plugin = it, os, arch, context)
}
if (osSpecificPlugins.isEmpty()) {
return@mapNotNull null
@@ -345,16 +283,7 @@ private suspend fun buildOsSpecificBundledPlugins(
.setAttribute("count", osSpecificPlugins.size.toLong())
.setAttribute("outDir", targetDir.toString())
.use {
buildPlugins(
moduleOutputPatcher = moduleOutputPatcher,
plugins = osSpecificPlugins,
targetDir = targetDir,
state = state,
context = context,
os = os,
buildPlatformJob = buildPlatformJob,
searchableOptionSet = searchableOptionSet,
)
buildPlugins(moduleOutputPatcher, osSpecificPlugins, os, targetDir, state, context, buildPlatformJob, searchableOptionSet)
}
}
}
@@ -398,12 +327,12 @@ internal suspend fun buildNonBundledPlugins(
return@executeStep emptyList()
}
var buildKeymapPluginsTask = if (context.options.buildStepsToSkip.contains(BuildOptions.KEYMAP_PLUGINS_STEP)) {
val buildKeymapPluginsTask = if (context.options.buildStepsToSkip.contains(BuildOptions.KEYMAP_PLUGINS_STEP)) {
null
}
else {
async(CoroutineName("build keymap plugins")) {
buildKeymapPlugins(targetDir = context.nonBundledPluginsToBePublished, context = context)
buildKeymapPlugins(targetDir = context.nonBundledPluginsToBePublished, context)
}
}
val moduleOutputPatcher = ModuleOutputPatcher()
@@ -413,18 +342,11 @@ internal suspend fun buildNonBundledPlugins(
// buildPlugins pluginBuilt listener is called concurrently
val pluginSpecs = ConcurrentLinkedQueue<PluginRepositorySpec>()
val prepareCustomPluginRepository = context.productProperties.productLayout.prepareCustomPluginRepositoryForPublishedPlugins &&
!context.isStepSkipped(BuildOptions.ARCHIVE_PLUGINS)
val mappings = buildPlugins(
moduleOutputPatcher = moduleOutputPatcher,
plugins = pluginsToPublish.sortedWith(PLUGIN_LAYOUT_COMPARATOR_BY_MAIN_MODULE),
targetDir = stageDir,
state = state,
searchableOptionSet = searchableOptionSet,
context = context,
buildPlatformJob = buildPlatformLibJob,
os = null,
) { plugin, pluginDirOrFile ->
val prepareCustomPluginRepository =
context.productProperties.productLayout.prepareCustomPluginRepositoryForPublishedPlugins &&
!context.isStepSkipped(BuildOptions.ARCHIVE_PLUGINS)
val plugins = pluginsToPublish.sortedWith(PLUGIN_LAYOUT_COMPARATOR_BY_MAIN_MODULE)
val mappings = buildPlugins(moduleOutputPatcher, plugins, os = null, stageDir, state, context, buildPlatformLibJob, searchableOptionSet) { plugin, pluginDirOrFile ->
val pluginVersion = if (plugin.mainModule == BUILT_IN_HELP_MODULE_NAME) {
context.buildNumber
}
@@ -432,7 +354,7 @@ internal suspend fun buildNonBundledPlugins(
plugin.versionEvaluator.evaluate(
pluginXmlSupplier = { (context as BuildContextImpl).jarPackagerDependencyHelper.getPluginXmlContent(context.findRequiredModule(plugin.mainModule)) },
ideBuildVersion = context.pluginBuildNumber,
context = context,
context,
).pluginVersion
}
@@ -447,19 +369,11 @@ internal suspend fun buildNonBundledPlugins(
dirToJar.add(NonBundledPlugin(sourceDir = pluginDirOrFile, targetZip = destFile, optimizedZip = !plugin.enableSymlinksAndExecutableResources))
}
archivePlugins(items = dirToJar, compress = compressPluginArchive, withBlockMap = compressPluginArchive, context = context)
archivePlugins(items = dirToJar, compress = compressPluginArchive, withBlockMap = compressPluginArchive, context)
val helpPlugin = buildHelpPlugin(pluginVersion = context.pluginBuildNumber, context = context)
val helpPlugin = buildHelpPlugin(pluginVersion = context.pluginBuildNumber, context)
if (helpPlugin != null) {
val spec = buildHelpPlugin(
helpPlugin = helpPlugin,
pluginsToPublishDir = stageDir,
targetDir = context.nonBundledPluginsToBePublished,
moduleOutputPatcher = moduleOutputPatcher,
state = state,
searchableOptionSetDescriptor = searchableOptionSet,
context = context,
)
val spec = buildHelpPlugin(helpPlugin, stageDir, context.nonBundledPluginsToBePublished, moduleOutputPatcher, state, searchableOptionSet, context)
pluginSpecs.add(spec)
}
@@ -496,7 +410,7 @@ private suspend fun validatePlugins(context: BuildContext, pluginSpecs: Collecti
continue
}
launch(CoroutineName("$path plugin validation")) {
validatePlugin(file = path, context = context, span = span)
validatePlugin(path, context, span)
}
}
}
@@ -514,10 +428,8 @@ private fun validatePlugin(file: Path, context: BuildContext, span: Span) {
if (problems.isNotEmpty()) {
span.addEvent("failed", Attributes.of(AttributeKey.stringKey("path"), "$file"))
context.messages.reportBuildProblem(
problems.joinToString(
prefix = "${id ?: file}: ",
separator = ". ",
), identity = "${id ?: file}"
description = problems.joinToString(". ", prefix = "${id ?: file}: "),
identity = "${id ?: file}"
)
}
}
@@ -534,17 +446,9 @@ private suspend fun buildHelpPlugin(
val directory = helpPlugin.directoryName
val destFile = targetDir.resolve("$directory.zip")
spanBuilder("build help plugin").setAttribute("dir", directory).use {
buildPlugins(
moduleOutputPatcher = moduleOutputPatcher,
plugins = listOf(helpPlugin),
targetDir = pluginsToPublishDir.resolve(directory),
state = state,
context = context,
searchableOptionSet = searchableOptionSetDescriptor,
buildPlatformJob = null,
os = null,
)
zipWithCompression(targetFile = destFile, dirs = mapOf(pluginsToPublishDir.resolve(directory) to ""))
val targetDir = pluginsToPublishDir.resolve(directory)
buildPlugins(moduleOutputPatcher, listOf(helpPlugin), os = null, targetDir, state, context, buildPlatformJob = null, searchableOptionSetDescriptor)
zipWithCompression(targetFile = destFile, dirs = mapOf(targetDir to ""))
null
}
return PluginRepositorySpec(pluginZip = destFile, pluginXml = moduleOutputPatcher.getPatchedPluginXml(helpPlugin.mainModule))
@@ -555,12 +459,12 @@ internal suspend fun generateProjectStructureMapping(platformLayout: PlatformLay
val moduleOutputPatcher = ModuleOutputPatcher()
val libDirLayout = async(CoroutineName("layout platform distribution")) {
layoutPlatformDistribution(
moduleOutputPatcher = moduleOutputPatcher,
moduleOutputPatcher,
targetDirectory = context.paths.distAllDir,
platform = platformLayout,
context = context,
searchableOptionSet = null,
copyFiles = false,
context,
)
}
@@ -570,17 +474,17 @@ internal suspend fun generateProjectStructureMapping(platformLayout: PlatformLay
)
val entries = mutableListOf<Pair<PluginBuildDescriptor, List<DistributionFileEntry>>>()
for (plugin in allPlugins) {
if (satisfiesBundlingRequirements(plugin = plugin, osFamily = null, arch = null, context = context)) {
if (satisfiesBundlingRequirements(plugin, osFamily = null, arch = null, context)) {
val targetDirectory = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY).resolve(plugin.directoryName)
entries.add(PluginBuildDescriptor(dir = targetDirectory, layout = plugin, os = null, moduleNames = emptyList()) to layoutDistribution(
layout = plugin,
platformLayout = platformLayout,
targetDirectory = targetDirectory,
platformLayout,
targetDirectory,
copyFiles = false,
moduleOutputPatcher = moduleOutputPatcher,
moduleOutputPatcher,
includedModules = plugin.includedModules,
searchableOptionSet = null,
context = context,
context,
).first)
}
}
@@ -609,16 +513,16 @@ internal suspend fun buildPlugins(
val entries = coroutineScope {
plugins.map { plugin ->
if (plugin.mainModule != BUILT_IN_HELP_MODULE_NAME) {
checkOutputOfPluginModules(mainPluginModule = plugin.mainModule, includedModules = plugin.includedModules, moduleExcludes = plugin.moduleExcludes, context = context)
checkOutputOfPluginModules(plugin.mainModule, plugin.includedModules, plugin.moduleExcludes, context)
patchPluginXml(
moduleOutputPatcher = moduleOutputPatcher,
plugin = plugin,
releaseDate = context.applicationInfo.majorReleaseDate,
releaseVersion = context.applicationInfo.releaseVersionForLicensing,
pluginsToPublish = state.pluginsToPublish,
moduleOutputPatcher,
plugin,
context.applicationInfo.majorReleaseDate,
context.applicationInfo.releaseVersionForLicensing,
state.pluginsToPublish,
helper = (context as BuildContextImpl).jarPackagerDependencyHelper,
platformLayout = state.platform,
context = context,
context,
)
}
@@ -631,10 +535,10 @@ internal suspend fun buildPlugins(
platformLayout = state.platform,
targetDirectory = pluginDir,
copyFiles = true,
moduleOutputPatcher = moduleOutputPatcher,
includedModules = plugin.includedModules,
searchableOptionSet = searchableOptionSet,
context = context,
moduleOutputPatcher,
plugin.includedModules,
searchableOptionSet,
context,
)
pluginBuilt?.invoke(plugin, file)
entries
@@ -651,11 +555,11 @@ internal suspend fun buildPlugins(
}
else {
// we cannot start executing right now because the plugin can use other plugins in a scramble classpath
scrambleTasks.add(ScrambleTask(plugin = plugin, pluginDir = pluginDir, targetDir = targetDir))
scrambleTasks.add(ScrambleTask(plugin, pluginDir, targetDir))
}
}
PluginBuildDescriptor(dir = pluginDir, layout = plugin, os = os, moduleNames = emptyList()) to task.await()
PluginBuildDescriptor(pluginDir, os, layout = plugin, moduleNames = emptyList()) to task.await()
}
}
@@ -674,7 +578,7 @@ internal suspend fun buildPlugins(
targetDir = scrambleTask.pluginDir,
additionalPluginDir = scrambleTask.targetDir,
layouts = plugins,
context = context,
context,
)
}
}
@@ -752,14 +656,8 @@ suspend fun buildLib(
searchableOptionSetDescriptor: SearchableOptionSetDescriptor?,
context: BuildContext,
): List<DistributionFileEntry> {
val libDirMappings = layoutPlatformDistribution(
moduleOutputPatcher = moduleOutputPatcher,
targetDirectory = context.paths.distAllDir,
platform = platform,
copyFiles = true,
searchableOptionSet = searchableOptionSetDescriptor,
context = context,
)
val targetDirectory = context.paths.distAllDir
val libDirMappings = layoutPlatformDistribution(moduleOutputPatcher, targetDirectory, platform, searchableOptionSetDescriptor, copyFiles = true, context)
context.proprietaryBuildTools.scrambleTool?.validatePlatformLayout(platform.includedModules, context)
return libDirMappings
}
@@ -774,9 +672,9 @@ suspend fun layoutPlatformDistribution(
): List<DistributionFileEntry> {
if (copyFiles) {
coroutineScope {
createStatisticsRecorderBundledMetadataProviderTask(moduleOutputPatcher = moduleOutputPatcher, context = context)
createStatisticsRecorderBundledMetadataProviderTask(moduleOutputPatcher, context)
launch(CoroutineName("patch keymap with Alt click reassigned to multiple carets")) {
patchKeyMapWithAltClickReassignedToMultipleCarets(moduleOutputPatcher = moduleOutputPatcher, context = context)
patchKeyMapWithAltClickReassignedToMultipleCarets(moduleOutputPatcher, context)
}
launch(CoroutineName("write patched app info")) {
spanBuilder("write patched app info").use {
@@ -793,16 +691,8 @@ suspend fun layoutPlatformDistribution(
return spanBuilder("layout lib")
.setAttribute("path", targetDirectory.toString())
.use {
layoutDistribution(
layout = platform,
platformLayout = platform,
targetDirectory = targetDirectory,
copyFiles = copyFiles,
moduleOutputPatcher = moduleOutputPatcher,
includedModules = platform.includedModules,
searchableOptionSet = searchableOptionSet,
context = context,
).first
layoutDistribution(layout = platform, platformLayout = platform, targetDirectory, copyFiles, moduleOutputPatcher, platform.includedModules, searchableOptionSet, context)
.first
}
}
@@ -838,7 +728,7 @@ private suspend fun checkOutputOfPluginModules(
moduleName = module,
filePath = "com/intellij/uiDesigner/core/GridLayoutManager.class",
excludes = moduleExcludes[module] ?: emptyList(),
context = context,
context,
)) {
"Runtime classes of GUI designer must not be packaged to '$module' module in '$mainPluginModule' plugin, " +
"because they are included into a platform JAR. Make sure that 'Automatically copy form runtime classes " +
@@ -968,7 +858,7 @@ private suspend fun buildKeymapPlugins(targetDir: Path, context: BuildContext):
arrayOf("Sublime Text", "Sublime Text (Mac OS X)"),
).map {
async(CoroutineName("build keymap plugin for ${it[0]}")) {
buildKeymapPlugin(keymaps = it, buildNumber = context.buildNumber, targetDir = targetDir, keymapDir = keymapDir)
buildKeymapPlugin(keymaps = it, context.buildNumber, targetDir, keymapDir)
}
}
}.map { it.getCompleted() }
@@ -994,7 +884,7 @@ suspend fun layoutDistribution(
}
}
// patchers must be executed _before_ pack because patcher patches module output
// patchers must be executed _before_ packing, because patchers patch the module output
val patchers = layout.patchers
if (!patchers.isEmpty()) {
spanBuilder("execute custom patchers").setAttribute("count", patchers.size.toLong()).use {
@@ -1012,8 +902,8 @@ suspend fun layoutDistribution(
tasks.add(async(CoroutineName("pack $outputDir")) {
spanBuilder("pack").setAttribute("outputDir", outputDir.toString()).use {
JarPackager.pack(
includedModules = includedModules,
outputDir = outputDir,
includedModules,
outputDir,
isRootDir = layout is PlatformLayout,
layout = layout,
platformLayout = platformLayout,
@@ -1025,12 +915,14 @@ suspend fun layoutDistribution(
}
})
if (copyFiles &&
!context.options.skipCustomResourceGenerators &&
(layout.resourcePaths.isNotEmpty() || layout is PluginLayout && !layout.resourceGenerators.isEmpty())) {
if (
copyFiles &&
!context.options.skipCustomResourceGenerators &&
(layout.resourcePaths.isNotEmpty() || layout is PluginLayout && !layout.resourceGenerators.isEmpty())
) {
tasks.add(async(Dispatchers.IO + CoroutineName("pack additional resources")) {
spanBuilder("pack additional resources").use {
layoutAdditionalResources(layout = layout, context = context, targetDirectory = targetDirectory)
layoutAdditionalResources(layout, context, targetDirectory)
emptyList()
}
})
@@ -1039,7 +931,7 @@ suspend fun layoutDistribution(
if (!layout.includedArtifacts.isEmpty()) {
tasks.add(async(CoroutineName("pack artifacts")) {
spanBuilder("pack artifacts").use {
layoutArtifacts(layout = layout, context = context, copyFiles = copyFiles, targetDirectory = targetDirectory)
layoutArtifacts(layout, context, copyFiles, targetDirectory)
}
})
}
@@ -1051,7 +943,7 @@ suspend fun layoutDistribution(
private fun layoutResourcePaths(layout: BaseLayout, context: BuildContext, targetDirectory: Path, overwrite: Boolean) {
for (resourceData in layout.resourcePaths) {
val source = basePath(buildContext = context, moduleName = resourceData.moduleName).resolve(resourceData.resourcePath).normalize()
val source = basePath(context, resourceData.moduleName).resolve(resourceData.resourcePath).normalize()
var target = targetDirectory.resolve(resourceData.relativeOutputPath).normalize()
if (resourceData.packToZip) {
if (Files.isDirectory(source)) {
@@ -1076,12 +968,12 @@ private fun layoutResourcePaths(layout: BaseLayout, context: BuildContext, targe
}
else {
if (overwrite) {
copyDir(sourceDir = source, targetDir = target, fileFilter = Predicate {
copyDir(source, target, fileFilter = Predicate {
copyIfChanged(target, source, it)
})
}
else {
copyDir(sourceDir = source, targetDir = target)
copyDir(source, target)
}
}
}
@@ -1107,7 +999,7 @@ private fun copyIfChanged(targetDir: Path, sourceDir: Path, sourceFile: Path): B
private suspend fun layoutAdditionalResources(layout: BaseLayout, context: BuildContext, targetDirectory: Path) {
// quick fix for a very annoying FileAlreadyExistsException in CLion dev build
val overwrite = ("intellij.rider.plugins.clion.radler" == (layout as? PluginLayout)?.mainModule)
layoutResourcePaths(layout = layout, context = context, targetDirectory = targetDirectory, overwrite = overwrite)
layoutResourcePaths(layout, context, targetDirectory, overwrite = overwrite)
if (layout !is PluginLayout) {
return
}
@@ -1156,7 +1048,7 @@ private suspend fun layoutArtifacts(layout: BaseLayout,
}
}
}
addArtifactMapping(artifact = artifact, entries = entries, artifactFile = artifactPath)
addArtifactMapping(artifact, entries, artifactPath)
}
return entries
}
@@ -1231,7 +1123,7 @@ private suspend fun archivePlugins(items: Collection<NonBundledPlugin>, compress
.setAttribute("outputFile", target.toString())
.setAttribute("optimizedZip", optimized)
.use {
archivePlugin(optimized = optimized, target = target, compress = compress, source = source, context = context)
archivePlugin(optimized, target, compress, source, context)
}
if (withBlockMap) {
spanBuilder("build plugin blockmap").setAttribute("file", target.toString()).use {
@@ -1245,7 +1137,7 @@ private suspend fun archivePlugins(items: Collection<NonBundledPlugin>, compress
private fun archivePlugin(optimized: Boolean, target: Path, compress: Boolean, source: Path, context: BuildContext) {
if (optimized) {
writeNewZipWithoutIndex(target, compress = compress) { zipCreator ->
writeNewZipWithoutIndex(target, compress) { zipCreator ->
ZipArchiver(zipCreator).use { archiver ->
if (Files.isDirectory(source)) {
archiver.setRootDir(source, source.fileName.toString())
@@ -1260,7 +1152,7 @@ private fun archivePlugin(optimized: Boolean, target: Path, compress: Boolean, s
}
else {
writeNewFile(target) { outFileChannel ->
NoDuplicateZipArchiveOutputStream(outFileChannel, compress = context.options.compressZipFiles).use { out ->
NoDuplicateZipArchiveOutputStream(outFileChannel, context.options.compressZipFiles).use { out ->
out.setUseZip64(Zip64Mode.Never)
out.dir(source, "${source.fileName}/", entryCustomizer = { entry, file, _ ->
if (Files.isExecutable(file)) {
@@ -1273,7 +1165,7 @@ private fun archivePlugin(optimized: Boolean, target: Path, compress: Boolean, s
}
/**
* Builds a blockmap and hash files for plugin to provide downloading plugins via incremental downloading algorithm Blockmap.
* Builds a blockmap and hash files for a plugin.
*/
private fun buildBlockMap(file: Path, json: JSON) {
val algorithm = "SHA-256"
@@ -1312,8 +1204,8 @@ private fun sortEntries(unsorted: List<DistributionFileEntry>): List<Distributio
// also, put libraries from Maven repo ahead of others, for them to not depend on the lexicographical order of Maven repo and source path
private fun isFromLocalMavenRepo(path: Path) = path.startsWith(MAVEN_REPO)
suspend fun createIdeClassPath(platform: PlatformLayout, context: BuildContext): Collection<String> {
val contentReport = generateProjectStructureMapping(context = context, platformLayout = platform)
suspend fun createIdeClassPath(platformLayout: PlatformLayout, context: BuildContext): Collection<String> {
val contentReport = generateProjectStructureMapping(platformLayout, context)
val pluginLayouts = context.productProperties.productLayout.pluginLayouts
val classPath = LinkedHashSet<Path>()
@@ -1339,7 +1231,7 @@ suspend fun createIdeClassPath(platform: PlatformLayout, context: BuildContext):
val pluginDir = context.paths.distAllDir.resolve(PLUGINS_DIRECTORY)
for (entry in contentReport.bundledPlugins.flatMap { it.second }) {
val relativePath = pluginDir.relativize(entry.path)
// for plugins, our classloader load jars only from lib folder
// for plugins, our classloader load JARs only from the "lib/" directory
if (relativePath.nameCount != 3 || relativePath.getName(1).toString() != LIB_DIRECTORY) {
continue
}
@@ -1352,8 +1244,7 @@ suspend fun createIdeClassPath(platform: PlatformLayout, context: BuildContext):
}
}
is LibraryFileEntry -> classPath.add(entry.libraryFile!!)
is CustomAssetEntry -> {
}
is CustomAssetEntry -> { }
else -> throw UnsupportedOperationException("Entry $entry is not supported")
}
}

View File

@@ -180,7 +180,7 @@ class LinuxDistributionBuilder(
.use(Dispatchers.IO) {
val executableFileMatchers = generateExecutableFilesMatchers(includeRuntime = runtimeDir != null, arch).keys
tar(tarPath, tarRoot, dirs, executableFileMatchers, context.options.buildDateInSeconds)
checkInArchive(tarPath, tarRoot, context)
validateProductJson(tarPath, tarRoot, context)
context.notifyArtifactBuilt(tarPath)
checkExecutablePermissions(tarPath, rootDirectoryName, includeRuntime = runtimeDir != null, arch)
}
@@ -271,7 +271,7 @@ class LinuxDistributionBuilder(
val productJsonDir = context.paths.tempDir.resolve("linux.dist.snap.product-info.json.$architecture")
val productJsonFile = writeProductJsonFile(productJsonDir, arch)
val installationDirectories = listOf(context.paths.distAllDir, unixDistPath, runtimeDir)
validateProductJson(jsonText = productJsonFile.readText(), relativePathToProductJson = "", installationDirectories, installationArchives = emptyList(), context)
validateProductJson(jsonText = productJsonFile.readText(), installationDirectories, installationArchives = emptyList(), context)
val resultDir = snapDir.resolve("result")
Files.createDirectories(resultDir)

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
@@ -142,7 +142,7 @@ class MacDistributionBuilder(
val extraFiles = context.getDistFiles(os = OsFamily.MACOS, arch)
val directories = listOf(context.paths.distAllDir, osAndArchSpecificDistPath, runtimeDir)
val builder = this@MacDistributionBuilder
val productJson = generateProductJson(context, arch = arch, withRuntime = true)
val productJson = generateProductJson(context, arch, withRuntime = true)
buildMacZip(
macDistributionBuilder = builder,
targetFile = macZip,
@@ -161,7 +161,7 @@ class MacDistributionBuilder(
targetFile = macZipWithoutRuntime,
zipRoot = zipRoot,
arch = arch,
productJson = generateProductJson(context, arch = arch, withRuntime = false),
productJson = generateProductJson(context, arch, withRuntime = false),
directories = directories.filterNot { it == runtimeDir },
extraFiles = extraFiles,
includeRuntime = false,
@@ -344,29 +344,29 @@ class MacDistributionBuilder(
override fun isRuntimeBundled(file: Path): Boolean = !file.name.contains(NO_RUNTIME_SUFFIX)
private suspend fun generateProductJson(context: BuildContext, arch: JvmArchitecture, withRuntime: Boolean): String =
generateProductInfoJson(
relativePathToBin = "../bin",
builtinModules = context.builtinModule,
launch = listOf(createProductInfoLaunchData(context, arch, withRuntime)),
context
)
private suspend fun createProductInfoLaunchData(context: BuildContext, arch: JvmArchitecture, withRuntime: Boolean): ProductInfoLaunchData {
private suspend fun generateProductJson(context: BuildContext, arch: JvmArchitecture, withRuntime: Boolean): String {
val embeddedFrontendLaunchData = generateEmbeddedFrontendLaunchData(arch, OsFamily.MACOS, context) {
"../bin/${it.productProperties.baseFileName}.vmoptions"
}
val qodanaCustomLaunchData = generateQodanaLaunchData(context, arch, OsFamily.MACOS)
return ProductInfoLaunchData.create(
OsFamily.MACOS.osName,
arch.dirName,
launcherPath = "../MacOS/${context.productProperties.baseFileName}",
javaExecutablePath = if (withRuntime) "../jbr/Contents/Home/bin/java" else null,
vmOptionsFilePath = "../bin/${context.productProperties.baseFileName}.vmoptions",
bootClassPathJarNames = context.bootClassPathJarNames,
additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.MACOS, arch),
mainClass = context.ideMainClassName,
customCommands = listOfNotNull(embeddedFrontendLaunchData, qodanaCustomLaunchData)
return generateProductInfoJson(
relativePathToBin = "../bin",
builtinModules = context.builtinModule,
launch = listOf(
ProductInfoLaunchData.create(
OsFamily.MACOS.osName,
arch.dirName,
launcherPath = "../MacOS/${context.productProperties.baseFileName}",
javaExecutablePath = if (withRuntime) "../jbr/Contents/Home/bin/java" else null,
vmOptionsFilePath = "../bin/${context.productProperties.baseFileName}.vmoptions",
bootClassPathJarNames = context.bootClassPathJarNames,
additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.MACOS, arch),
mainClass = context.ideMainClassName,
customCommands = listOfNotNull(embeddedFrontendLaunchData, qodanaCustomLaunchData)
)
),
context
)
}
@@ -444,7 +444,7 @@ class MacDistributionBuilder(
}
}
checkInArchive(targetFile, pathInArchive = "${zipRoot}/Resources", macDistributionBuilder.context)
validateProductJson(targetFile, pathInArchive = "${zipRoot}/Resources", macDistributionBuilder.context)
}
}

View File

@@ -122,9 +122,8 @@ interface OsSpecificDistributionBuilder {
}
}
private fun checkZip(distribution: Path, root: String, patterns: Collection<PathMatcher>): List<MatchedFile> {
return ZipFile(Files.newByteChannel(distribution)).use { zipFile ->
return ZipFile.Builder().setSeekableByteChannel(Files.newByteChannel(distribution)).get().use { zipFile ->
zipFile.entries.asSequence().filter { !it.isDirectory }.mapNotNull { entry ->
var entryPath = Path.of(entry.name)
if (!root.isEmpty()) {
@@ -141,9 +140,7 @@ interface OsSpecificDistributionBuilder {
companion object {
@Internal
fun suffix(arch: JvmArchitecture): String = when (arch) {
JvmArchitecture.x64 -> ""
else -> "-${arch.fileSuffix}"
}
fun suffix(arch: JvmArchitecture): String =
if (arch == JvmArchitecture.x64) "" else "-${arch.fileSuffix}"
}
}

View File

@@ -1,11 +1,11 @@
// 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 org.jetbrains.intellij.build.FrontendModuleFilter
/**
* Names of JAR files from IDE_HOME/lib directory. These names are implementation detail and may be changed in the future, code outside
* the build scripts must not rely on them.
* Names of JAR files from `IDE_HOME/lib` directory.
* These names are implementation detail and may be changed in the future; code outside the build scripts must not rely on them.
*/
object PlatformJarNames {
/**
@@ -20,7 +20,7 @@ object PlatformJarNames {
internal const val APP_CLIENT_JAR: String = "app-client.jar"
/**
* Returns name of the default JAR for a platform module.
* Returns the name of the default JAR for a platform module.
*/
internal fun getPlatformModuleJarName(moduleName: String, frontendModuleFilter: FrontendModuleFilter): String {
return if (frontendModuleFilter.isModuleIncluded(moduleName)) APP_CLIENT_JAR else APP_JAR
@@ -53,9 +53,7 @@ object PlatformJarNames {
const val TEST_FRAMEWORK_JAR: String = "testFramework.jar"
/**
* The custom filesystem implementations for:
* * Fleet and FSD
* * IJent on WSL
* The custom filesystem implementations for Eel and Fleet/FSD.
*/
const val PLATFORM_CORE_NIO_FS: String = "nio-fs.jar"
}
}

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.ReviseWhenPortedToJDK
@@ -98,6 +98,6 @@ object VmOptionsGenerator {
}
internal fun writeVmOptions(file: Path, vmOptions: Sequence<String>, separator: String) {
Files.writeString(file, vmOptions.joinToString(separator = separator, postfix = separator), StandardCharsets.US_ASCII)
Files.writeString(file, vmOptions.joinToString(separator, postfix = separator), StandardCharsets.US_ASCII)
}
}

View File

@@ -128,7 +128,7 @@ internal class WindowsDistributionBuilder(
val productJsonDir = Files.createTempDirectory(context.paths.tempDir, "win-product-info")
val productJsonFile = writeProductJsonFile(productJsonDir, arch)
val installationDirectories = listOf(context.paths.distAllDir, osAndArchSpecificDistPath, runtimeDir)
validateProductJson(jsonText = productJsonFile.readText(), relativePathToProductJson = "", installationDirectories, installationArchives = emptyList(), context)
validateProductJson(jsonText = productJsonFile.readText(), installationDirectories, installationArchives = emptyList(), context)
launch(Dispatchers.IO + CoroutineName("build Windows ${arch.dirName} installer")) {
exePath = buildNsisInstaller(osAndArchSpecificDistPath, additionalDirectoryToInclude = productJsonDir, suffix(arch), customizer, runtimeDir, context)
}
@@ -263,7 +263,7 @@ internal class WindowsDistributionBuilder(
else {
zip(targetFile = targetFile, dirs = dirMap, addDirEntriesMode = AddDirEntriesMode.NONE)
}
checkInArchive(archiveFile = targetFile, pathInArchive = zipPrefix, context = context)
validateProductJson(archiveFile = targetFile, pathInArchive = zipPrefix, context = context)
context.notifyArtifactBuilt(targetFile)
targetFile
}

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.productInfo
import com.intellij.platform.buildData.productInfo.*
@@ -6,7 +6,10 @@ import com.jetbrains.plugin.structure.base.utils.isDirectory
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.jetbrains.intellij.build.*
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuiltinModulesFileData
import org.jetbrains.intellij.build.JvmArchitecture
import org.jetbrains.intellij.build.OsFamily
import org.jetbrains.intellij.build.impl.Git
import org.jetbrains.intellij.build.impl.client.ADDITIONAL_EMBEDDED_CLIENT_VM_OPTIONS
import org.jetbrains.intellij.build.impl.client.createFrontendContextForLaunchers
@@ -28,7 +31,7 @@ internal val jsonEncoder: Json by lazy {
}
/**
* Generates product-info.json file containing meta-information about product installation.
* Generates a `product-info.json` file describing product installation.
*/
internal fun generateProductInfoJson(
relativePathToBin: String,
@@ -59,10 +62,8 @@ internal fun generateProductInfoJson(
customProperties = listOfNotNull(generateGitRevisionProperty(context)) + productProperties.generateCustomPropertiesForProductInfo(),
bundledPlugins = builtinModules?.plugins ?: emptyList(),
fileExtensions = builtinModules?.fileExtensions ?: emptyList(),
modules = (builtinModules?.layout?.asSequence() ?: emptySequence()).filter { it.kind == ProductInfoLayoutItemKind.pluginAlias }.map { it.name }.toList(),
layout = builtinModules?.layout ?: emptyList(),
flavors = jbrFlavors + productFlavors,
)
return jsonEncoder.encodeToString<ProductInfoData>(json)
@@ -110,4 +111,3 @@ internal suspend fun generateEmbeddedFrontendLaunchData(
)
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 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.productInfo
import com.fasterxml.jackson.databind.ObjectMapper
@@ -11,7 +11,6 @@ import org.apache.commons.compress.archivers.zip.ZipFile
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuildMessages
import org.jetbrains.intellij.build.CompilationContext
import org.jetbrains.intellij.build.OsFamily
import java.nio.channels.FileChannel
import java.nio.file.Files
@@ -19,61 +18,35 @@ import java.nio.file.Path
import java.nio.file.StandardOpenOption
/**
* Checks that product-info.json file located in [archiveFile] archive in [pathInArchive] subdirectory is correct
* Checks that 'product-info.json' file located in [archiveFile] archive in [pathInArchive] subdirectory is correct.
*/
internal fun checkInArchive(archiveFile: Path, pathInArchive: String, context: BuildContext) {
internal fun validateProductJson(archiveFile: Path, pathInArchive: String, context: BuildContext) {
val productJsonPath = joinPaths(pathInArchive, PRODUCT_INFO_FILE_NAME)
val entryData = loadEntry(archiveFile, productJsonPath)
?: throw RuntimeException("Failed to validate product-info.json: cannot find '${productJsonPath}' in ${archiveFile}")
validateProductJson(jsonText = entryData.decodeToString(),
relativePathToProductJson = "",
installationDirectories = emptyList(),
installationArchives = listOf(archiveFile to pathInArchive),
context = context)
val entryData = loadEntry(archiveFile, productJsonPath) ?: throw RuntimeException("Failed to validate product-info.json: cannot find '${productJsonPath}' in ${archiveFile}")
validateProductJson(jsonText = entryData.decodeToString(), installationDirectories = emptyList(), installationArchives = listOf(archiveFile to pathInArchive), context)
}
/**
* Checks that product-info.json file is correct.
* Checks that the 'product-info.json' file is correct.
*
* @param installationDirectories directories which will be included in the product installation
* @param installationArchives archives which will be unpacked and included in the product installation
* (the first part specifies a path to the archive, the second part - a path inside the archive)
*/
internal fun validateProductJson(jsonText: String,
relativePathToProductJson: String,
installationDirectories: List<Path>,
installationArchives: List<Pair<Path, String>>,
context: CompilationContext) {
val schemaPath = context.paths.communityHomeDir
.resolve("platform/buildData/resources/product-info.schema.json")
val messages = context.messages
verifyJsonBySchema(jsonText, schemaPath, messages)
internal fun validateProductJson(jsonText: String, installationDirectories: List<Path>, installationArchives: List<Pair<Path, String>>, context: BuildContext) {
val schemaPath = context.paths.communityHomeDir.resolve("platform/buildData/resources/product-info.schema.json")
verifyJsonBySchema(jsonText, schemaPath, context.messages)
val productJson = jsonEncoder.decodeFromString<ProductInfoData>(jsonText)
checkFileExists(path = productJson.svgIconPath,
description = "svg icon",
relativePathToProductJson = relativePathToProductJson,
installationDirectories = installationDirectories,
installationArchives = installationArchives)
checkFileExists(productJson.svgIconPath, description = "svg icon", installationDirectories, installationArchives)
for (item in productJson.launch) {
val os = item.os
check(OsFamily.ALL.any { it.osName == os }) {
"Incorrect OS name '${os}' in ${relativePathToProductJson}/${PRODUCT_INFO_FILE_NAME}"
"Incorrect OS name '${os}' in ${PRODUCT_INFO_FILE_NAME}"
}
checkFileExists(path = item.launcherPath,
description = "${os} launcher",
relativePathToProductJson = relativePathToProductJson,
installationDirectories = installationDirectories,
installationArchives = installationArchives)
checkFileExists(path = item.javaExecutablePath,
description = "${os} java executable",
relativePathToProductJson = relativePathToProductJson,
installationDirectories = installationDirectories,
installationArchives = installationArchives)
checkFileExists(path = item.vmOptionsFilePath,
description = "${os} VM options file",
relativePathToProductJson = relativePathToProductJson,
installationDirectories = installationDirectories,
installationArchives = installationArchives)
checkFileExists(item.launcherPath, description = "${os} launcher", installationDirectories, installationArchives)
checkFileExists(item.javaExecutablePath, description = "${os} java executable", installationDirectories, installationArchives)
checkFileExists(item.vmOptionsFilePath, description = "${os} VM options file", installationDirectories, installationArchives)
}
}
@@ -81,40 +54,36 @@ private fun verifyJsonBySchema(jsonData: String, jsonSchemaFile: Path, messages:
val schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7).getSchema(Files.readString(jsonSchemaFile))
val errors = schema.validate(ObjectMapper().readTree(jsonData))
if (!errors.isEmpty()) {
messages.error("Unable to validate JSON against $jsonSchemaFile:" +
"\n${errors.joinToString(separator = "\n")}\njson file content:\n$jsonData")
messages.error("Unable to validate JSON against ${jsonSchemaFile}:\n${errors.joinToString("\n")}\nfile content:\n${jsonData}")
}
}
private fun checkFileExists(path: String?,
description: String,
relativePathToProductJson: String,
installationDirectories: List<Path>,
installationArchives: List<Pair<Path, String>>) {
private fun checkFileExists(path: String?, description: String, installationDirectories: List<Path>, installationArchives: List<Pair<Path, String>>) {
if (path == null) {
return
}
val pathFromProductJson = relativePathToProductJson + path
if (installationDirectories.none { Files.exists(it.resolve(pathFromProductJson)) } &&
installationArchives.none { archiveContainsEntry(it.first, joinPaths(it.second, pathFromProductJson)) }) {
throw RuntimeException("Incorrect path to $description '$path' in $relativePathToProductJson/product-info.json: " +
"the specified file doesn't exist in directories $installationDirectories " +
"and archives ${installationArchives.map { "${it.first}/${it.second}" }}")
val pathFromProductJson = path
if (
installationDirectories.none { Files.exists(it.resolve(pathFromProductJson)) } &&
installationArchives.none { archiveContainsEntry(it.first, joinPaths(it.second, pathFromProductJson)) }
) {
throw RuntimeException(
"Incorrect path to ${description} '${path}' in product-info.json:" +
" the specified file doesn't exist neither in directories ${installationDirectories}" +
" nor in archives ${installationArchives.map { "${it.first}/${it.second}" }}")
}
}
private fun joinPaths(parent: String, child: String): String {
return FileUtilRt.toCanonicalPath(/* path = */ "$parent/$child", /* separatorChar = */ '/', /* removeLastSlash = */ true)
.dropWhile { it == '/' }
}
private fun joinPaths(parent: String, child: String): String =
FileUtilRt.toCanonicalPath(/*path =*/ "${parent}/${child}", /*separatorChar =*/ '/', /*removeLastSlash =*/ true).dropWhile { it == '/' }
private fun archiveContainsEntry(archiveFile: Path, entryPath: String): Boolean {
val fileName = archiveFile.fileName.toString()
if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) {
// don't use ImmutableZipFile - archive maybe more than 2GB
FileChannel.open(archiveFile, StandardOpenOption.READ).use { channel ->
ZipFile(channel).use {
ZipFile.Builder().setSeekableByteChannel(channel).get().use {
return it.getEntry(entryPath) != null
}
}
@@ -134,13 +103,12 @@ private fun archiveContainsEntry(archiveFile: Path, entryPath: String): Boolean
return false
}
private fun loadEntry(archiveFile: Path, entryPath: String): ByteArray? {
val fileName = archiveFile.fileName.toString()
if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) {
// don't use ImmutableZipFile - archive maybe more than 2GB
FileChannel.open(archiveFile, StandardOpenOption.READ).use { channel ->
ZipFile(channel).use {
ZipFile.Builder().setSeekableByteChannel(channel).get().use {
return it.getInputStream(it.getEntry(entryPath)).readAllBytes()
}
}

View File

@@ -1,17 +1,12 @@
// 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 com.intellij.platform.buildData.productInfo.CustomCommandLaunchData
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.JvmArchitecture
import org.jetbrains.intellij.build.OsFamily
import com.intellij.platform.buildData.productInfo.CustomCommandLaunchData
internal fun generateQodanaLaunchData(
ideContext: BuildContext,
arch: JvmArchitecture,
os: OsFamily,
): CustomCommandLaunchData? {
internal fun generateQodanaLaunchData(ideContext: BuildContext, arch: JvmArchitecture, os: OsFamily): CustomCommandLaunchData? {
val qodanaProductProperties = ideContext.productProperties.qodanaProductProperties ?: return null
val vmOptions = ideContext.getAdditionalJvmArguments(os, arch, isQodana = true) + qodanaProductProperties.getAdditionalVmOptions(ideContext)
return CustomCommandLaunchData(

View File

@@ -1,8 +1,7 @@
// 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 com.intellij.platform.buildScripts.testFramework
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.NioFiles
import com.intellij.platform.buildScripts.testFramework.binaryReproducibility.BuildArtifactsReproducibilityTest
import com.intellij.platform.runtime.product.ProductMode
@@ -39,16 +38,15 @@ fun createBuildOptionsForTest(productProperties: ProductProperties, homeDir: Pat
jarCacheDir = homeDir.resolve("out/dev-run/jar-cache"),
compressZipFiles = false,
)
customizeBuildOptionsForTest(options = options, outDir = outDir, skipDependencySetup = skipDependencySetup, testInfo = testInfo)
customizeBuildOptionsForTest(options, outDir, skipDependencySetup, testInfo)
return options
}
fun createTestBuildOutDir(productProperties: ProductProperties): Path {
return FileUtil.createTempDirectory("test-build-${productProperties.baseFileName}", null, false).toPath()
}
fun createTestBuildOutDir(productProperties: ProductProperties): Path =
Files.createTempDirectory("test-build-${productProperties.baseFileName}")
private inline fun createBuildOptionsForTest(productProperties: ProductProperties, homeDir: Path, testInfo: TestInfo, customizer: (BuildOptions) -> Unit): BuildOptions {
val options = createBuildOptionsForTest(productProperties = productProperties, homeDir = homeDir, testInfo = testInfo)
val options = createBuildOptionsForTest(productProperties, homeDir, testInfo = testInfo)
customizer(options)
return options
}
@@ -56,10 +54,7 @@ private inline fun createBuildOptionsForTest(productProperties: ProductPropertie
fun customizeBuildOptionsForTest(options: BuildOptions, outDir: Path, skipDependencySetup: Boolean = false, testInfo: TestInfo?) {
options.skipDependencySetup = skipDependencySetup
options.isTestBuild = true
/**
* Differs from [SnapshotBuildNumber] to closer match the production
*/
options.buildNumber = "${SnapshotBuildNumber.BASE}.1"
options.buildNumber = "${SnapshotBuildNumber.BASE}.1" // differs from [SnapshotBuildNumber] to closer match the production
options.buildStepsToSkip += listOf(
BuildOptions.LIBRARY_URL_CHECK_STEP,
BuildOptions.TEAMCITY_ARTIFACTS_PUBLICATION_STEP,
@@ -85,56 +80,46 @@ suspend inline fun createBuildContext(
buildTools: ProprietaryBuildTools = ProprietaryBuildTools.DUMMY,
buildOptionsCustomizer: (BuildOptions) -> Unit = {},
): BuildContext {
val options = createBuildOptionsForTest(productProperties = productProperties, homeDir = homeDir)
val options = createBuildOptionsForTest(productProperties, homeDir)
buildOptionsCustomizer(options)
return BuildContextImpl.createContext(projectHome = homeDir, productProperties = productProperties, proprietaryBuildTools = buildTools, options = options)
return BuildContextImpl.createContext(homeDir, productProperties, proprietaryBuildTools = buildTools, options = options)
}
// don't expose BuildDependenciesCommunityRoot
fun runTestBuild(
homePath: Path,
productProperties: ProductProperties,
testInfo: TestInfo,
buildTools: ProprietaryBuildTools,
testInfo: TestInfo,
buildOptionsCustomizer: (BuildOptions) -> Unit = {},
) {
runTestBuild(
homeDir = homePath,
productProperties = productProperties,
buildTools = buildTools,
testInfo = testInfo,
isReproducibilityTestAllowed = true,
buildOptionsCustomizer = buildOptionsCustomizer,
)
runTestBuild(homePath, productProperties, testInfo, buildTools, isReproducibilityTestAllowed = true, buildOptionsCustomizer = buildOptionsCustomizer)
}
fun runTestBuild(
homeDir: Path,
productProperties: ProductProperties,
buildTools: ProprietaryBuildTools = ProprietaryBuildTools.DUMMY,
testInfo: TestInfo,
buildTools: ProprietaryBuildTools = ProprietaryBuildTools.DUMMY,
isReproducibilityTestAllowed: Boolean = true,
build: suspend (BuildContext) -> Unit = { buildDistributions(it) },
build: suspend (BuildContext) -> Unit = { buildDistributions(context = it) },
onSuccess: suspend (BuildContext) -> Unit = {},
buildOptionsCustomizer: (BuildOptions) -> Unit = {}
) = runBlocking(Dispatchers.Default) {
): Unit = runBlocking(Dispatchers.Default) {
if (isReproducibilityTestAllowed && BuildArtifactsReproducibilityTest.isEnabled) {
val reproducibilityTest = BuildArtifactsReproducibilityTest()
repeat(reproducibilityTest.iterations) { iterationNumber ->
launch {
doRunTestBuild(
context = {
BuildContextImpl.createContext(
projectHome = homeDir,
productProperties = productProperties,
proprietaryBuildTools = buildTools,
setupTracer = false,
options = createBuildOptionsForTest(productProperties = productProperties, homeDir = homeDir, testInfo = testInfo, customizer = buildOptionsCustomizer).also {
reproducibilityTest.configure(it)
},
)
},
traceSpanName = "${testInfo.spanName}#$iterationNumber",
context = BuildContextImpl.createContext(
homeDir,
productProperties,
setupTracer = false,
buildTools,
createBuildOptionsForTest(productProperties, homeDir, testInfo, buildOptionsCustomizer).also {
reproducibilityTest.configure(it)
}
),
traceSpanName = "${testInfo.spanName}#${iterationNumber}",
writeTelemetry = false,
build = { context ->
build(context)
@@ -147,15 +132,13 @@ fun runTestBuild(
}
else {
doRunTestBuild(
context = {
BuildContextImpl.createContext(
projectHome = homeDir,
productProperties = productProperties,
proprietaryBuildTools = buildTools,
setupTracer = false,
options = createBuildOptionsForTest(productProperties = productProperties, homeDir = homeDir, testInfo = testInfo, customizer = buildOptionsCustomizer),
)
},
context = BuildContextImpl.createContext(
homeDir,
productProperties,
setupTracer = false,
buildTools,
createBuildOptionsForTest(productProperties, homeDir, testInfo, buildOptionsCustomizer),
),
writeTelemetry = true,
traceSpanName = testInfo.spanName,
build = { context ->
@@ -172,18 +155,17 @@ suspend fun runTestBuild(
context: suspend () -> BuildContext,
build: suspend (BuildContext) -> Unit = { buildDistributions(it) }
) {
doRunTestBuild(context = context, traceSpanName = testInfo.spanName, writeTelemetry = true, build = build)
doRunTestBuild(context(), traceSpanName = testInfo.spanName, writeTelemetry = true, build = build)
}
private val defaultLogFactory = Logger.getFactory()
private suspend fun doRunTestBuild(context: suspend () -> BuildContext, traceSpanName: String, writeTelemetry: Boolean, build: suspend (context: BuildContext) -> Unit) {
private suspend fun doRunTestBuild(context: BuildContext, traceSpanName: String, writeTelemetry: Boolean, build: suspend (context: BuildContext) -> Unit) {
var outDir: Path? = null
var traceFile: Path? = null
var error: Throwable? = null
try {
spanBuilder(traceSpanName).use { span ->
val context = context()
context.cleanupJarCache()
outDir = context.paths.buildOutputDir
span.setAttribute("outDir", outDir.toString())

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 com.intellij.platform.buildData.productInfo
import kotlinx.serialization.Serializable
@@ -30,12 +30,11 @@ class ProductInfoData private constructor(
val launch: List<ProductInfoLaunchData>,
val customProperties: List<CustomProperty> = emptyList(),
val bundledPlugins: List<String> = emptyList(),
// it is not modules, but plugin aliases
val modules: List<String> = emptyList(),
val modules: List<String> = emptyList(), // actually, it is not modules but plugin aliases
val fileExtensions: List<String> = emptyList(),
val flavors: List<ProductFlavorData> = emptyList(),
// not used by launcher, specify in the end
// not used by the launcher; must be at the end
@ApiStatus.Internal
val layout: List<ProductInfoLayoutItem> = emptyList(),
) {
@@ -64,26 +63,10 @@ class ProductInfoData private constructor(
fileExtensions: List<String>,
flavors: List<ProductFlavorData>,
layout: List<ProductInfoLayoutItem>,
): ProductInfoData {
return ProductInfoData(
name = name,
version = version,
versionSuffix = versionSuffix,
buildNumber = buildNumber,
productCode = productCode,
envVarBaseName = envVarBaseName,
dataDirectoryName = dataDirectoryName,
svgIconPath = svgIconPath,
productVendor = productVendor,
launch = launch,
customProperties = customProperties,
bundledPlugins = bundledPlugins,
modules = modules,
fileExtensions = fileExtensions,
flavors = flavors,
layout = layout,
)
}
): ProductInfoData = ProductInfoData(
name, version, versionSuffix, buildNumber, productCode, envVarBaseName, dataDirectoryName, svgIconPath, productVendor, launch,
customProperties, bundledPlugins, modules, fileExtensions, flavors, layout
)
}
}
@@ -125,20 +108,10 @@ class ProductInfoLaunchData private constructor(
mainClass: String,
startupWmClass: String? = null,
customCommands: List<CustomCommandLaunchData> = emptyList(),
): ProductInfoLaunchData {
return ProductInfoLaunchData(
os = os,
arch = arch,
launcherPath = launcherPath,
javaExecutablePath = javaExecutablePath,
vmOptionsFilePath = vmOptionsFilePath,
startupWmClass = startupWmClass,
bootClassPathJarNames = bootClassPathJarNames,
additionalJvmArguments = additionalJvmArguments,
mainClass = mainClass,
customCommands = customCommands
)
}
): ProductInfoLaunchData = ProductInfoLaunchData(
os, arch, launcherPath, javaExecutablePath, vmOptionsFilePath, startupWmClass, bootClassPathJarNames, additionalJvmArguments,
mainClass, customCommands
)
}
}
@@ -157,4 +130,4 @@ class CustomCommandLaunchData @ApiStatus.Internal constructor(
class CustomProperty @ApiStatus.Internal constructor(
val key: String,
val value: String,
)
)

View File

@@ -30,4 +30,4 @@ const val IJENT_REQUIRED_DEFAULT_NIO_FS_PROVIDER_CLASS: String = "com.intellij.p
val MULTI_ROUTING_FILE_SYSTEM_VMOPTIONS: List<String> = listOf(
"-Djava.nio.file.spi.DefaultFileSystemProvider=$IJENT_REQUIRED_DEFAULT_NIO_FS_PROVIDER_CLASS",
)
)

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.pycharm
import com.intellij.openapi.application.PathManager
@@ -39,14 +39,9 @@ class PyCharmCommunityBuildTest {
fun build(testInfo: TestInfo) {
val homePath = PathManager.getHomeDirFor(javaClass)!!
val communityHomePath = BuildDependenciesCommunityRoot(homePath.resolve("community"))
runTestBuild(
homeDir = communityHomePath.communityRoot,
testInfo = testInfo,
productProperties = PyCharmCommunityProperties(communityHomePath.communityRoot),
) {
it.classOutDir = System.getProperty(BuildOptions.PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY)
?: "$homePath/out/classes"
runTestBuild(communityHomePath.communityRoot, PyCharmCommunityProperties(communityHomePath.communityRoot), testInfo) {
it.classOutDir = System.getProperty(BuildOptions.PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY) ?: "${homePath}/out/classes"
stubSkeletons(communityHomePath.communityRoot, it)
}
}
}
}