diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/BuildServer.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/BuildServer.kt index e3ef71f05b23..297331da8e6b 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/BuildServer.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/BuildServer.kt @@ -16,9 +16,6 @@ import org.jetbrains.intellij.build.closeKtorClient import java.nio.file.Files import java.nio.file.Path import java.util.* -import kotlin.io.path.bufferedReader -import kotlin.io.path.exists -import kotlin.io.path.listDirectoryEntries @Serializable internal data class Configuration(@JvmField val products: Map) @@ -35,22 +32,27 @@ private const val CUSTOM_PRODUCT_PROPERTIES_PATH = "idea.product.properties.path @Suppress("SpellCheckingInspection") fun getIdeSystemProperties(runDir: Path): Map { + val result = LinkedHashMap() + + val properties = Properties() + properties.load(Files.newInputStream(runDir.resolve("bin/idea.properties"))) + for (property in properties) { + result.put(property.key.toString(), property.value.toString()) + } + // see BuildContextImpl.getAdditionalJvmArguments - we should somehow deduplicate code val libDir = runDir.resolve("lib") - - val defaultProperties: Map = Properties().apply { - load(runDir.resolve("bin/idea.properties").bufferedReader()) - }.map { it.key.toString() to it.value.toString() }.toMap() - - return defaultProperties.plus( - mapOf( + result.putAll( + listOf( "jna.boot.library.path" to "$libDir/jna/${JvmArchitecture.currentJvmArch.dirName}", "pty4j.preferred.native.folder" to "$libDir/pty4j", // require bundled JNA dispatcher lib "jna.nosys" to "true", "jna.noclasspath" to "true", - "jb.vmOptionsFile" to "${runDir.parent.listDirectoryEntries(glob = "*.vmoptions").singleOrNull()}" - )) + "jb.vmOptionsFile" to "${Files.newDirectoryStream(runDir.parent, "*.vmoptions").use { it.singleOrNull() }}" + ) + ) + return result } /** Returns IDE installation directory */ @@ -80,9 +82,9 @@ private fun createConfiguration(productionClassOutput: Path, homePath: Path): Co } private fun getProductPropertiesPath(homePath: Path): Path { - // Handle custom product properties path + // handle a custom product properties path val customPath = System.getProperty(CUSTOM_PRODUCT_PROPERTIES_PATH)?.let { homePath.resolve(it) } - if (customPath != null && customPath.exists()) { + if (customPath != null && Files.exists(customPath)) { return customPath } return homePath.resolve(PRODUCTS_PROPERTIES_PATH) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/LinuxDistributionBuilder.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/LinuxDistributionBuilder.kt index a42f04ae60e8..fd7053ae4511 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/LinuxDistributionBuilder.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/LinuxDistributionBuilder.kt @@ -26,9 +26,11 @@ import kotlin.io.path.name import kotlin.io.path.nameWithoutExtension import kotlin.time.Duration.Companion.minutes -class LinuxDistributionBuilder(override val context: BuildContext, - private val customizer: LinuxDistributionCustomizer, - private val ideaProperties: CharSequence?) : OsSpecificDistributionBuilder { +class LinuxDistributionBuilder( + override val context: BuildContext, + private val customizer: LinuxDistributionCustomizer, + private val ideaProperties: CharSequence? +) : OsSpecificDistributionBuilder { private companion object { const val NO_RUNTIME_SUFFIX = "-no-jbr" } @@ -84,12 +86,16 @@ class LinuxDistributionBuilder(override val context: BuildContext, context.executeStep(spanBuilder("build linux .tar.gz").setAttribute("arch", arch.name), BuildOptions.LINUX_ARTIFACTS_STEP) { if (customizer.buildArtifactWithoutRuntime) { launch { - context.executeStep(spanBuilder("Build Linux .tar.gz without bundled Runtime").setAttribute("arch", arch.name), - BuildOptions.LINUX_TAR_GZ_WITHOUT_BUNDLED_RUNTIME_STEP) { - buildTarGz(runtimeDir = null, - unixDistPath = osAndArchSpecificDistPath, - suffix = NO_RUNTIME_SUFFIX + suffix(arch), - arch = arch) + context.executeStep( + spanBuilder("Build Linux .tar.gz without bundled Runtime").setAttribute("arch", arch.name), + BuildOptions.LINUX_TAR_GZ_WITHOUT_BUNDLED_RUNTIME_STEP + ) { + buildTarGz( + runtimeDir = null, + unixDistPath = osAndArchSpecificDistPath, + suffix = NO_RUNTIME_SUFFIX + suffix(arch), + arch = arch + ) } } } @@ -111,10 +117,12 @@ class LinuxDistributionBuilder(override val context: BuildContext, val tempTar = Files.createTempDirectory(context.paths.tempDir, "tar-") try { unTar(tarGzPath, tempTar) - RepairUtilityBuilder.generateManifest(context = context, - unpackedDistribution = tempTar.resolve(rootDirectoryName), - os = OsFamily.LINUX, - arch = arch) + RepairUtilityBuilder.generateManifest( + context = context, + unpackedDistribution = tempTar.resolve(rootDirectoryName), + os = OsFamily.LINUX, + arch = arch + ) } finally { NioFiles.deleteRecursively(tempTar) @@ -135,16 +143,19 @@ class LinuxDistributionBuilder(override val context: BuildContext, val fullName = context.applicationInfo.fullProductName val sourceFile = context.paths.communityHomeDir.resolve("platform/build-scripts/resources/linux/Install-Linux-tar.txt") val targetFile = unixDistPath.resolve("Install-Linux-tar.txt") - substituteTemplatePlaceholders(sourceFile, targetFile, "@@", listOf( + substituteTemplatePlaceholders( + sourceFile, targetFile, "@@", listOf( "product_full" to fullName, "product" to context.productProperties.baseFileName, "product_vendor" to context.applicationInfo.shortCompanyName, "system_selector" to context.systemSelector - ), convertToUnixLineEndings = true) + ), convertToUnixLineEndings = true + ) } - override fun generateExecutableFilesPatterns(includeRuntime: Boolean, arch: JvmArchitecture): List = - customizer.generateExecutableFilesPatterns(context, includeRuntime, arch) + override fun generateExecutableFilesPatterns(includeRuntime: Boolean, arch: JvmArchitecture): List { + return customizer.generateExecutableFilesPatterns(context, includeRuntime, arch) + } private val rootDirectoryName: String get() = customizer.getRootDirectoryName(context.applicationInfo, context.buildNumber) @@ -161,18 +172,24 @@ class LinuxDistributionBuilder(override val context: BuildContext, check(Files.exists(runtimeDir.resolve(javaExecutablePath))) { "$javaExecutablePath was not found under $runtimeDir" } } - val productJsonDir = context.paths.tempDir.resolve("linux.dist.product-info.json${suffix}") - generateProductJson(productJsonDir, context, arch, withRuntime = runtimeDir != null) + val productJsonDir = context.paths.tempDir.resolve("linux.dist.product-info.json$suffix") + generateProductJson(targetDir = productJsonDir, context = context, arch = arch, withRuntime = runtimeDir != null) dirs.add(productJsonDir) spanBuilder("build Linux tar.gz") .setAttribute("runtimeDir", runtimeDir?.toString() ?: "") .useWithScope { - val executableFileMatchers = generateExecutableFilesMatchers(runtimeDir != null, arch).keys - tar(tarPath, tarRoot, dirs, executableFileMatchers, context.options.buildDateInSeconds) + val executableFileMatchers = generateExecutableFilesMatchers(includeRuntime = runtimeDir != null, arch = arch).keys + tar( + archive = tarPath, + rootDir = tarRoot, + directories = dirs, + executableFileMatchers = executableFileMatchers, + buildDateInSeconds = context.options.buildDateInSeconds, + ) checkInArchive(tarPath, tarRoot, context) context.notifyArtifactBuilt(tarPath) - checkExecutablePermissions(tarPath, rootDirectoryName, includeRuntime = runtimeDir != null, arch = arch) + checkExecutablePermissions(distribution = tarPath, root = rootDirectoryName, includeRuntime = runtimeDir != null, arch = arch) } tarPath } @@ -228,7 +245,8 @@ class LinuxDistributionBuilder(override val context: BuildContext, ) copyFile(iconPngPath, snapDir.resolve("$snapName.png")) val snapcraftTemplate = context.paths.communityHomeDir.resolve( - "platform/build-scripts/resources/linux/snap/snapcraft-template.yaml") + "platform/build-scripts/resources/linux/snap/snapcraft-template.yaml" + ) val snapcraftConfig = snapDir.resolve("snapcraft.yaml") substituteTemplatePlaceholders( inputFile = snapcraftTemplate, @@ -243,19 +261,23 @@ class LinuxDistributionBuilder(override val context: BuildContext, "SCRIPT" to "bin/${context.productProperties.baseFileName}.sh" ) ) - context.messages.info(""" + context.messages.info( + """ |# <${snapcraftConfig.name}> |${Files.readString(snapcraftConfig)} |# - """.trimMargin()) + """.trimMargin() + ) val productJsonDir = context.paths.tempDir.resolve("linux.dist.snap.product-info.json") val jsonText = generateProductJson(productJsonDir, context, arch) - validateProductJson(jsonText = jsonText, - relativePathToProductJson = "", - installationDirectories = listOf(context.paths.distAllDir, unixDistPath, runtimeDir), - installationArchives = listOf(), - context = context) + validateProductJson( + jsonText = jsonText, + relativePathToProductJson = "", + installationDirectories = listOf(context.paths.distAllDir, unixDistPath, runtimeDir), + installationArchives = listOf(), + context = context + ) val resultDir = snapDir.resolve("result") Files.createDirectories(resultDir) span.addEvent("build package") @@ -317,17 +339,21 @@ private fun generateProductJson(targetDir: Path, context: BuildContext, arch: Jv val json = generateProductInfoJson( relativePathToBin = "bin", builtinModules = context.builtinModule, - launch = listOf(ProductInfoLaunchData( - os = OsFamily.LINUX.osName, - arch = arch.dirName, - launcherPath = "bin/${context.productProperties.baseFileName}.sh", - javaExecutablePath = if (withRuntime) "jbr/bin/java" else null, - vmOptionsFilePath = "bin/${context.productProperties.baseFileName}64.vmoptions", - startupWmClass = getLinuxFrameClass(context), - bootClassPathJarNames = context.bootClassPathJarNames, - additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.LINUX, arch), - mainClass = context.ideMainClassName)), - context) + launch = listOf( + ProductInfoLaunchData( + os = OsFamily.LINUX.osName, + arch = arch.dirName, + launcherPath = "bin/${context.productProperties.baseFileName}.sh", + javaExecutablePath = if (withRuntime) "jbr/bin/java" else null, + vmOptionsFilePath = "bin/${context.productProperties.baseFileName}64.vmoptions", + startupWmClass = getLinuxFrameClass(context), + bootClassPathJarNames = context.bootClassPathJarNames, + additionalJvmArguments = context.getAdditionalJvmArguments(OsFamily.LINUX, arch), + mainClass = context.ideMainClassName, + ) + ), + context + ) writeProductInfoJson(targetDir.resolve(PRODUCT_INFO_FILE_NAME), json, context) return json } @@ -348,10 +374,12 @@ private fun generateScripts(distBinDir: Path, arch: JvmArchitecture, context: Bu for (file in it) { val fileName = file.fileName.toString() if (fileName != EXECUTABLE_TEMPLATE_NAME) { - copyScript(sourceFile = file, - targetFile = distBinDir.resolve(fileName), - additionalTemplateValues = emptyList(), - context = context) + copyScript( + sourceFile = file, + targetFile = distBinDir.resolve(fileName), + additionalTemplateValues = emptyList(), + context = context + ) } } } @@ -398,16 +426,20 @@ private fun generateMainScript(distBinDir: Path, arch: JvmArchitecture, addition val sourceScriptDir = context.paths.communityHomeDir.resolve("platform/build-scripts/resources/linux/scripts") - copyScript(sourceFile = sourceScriptDir.resolve(EXECUTABLE_TEMPLATE_NAME), - targetFile = distBinDir.resolve(context.productProperties.mainScriptFileName), - additionalTemplateValues = additionalTemplateValues, - context = context) + copyScript( + sourceFile = sourceScriptDir.resolve(EXECUTABLE_TEMPLATE_NAME), + targetFile = distBinDir.resolve(context.productProperties.mainScriptFileName), + additionalTemplateValues = additionalTemplateValues, + context = context + ) } -private fun copyScript(sourceFile: Path, - targetFile: Path, - additionalTemplateValues: List>, - context: BuildContext) { +private fun copyScript( + sourceFile: Path, + targetFile: Path, + additionalTemplateValues: List>, + context: BuildContext +) { // Until CR (\r) will be removed from the repository checkout, we need to filter it out from Unix-style scripts // https://youtrack.jetbrains.com/issue/IJI-526/Force-git-to-use-LF-line-endings-in-working-copy-of-via-gitattri substituteTemplatePlaceholders( @@ -431,8 +463,10 @@ private fun writeLinuxVmOptions(distBinDir: Path, context: BuildContext): Path { @Suppress("SpellCheckingInspection") val vmOptions = VmOptionsGenerator.computeVmOptions(context) + - listOf("-Dsun.tools.attach.tmp.only=true", - "-Dawt.lock.fair=true") + listOf( + "-Dsun.tools.attach.tmp.only=true", + "-Dawt.lock.fair=true" + ) writeVmOptions(file = vmOptionsPath, vmOptions = vmOptions, separator = "\n") return vmOptionsPath diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/productInfo/ProductInfoGenerator.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/productInfo/ProductInfoGenerator.kt index 911ea9216f29..915ec250d1ff 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/productInfo/ProductInfoGenerator.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/productInfo/ProductInfoGenerator.kt @@ -1,10 +1,10 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.intellij.build.impl.productInfo import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer import org.jetbrains.intellij.build.BuildContext import org.jetbrains.intellij.build.BuiltinModulesFileData import java.nio.file.Files @@ -12,10 +12,10 @@ import java.nio.file.Path import java.nio.file.attribute.FileTime import java.util.concurrent.TimeUnit -const val PRODUCT_INFO_FILE_NAME = "product-info.json" +const val PRODUCT_INFO_FILE_NAME: String = "product-info.json" @OptIn(ExperimentalSerializationApi::class) -internal val jsonEncoder by lazy { +internal val jsonEncoder: Json by lazy { Json { prettyPrint = true prettyPrintIndent = " " @@ -27,10 +27,12 @@ internal val jsonEncoder by lazy { /** * Generates product-info.json file containing meta-information about product installation. */ -internal fun generateProductInfoJson(relativePathToBin: String, - builtinModules: BuiltinModulesFileData?, - launch: List, - context: BuildContext): String { +internal fun generateProductInfoJson( + relativePathToBin: String, + builtinModules: BuiltinModulesFileData?, + launch: List, + context: BuildContext, +): String { val appInfo = context.applicationInfo val jbrFlavors = if (launch.any { it.javaExecutablePath != null } && context.bundledRuntime.build.startsWith("21.")) { listOf(ProductFlavorData("jbr21")) @@ -55,7 +57,7 @@ internal fun generateProductInfoJson(relativePathToBin: String, modules = builtinModules?.modules ?: emptyList(), flavors = jbrFlavors + productFlavors ) - return jsonEncoder.encodeToString(serializer(), json) + return jsonEncoder.encodeToString(json) } internal fun writeProductInfoJson(targetFile: Path, json: String, context: BuildContext) { diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/tar.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/tar.kt index 9f4606a9db11..338d10946527 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/tar.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/tar.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.intellij.build.impl import com.intellij.openapi.util.SystemInfoRt @@ -6,17 +6,15 @@ import com.intellij.util.io.Compressor import com.intellij.util.io.Decompressor import com.intellij.util.io.PosixFilePermissionsUtil import java.io.InputStream - import java.nio.file.Files import java.nio.file.Path import java.nio.file.PathMatcher import java.nio.file.attribute.PosixFilePermission import java.util.concurrent.TimeUnit -import kotlin.io.path.isDirectory fun tar(archive: Path, rootDir: String, directories: List, executableFileMatchers: Collection, buildDateInSeconds: Long) { val normalizedRootDir = rootDir.removeSuffix("/") - FileSystemIndependentTarGzCompressor(archive) { entryName: String, fileSystemMode: Int -> + FileSystemIndependentTarGzCompressor(archive) { entryName, fileSystemMode -> val relativePath = Path.of(entryName.removePrefix(rootDir).removePrefix("/")) if (executableFileMatchers.any { it.matches(relativePath) } || PosixFilePermission.OWNER_EXECUTE in PosixFilePermissionsUtil.fromUnixMode(fileSystemMode)) { @@ -24,21 +22,25 @@ fun tar(archive: Path, rootDir: String, directories: List, executableFileM } else 0 }.use { compressor -> - directories.forEach { - require(it.isDirectory()) - compressor.addDirectory(normalizedRootDir, it, buildDateInSeconds) + for (dir in directories) { + require(Files.isDirectory(dir)) + compressor.addDirectory(normalizedRootDir, dir, buildDateInSeconds) } } } -private class FileSystemIndependentTarGzCompressor(archive: Path, val mode: (String, Int) -> Int) : Compressor.Tar(archive, - Compression.GZIP) { - override fun writeFileEntry(name: String, - source: InputStream, - length: Long, - timestamp: Long, - fileSystemMode: Int, - symlinkTarget: String?) { +private class FileSystemIndependentTarGzCompressor( + archive: Path, + @JvmField val mode: (String, Int) -> Int, +) : Compressor.Tar(archive, Compression.GZIP) { + override fun writeFileEntry( + name: String, + source: InputStream, + length: Long, + timestamp: Long, + fileSystemMode: Int, + symlinkTarget: String?, + ) { super.writeFileEntry(name, source, length, timestamp, mode(name, fileSystemMode), symlinkTarget) } }