rider/build.gradle.kts (507 lines of code) (raw):

import com.jetbrains.plugin.structure.base.utils.isFile import com.jetbrains.plugin.structure.base.utils.listFiles import com.ullink.gradle.nunit.NUnit import groovy.xml.XmlParser import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.ChangelogPluginExtension import org.jetbrains.intellij.platform.gradle.Constants import org.jetbrains.intellij.platform.gradle.tasks.PatchPluginXmlTask import org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.util.* import kotlin.io.path.* plugins { id("com.ullink.nuget") version "2.23" id("com.ullink.nunit") version "2.8" id("me.filippov.gradle.jvm.wrapper") id("org.jetbrains.changelog") version "2.0.0" id("org.jetbrains.intellij.platform") kotlin("jvm") } repositories { maven("https://cache-redirector.jetbrains.com/intellij-dependencies") maven("https://cache-redirector.jetbrains.com/intellij-repository/releases") maven("https://cache-redirector.jetbrains.com/intellij-repository/snapshots") maven("https://cache-redirector.jetbrains.com/maven-central") intellijPlatform { defaultRepositories() jetbrainsRuntime() } } apply { plugin("kotlin") } val repoRoot = projectDir.parentFile!! val isWindows = System.getProperty("os.name").lowercase(Locale.getDefault()).startsWith("win") val bundledRiderSdkRoot = File(projectDir, "build/rider") // SDK from TC configuration/artifacts val bundledMavenArtifacts = File(projectDir, "build/maven-artifacts") val productVersion = extra["productVersion"].toString() val maintenanceVersion = extra["maintenanceVersion"].toString() val pluginVersion = "$productVersion.$maintenanceVersion" val buildCounter = extra["BuildCounter"].toString() val releaseConfiguration = "Release" val isAutomatedBuild = System.getenv("TEAMCITY_VERSION") != null val buildConfiguration = if (isAutomatedBuild) { releaseConfiguration } else { extra["BuildConfiguration"] } val isReleaseBuild = buildConfiguration == releaseConfiguration val warningsAsErrors = extra["warningsAsErrors"].toString().lowercase(Locale.getDefault()).toBoolean() val modelSrcDir = File(repoRoot, "rider/protocol/src/main/kotlin/model") val hashBaseDir = File(repoRoot, "rider/build/rdgen") val skipDotnet = extra["skipDotnet"].toString().lowercase(Locale.getDefault()).toBoolean() val runTests = extra["RunTests"].toString().lowercase(Locale.getDefault()).toBoolean() val isMonorepo = rootProject.projectDir != projectDir val dotnetDllFiles = files( "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Shaders.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Shaders.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Json.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Json.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Json.Rider.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Json.Rider.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Yaml.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Yaml.pdb", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Yaml.Rider.dll", "../resharper/build/Unity/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Yaml.Rider.pdb" ) val debuggerDllFiles = files( "../resharper/build/debugger/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.dll", "../resharper/build/debugger/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.pdb" ) val textureDebuggerDllFiles = files( "../resharper/build/texture-debugger/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.Presentation.Texture.dll", "../resharper/build/texture-debugger/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.Presentation.Texture.pdb" ) val pausePointDllFiles = files( "../resharper/build/pausepoint-helper/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.PausePoint.Helper.dll", "../resharper/build/pausepoint-helper/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Debugger.PausePoint.Helper.pdb" ) val listIosUsbDevicesFiles = files( "../resharper/build/ios-list-usb-devices/bin/$buildConfiguration/net7.0/JetBrains.Rider.Unity.ListIosUsbDevices.dll", "../resharper/build/ios-list-usb-devices/bin/$buildConfiguration/net7.0/JetBrains.Rider.Unity.ListIosUsbDevices.pdb", "../resharper/build/ios-list-usb-devices/bin/$buildConfiguration/net7.0/JetBrains.Rider.Unity.ListIosUsbDevices.runtimeconfig.json" ) // EditorPlugin build was removed from github build // there are more versions of EditorPlugin, except 2019.2 //val unityEditorDllFiles = files( // "../unity/build/EditorPlugin.SinceUnity.2019.2/bin/$buildConfiguration/netstandard2.0/JetBrains.Rider.Unity.Editor.Plugin.Net46.Repacked.dll", // "../unity/build/EditorPlugin.SinceUnity.2019.2/bin/$buildConfiguration/netstandard2.0/JetBrains.Rider.Unity.Editor.Plugin.Net46.Repacked.pdb" //) val rdLibDirectory: () -> File = { file(intellijPlatform.platformPath.resolve("lib/rd/")) } val rdModelJarFile: File by lazy { val jarFile = File(rdLibDirectory(), "rider-model.jar").canonicalFile assert(jarFile.isFile) return@lazy jarFile } val backendDir = projectDir.parentFile.resolve("resharper") val resharperHostPluginSolution = backendDir.resolve("resharper-unity.sln") version = "${pluginVersion}.$buildCounter" java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } sourceSets { main { java { srcDir("src/main/gen") srcDir("src/main/rdgen/kotlin") } resources { srcDir("src/main/rdgen/resources") } } } idea { module { generatedSourceDirs.add(file("src/main/rdgen/kotlin")) resourceDirs.add(file("src/main/rdgen/resources")) } } dependencies { intellijPlatform { with(file("build/rider")) { when { exists() -> { logger.lifecycle("*** Using Rider SDK from local path $this") local(this) } else -> { logger.lifecycle("*** Using Rider SDK from intellij-snapshots repository") rider("${productVersion}-SNAPSHOT", useInstaller = false) } } } jetbrainsRuntime() bundledModule("intellij.rider.rdclient.dotnet.spellchecker") bundledModule("intellij.rider.cpp.core") bundledModule("intellij.rider.cpp.core.languages") bundledModule("intellij.rider.hlsl") bundledModule("intellij.css.actions") bundledPlugin("rider.intellij.plugin.appender") bundledPlugin("com.intellij.css") bundledPlugin("org.jetbrains.plugins.yaml") bundledPlugin("com.intellij.modules.json") bundledPlugin("com.jetbrains.dotCover") bundledPlugin("com.intellij.platform.images") // TODO: Temporary I hope hope hope bundledLibrary(provider { project.intellijPlatform.platformPath.resolve("lib/testFramework.jar").pathString }) } } intellijPlatform { pluginConfiguration { name = "rider-unity" } } configure<ChangelogPluginExtension> { val regex = """^((0|[1-9]\d*)\.(0|[1-9]\d*)(\.\d+)?).*$|^Unreleased.*$""".toRegex() version.set(regex.matchEntire(project.version.toString())?.groups?.get(1)?.value) path.set("${project.projectDir}/../CHANGELOG.md") headerParserRegex.set(regex) } fun getChangelogItem(): Changelog.Item { return changelog.getOrNull(pluginVersion) ?: if (changelog.has(changelog.unreleasedTerm.get())) changelog.getUnreleased() else null ?: changelog.getLatest() } logger.lifecycle("Version=$version") logger.lifecycle("BuildConfiguration=$buildConfiguration") val riderModel: Configuration by configurations.creating { isCanBeConsumed = true isCanBeResolved = false } artifacts { add(riderModel.name, provider { intellijPlatform.platformPath.resolve("lib/rd/rider-model.jar").also { check(it.isFile) { "rider-model.jar is not found at $riderModel" } } }) { builtBy(Constants.Tasks.INITIALIZE_INTELLIJ_PLATFORM_PLUGIN) } } tasks { val backendGroup = "backend" val ciGroup = "ci" val testGroup = "verification" val dotNetSdkPath by lazy { val sdkPath = intellijPlatform.platformPath.resolve("lib/DotNetSdkForRdPlugins").absolute() if (sdkPath.isDirectory().not()) error("$sdkPath does not exist or not a directory") println("SDK path: $sdkPath") return@lazy sdkPath } instrumentCode { enabled = false } buildSearchableOptions { enabled = isReleaseBuild } runIde { // Match Rider's default heap size of 1.5Gb (default for runIde is 512Mb) maxHeapSize = "1500m" } val patchPluginXml by named<PatchPluginXmlTask>("patchPluginXml") { changeNotes.set( """ <body> <p><b>New in $pluginVersion</b></p> <p> ${changelog.renderItem(getChangelogItem(), Changelog.OutputType.HTML)} </p> <p>See the <a href="https://github.com/JetBrains/resharper-unity/blob/net221/CHANGELOG.md">CHANGELOG</a> for more details and history.</p> </body>""".trimIndent() ) } val validatePluginXml by registering { group = ciGroup dependsOn(patchPluginXml) val pluginXml = File(repoRoot, "rider/src/main/resources/META-INF/plugin.xml") if (!pluginXml.isFile) throw GradleException("plugin.xml must be a valid file") inputs.file(pluginXml) outputs.file(pluginXml) doLast { val parsed = XmlParser().parse(pluginXml).text() if (parsed.isEmpty()) throw GradleException("plugin.xml cannot be empty") val rawBytes = pluginXml.readBytes() if (rawBytes.isEmpty()) throw GradleException("plugin.xml cannot be empty") if (rawBytes.any { it < 0 }) throw GradleException("plugin.xml cannot contain invalid bytes") logger.lifecycle("$pluginXml.path is valid XML and contains only US-ASCII symbols, bytes: ${rawBytes.size}") } } processResources { dependsOn(validatePluginXml) copy { from("../common/dictionaries/unity.dic") into("src/main/rdgen/resources/com/jetbrains/rider/plugins/unity/spellchecker/") } } create("setDotNetVersionTcParam") { group = ciGroup doLast { println("##teamcity[setParameter name='DotNetVersion' value='$version']") } } val publishCiBuildNumber by registering { group = ciGroup doLast { println("##teamcity[buildNumber '$version']") } } val generateModels = create("generateModels") { dependsOn(":protocol:rdgen") } named<KotlinCompile>("compileKotlin") { dependsOn(generateModels) compilerOptions { freeCompilerArgs.add("-Xjvm-default=all") jvmTarget.set(JvmTarget.JVM_21) allWarningsAsErrors.set(warningsAsErrors) } } named<KotlinCompile>("compileTestKotlin") { compilerOptions { jvmTarget.set(JvmTarget.JVM_21) allWarningsAsErrors.set(warningsAsErrors) } } val prepareRiderBuildProps = register("prepareRiderBuildProps") { val propsFile = File("${project.projectDir}/../resharper/build/generated/DotNetSdkPath.generated.props") group = backendGroup inputs.dir(dotNetSdkPath) outputs.file(propsFile) doLast { val dotNetSdkFile= dotNetSdkPath assert(dotNetSdkFile.isDirectory()) logger.info("Generating :${propsFile.canonicalPath}...") project.file(propsFile).writeText("""<Project> <PropertyGroup> <DotNetSdkPath>${dotNetSdkFile.toRealPath()}</DotNetSdkPath> </PropertyGroup> </Project>""".trimIndent()) } } val prepareNuGetConfig = register("prepareNuGetConfig") { val nuGetConfigFile = File("${project.projectDir}/../NuGet.Config") dependsOn(prepareRiderBuildProps) group = backendGroup doLast { logger.info("dotNetSdk location: '$dotNetSdkPath'") assert(dotNetSdkPath.isDirectory()) logger.info("Generating :${nuGetConfigFile.canonicalPath}...") val nugetConfigText = """<?xml version="1.0" encoding="utf-8"?> |<configuration> | <packageSources> | <clear /> | <add key="local-dotnet-sdk" value="${dotNetSdkPath.toRealPath()}" /> | <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> | </packageSources> |</configuration> """.trimMargin() nuGetConfigFile.writeText(nugetConfigText) logger.info("Generated content:\n$nugetConfigText") val sb = StringBuilder("Dump dotNetSdkFile content:\n") for(file in dotNetSdkPath.listFiles()) { sb.append("${file.toRealPath()}\n") } logger.info(sb.toString()) } } val buildReSharperHostPlugin = register("buildReSharperHostPlugin") { group = backendGroup description = "Builds the full ReSharper backend plugin solution" dependsOn(prepareNuGetConfig, generateModels) onlyIf { skipDotnet.not() } doLast { val buildConfiguration = project.ext.get("BuildConfiguration").toString() val warningsAsErrors = project.ext.get("warningsAsErrors").toString() logger.info("Building $buildFile ($buildConfiguration)") val dotNetCliPath = projectDir.parentFile.resolve("dotnet-sdk.cmd") val buildArguments = listOf( "build", resharperHostPluginSolution.canonicalPath, "-consoleLoggerParameters:ErrorsOnly", "/p:Configuration=$buildConfiguration", "/p:Version=${project.version}", "/p:TreatWarningsAsErrors=$warningsAsErrors", "/bl:${resharperHostPluginSolution.name + ".binlog"}", "/nologo" ) logger.info("dotnet call: '$dotNetCliPath' '$buildArguments' in '$backendDir'") project.exec { executable = dotNetCliPath.canonicalPath args = buildArguments workingDir = backendDir } } } val packReSharperPlugin by creating(com.ullink.NuGetPack::class) { // Don't know the way to rewrite this task in a lazy manner (using registering) because NuGetPack uses `project.afterEvaluate` in its implementation, // so it's just a workaround to not download the Rider SDK in the monorepo mode if (isMonorepo) { doFirst { throw GradleException("This task is not expected to be run in the monorepo environment") } return@creating } group = backendGroup onlyIf { isWindows } // non-windows builds are just for running tests, and agents don't have `mono` installed. NuGetPack requires `mono` though. description = "Packs resulting DLLs into a NuGet package which is an R# extension." dependsOn(buildReSharperHostPlugin) val changelogNotes = changelog.renderItem(getChangelogItem().withFilter { line -> !line.startsWith("- Rider:") && !line.startsWith("- Unity editor:") }, Changelog.OutputType.PLAIN_TEXT).trim().let { // There's a bug in the changelog plugin that adds extra newlines on Windows, possibly // due to a Unix / Windows line ending mismatch. // Remove this hack once JetBrains/gradle-changelog-plugin#8 is fixed if (isWindows) { it.replace("\n\n", "\r\n") } else { it } } val releaseNotes = """New in $pluginVersion $changelogNotes See CHANGELOG.md in the JetBrains/resharper-unity GitHub repo for more details and history.""".let { if (isWindows) { it.replace("&quot;", "\\\"") } else { it.replace("&quot;", "\"") } } // The command line to call nuget pack passes properties as a semicolon-delimited string // We can't have HTML-encoded entities (e.g. &quot;) if (releaseNotes.contains(";")) throw GradleException("Release notes cannot semi-colon") setNuspecFile( File( backendDir, "resharper-unity/src/Unity/resharper-unity.resharper.nuspec" ).canonicalPath ) setDestinationDir(File(backendDir, "build/distributions/$buildConfiguration").canonicalPath) packageAnalysis = false packageVersion = version properties = mapOf( "Configuration" to buildConfiguration, "ReleaseNotes" to releaseNotes ) doFirst { logger.info("Packing: ${nuspecFile.name}") } } val nunitReSharperJson by registering(NUnit::class) { nunitVersion = "3.16.2" // newer than default, helps running with net 7 group = testGroup shadowCopy = false outputs.upToDateWhen { false } val buildDir = File(repoRoot, "resharper/build") val testDll = File(buildDir, "Json.Tests/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Json.Tests.dll") testAssemblies = listOf(testDll) } val nunitReSharperYaml by registering(NUnit::class) { nunitVersion = "3.16.2" // newer than default, helps running with net 7 group = testGroup shadowCopy = false outputs.upToDateWhen { false } val buildDir = File(repoRoot, "resharper/build") val testDll = File(buildDir, "Yaml.Tests/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Yaml.Tests.dll") testAssemblies = listOf(testDll) } val nunitReSharperUnity by registering(NUnit::class) { nunitVersion = "3.16.2" // newer than default, helps running with net 7 group = testGroup shadowCopy = false outputs.upToDateWhen { false } useX86 = true val buildDir = File(repoRoot, "resharper/build") val testDll = File( buildDir, "Unity.Tests/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Tests.dll" ) testAssemblies = listOf(testDll) } val nunitReSharperUnityRider by registering(NUnit::class) { nunitVersion = "3.16.2" // newer than default, helps running with net 7 group = testGroup shadowCopy = false outputs.upToDateWhen { false } useX86 = true val buildDir = File(repoRoot, "resharper/build") val testDll = File( buildDir, "Unity.Rider.Tests/bin/$buildConfiguration/net472/JetBrains.ReSharper.Plugins.Unity.Rider.Tests.dll" ) testAssemblies = listOf(testDll) } val runNunit by registering { group = testGroup // nunit3 defaults to running test assemblies in parallel, which causes problems with shared access to databases; // The nunit plugin can't disable this, so we'll do it long hand... dependsOn( //buildReSharperHostPlugin, nunitReSharperJson, nunitReSharperYaml, nunitReSharperUnity, nunitReSharperUnityRider ) } // It might be better to make it a top-level task called separately, e.g., gradle buildPlugin nunit // (and we could get rid of RunTests then, too) if (runTests) { runNunit.get().shouldRunAfter(buildReSharperHostPlugin) buildReSharperHostPlugin.configure { finalizedBy(runNunit) } } val publishCiBackendArtifacts by registering { group = ciGroup inputs.files(packReSharperPlugin.outputs) doLast { println("##teamcity[publishArtifacts '${packReSharperPlugin.packageFile.absolutePath}']") } } val publishCiBuildData by registering { group = ciGroup dependsOn(publishCiBuildNumber, publishCiBackendArtifacts) } if (isAutomatedBuild) { named("buildPlugin") { finalizedBy(publishCiBuildData) } } withType<PrepareSandboxTask> { // Default dependsOn includes the standard Java build/jar task dependsOn(buildReSharperHostPlugin) // Have dependent tasks use upToDateWhen { project.buildServer.automatedBuild etc. } //inputs.files(buildRiderPlugin.outputs) // Backend: // Copy unity editor plugin repacked file to `rider-unity/EditorPlugin` // Copy JetBrains.ReSharper.Plugins.Unity.dll to `rider-unity/dotnet` // Copy annotations to `rider-unity/dotnet/Extensions/JetBrains.Unity/annotations` // Frontend: doLast { dotnetDllFiles.forEach { if (!it.exists()) error("File $it does not exist") } debuggerDllFiles.forEach { if (!it.exists()) error("File $it does not exist") } textureDebuggerDllFiles.forEach { if (!it.exists()) error("File $it does not exist") } pausePointDllFiles.forEach { if (!it.exists()) error("File $it does not exist") } listIosUsbDevicesFiles.forEach { if (!it.exists()) error("File $it does not exist") } // unityEditorDllFiles.forEach { if (!it.exists()) error("File $it does not exist") } } val pluginName = intellijPlatform.projectName.get() dotnetDllFiles.forEach { from(it) { into("${pluginName}/dotnet") } } debuggerDllFiles.forEach { from(it) { into("${pluginName}/dotnetDebuggerWorker") } } textureDebuggerDllFiles.forEach { from(it) { into("${pluginName}/DotFiles") } } pausePointDllFiles.forEach { from(it) { into("${pluginName}/DotFiles") } } listIosUsbDevicesFiles.forEach { from(it) { into("${pluginName}/DotFiles") } } // unityEditorDllFiles.forEach { from(it) { into("${pluginName}/EditorPlugin") } } } withType<Test>().configureEach { useTestNG() if (project.hasProperty("ignoreFailures")) { ignoreFailures = true } if (project.hasProperty("integrationTests")) { val testsType = project.property("integrationTests").toString() if (testsType == "include") { include("com/jetbrains/rider/unity/test/cases/integrationTests/**") } else if (testsType == "exclude") { exclude("com/jetbrains/rider/unity/test/cases/integrationTests/**") } } testLogging { showStandardStreams = true exceptionFormat = TestExceptionFormat.FULL } } wrapper { gradleVersion = "8.7" distributionUrl = "https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip" } }