diff --git a/dotnet-sdk.cmd b/dotnet-sdk.cmd new file mode 100755 index 00000000..f0ea15c2 --- /dev/null +++ b/dotnet-sdk.cmd @@ -0,0 +1,267 @@ +:<<"::CMDLITERAL" +@ECHO OFF +GOTO :CMDSCRIPT +::CMDLITERAL + +set -eu + +SCRIPT_VERSION=dotnet-cmd-v2 +COMPANY_DIR="JetBrains" +TARGET_DIR="${TEMPDIR:-$HOME/.local/share}/$COMPANY_DIR/dotnet-cmd" +KEEP_ROSETTA2=false +export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true +export DOTNET_CLI_TELEMETRY_OPTOUT=true +export DOTNET_MULTILEVEL_LOOKUP=false + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +retry_on_error () { + local n="$1" + shift + + for i in $(seq 2 "$n"); do + "$@" 2>&1 && return || echo "WARNING: Command '$1' returned non-zero exit status $?, try again" + done + "$@" +} + +is_linux_musl () { + (ldd --version 2>&1 || true) | grep -q musl +} + +case $(uname) in +Darwin) + DOTNET_ARCH=$(uname -m) + if ! $KEEP_ROSETTA2 && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" = "1" ]; then + DOTNET_ARCH=arm64 + fi + case $DOTNET_ARCH in + x86_64) + DOTNET_HASH_URL=91c41b31-cf90-4771-934b-6928bbb48aaf/76e95bac2a4cb3fd50c920fd1601527c + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-osx-x64 + ;; + arm64) + DOTNET_HASH_URL=d0c47b58-a384-46b3-8fce-bd9188541858/dbfe7b537396b747255e65c0fbc9641e + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-osx-arm64 + ;; + *) echo "Unknown architecture $DOTNET_ARCH" >&2; exit 1;; + esac;; +Linux) + DOTNET_ARCH=$(linux$(getconf LONG_BIT) uname -m) + case $DOTNET_ARCH in + x86_64) + if is_linux_musl; then + DOTNET_HASH_URL=43fbb34e-6e21-4abd-aa90-0e65de1fa3c2/0f29b8b0f6e7d5dfd6e790e49a4700db + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-musl-x64 + else + DOTNET_HASH_URL=c646b288-5d5b-4c9c-a95b-e1fad1c0d95d/e13d71d48b629fe3a85f5676deb09e2d + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-x64 + fi + ;; + aarch64) + if is_linux_musl; then + DOTNET_HASH_URL=b98e56bf-6021-4236-aa19-58b216a4674e/f367378def5dea5bc439dbb94b7699af + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-musl-arm64 + else + DOTNET_HASH_URL=72ec0dc2-f425-48c3-97f1-dc83740ba400/78e8fa01fa9987834fa01c19a23dd2e7 + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-arm64 + fi + ;; + armv7l | armv8l) + if is_linux_musl; then + DOTNET_HASH_URL=de77d6a5-d77e-4421-8198-135b3cce9caf/ad2194c26f424c81fb2b79decb6e9f2b + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-musl-arm + else + DOTNET_HASH_URL=54b057ec-36ef-4808-a436-50ee3fa39a44/87d696a761176b721daaf8ab9761c9c8 + DOTNET_FILE_NAME=dotnet-sdk-7.0.102-linux-arm + fi + ;; + *) echo "Unknown architecture $DOTNET_ARCH" >&2; exit 1;; + esac;; +*) echo "Unknown platform: $(uname)" >&2; exit 1;; +esac + +DOTNET_URL=https://cache-redirector.jetbrains.com/download.visualstudio.microsoft.com/download/pr/$DOTNET_HASH_URL/$DOTNET_FILE_NAME.tar.gz +DOTNET_TARGET_DIR=$TARGET_DIR/$DOTNET_FILE_NAME-$SCRIPT_VERSION +DOTNET_TEMP_FILE=$TARGET_DIR/dotnet-sdk-temp.tar.gz + +if grep -q -x "$DOTNET_URL" "$DOTNET_TARGET_DIR/.flag" 2>/dev/null; then + # Everything is up-to-date in $DOTNET_TARGET_DIR, do nothing + true +else +while true; do # Note(k15tfu): for goto + mkdir -p "$TARGET_DIR" + + LOCK_FILE="$TARGET_DIR/.dotnet-cmd-lock.pid" + TMP_LOCK_FILE="$TARGET_DIR/.tmp.$$.pid" + echo $$ >"$TMP_LOCK_FILE" + + while ! ln "$TMP_LOCK_FILE" "$LOCK_FILE" 2>/dev/null; do + LOCK_OWNER=$(cat "$LOCK_FILE" 2>/dev/null || true) + while [ -n "$LOCK_OWNER" ] && ps -p $LOCK_OWNER >/dev/null; do + warn "Waiting for the process $LOCK_OWNER to finish bootstrap dotnet.cmd" + sleep 1 + LOCK_OWNER=$(cat "$LOCK_FILE" 2>/dev/null || true) + + # Hurry up, bootstrap is ready.. + if grep -q -x "$DOTNET_URL" "$DOTNET_TARGET_DIR/.flag" 2>/dev/null; then + break 3 # Note(k15tfu): goto out of the outer if-else block. + fi + done + + if [ -n "$LOCK_OWNER" ] && grep -q -x $LOCK_OWNER "$LOCK_FILE" 2>/dev/null; then + die "ERROR: The lock file $LOCK_FILE still exists on disk after the owner process $LOCK_OWNER exited" + fi + done + + trap "rm -f \"$LOCK_FILE\"" EXIT + rm "$TMP_LOCK_FILE" + + if ! grep -q -x "$DOTNET_URL" "$DOTNET_TARGET_DIR/.flag" 2>/dev/null; then + warn "Downloading $DOTNET_URL to $DOTNET_TEMP_FILE" + + rm -f "$DOTNET_TEMP_FILE" + if command -v curl >/dev/null 2>&1; then + if [ -t 1 ]; then CURL_PROGRESS="--progress-bar"; else CURL_PROGRESS="--silent --show-error"; fi + retry_on_error 5 curl -L $CURL_PROGRESS --output "${DOTNET_TEMP_FILE}" "$DOTNET_URL" + elif command -v wget >/dev/null 2>&1; then + if [ -t 1 ]; then WGET_PROGRESS=""; else WGET_PROGRESS="-nv"; fi + retry_on_error 5 wget $WGET_PROGRESS -O "${DOTNET_TEMP_FILE}" "$DOTNET_URL" + else + die "ERROR: Please install wget or curl" + fi + + warn "Extracting $DOTNET_TEMP_FILE to $DOTNET_TARGET_DIR" + rm -rf "$DOTNET_TARGET_DIR" + mkdir -p "$DOTNET_TARGET_DIR" + + tar -x -f "$DOTNET_TEMP_FILE" -C "$DOTNET_TARGET_DIR" + rm -f "$DOTNET_TEMP_FILE" + + echo "$DOTNET_URL" >"$DOTNET_TARGET_DIR/.flag" + fi + + rm "$LOCK_FILE" + break +done +fi + +if [ ! -x "$DOTNET_TARGET_DIR/dotnet" ]; then + die "Unable to find dotnet under $DOTNET_TARGET_DIR" +fi + +exec "$DOTNET_TARGET_DIR/dotnet" "$@" + +:CMDSCRIPT + +setlocal +set SCRIPT_VERSION=v2 +set COMPANY_NAME=JetBrains +set TARGET_DIR=%LOCALAPPDATA%\%COMPANY_NAME%\dotnet-cmd\ + +for /f "tokens=3 delims= " %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v "PROCESSOR_ARCHITECTURE"') do set ARCH=%%a + +if "%ARCH%"=="ARM64" ( + set DOTNET_HASH_URL=a93af409-9846-4340-b810-1c112db0969b/52557cefadc77036a5febb00a31a439b + set DOTNET_FILE_NAME=dotnet-sdk-7.0.102-win-arm64 +) else ( + +if "%ARCH%"=="AMD64" ( + set DOTNET_HASH_URL=7c869d6e-b49e-4c52-b197-77fca05f0c69/f3b6fb63231c8ed6afc585da090d4595 + set DOTNET_FILE_NAME=dotnet-sdk-7.0.102-win-x64 +) else ( + +if "%ARCH%"=="x86" ( + set DOTNET_HASH_URL=30d69e4b-74d9-4043-9b50-2f91b27d9e80/c6ecb4b16858afb2fc1f056bd3ecdbb4 + set DOTNET_FILE_NAME=dotnet-sdk-7.0.102-win-x86 +) else ( + +echo Unknown Windows architecture +goto fail + +))) + +set DOTNET_URL=https://cache-redirector.jetbrains.com/download.visualstudio.microsoft.com/download/pr/%DOTNET_HASH_URL%/%DOTNET_FILE_NAME%.zip +set DOTNET_TARGET_DIR=%TARGET_DIR%%DOTNET_FILE_NAME%-%SCRIPT_VERSION%\ +set DOTNET_TEMP_FILE=%TARGET_DIR%dotnet-sdk-temp.zip +set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true +set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true +set DOTNET_CLI_TELEMETRY_OPTOUT=true +set export DOTNET_MULTILEVEL_LOOKUP=false + +set POWERSHELL=%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe + +if not exist "%DOTNET_TARGET_DIR%.flag" goto downloadAndExtractDotNet + +set /p CURRENT_FLAG=<"%DOTNET_TARGET_DIR%.flag" +if "%CURRENT_FLAG%" == "%DOTNET_URL%" goto continueWithDotNet + +:downloadAndExtractDotNet + +set DOWNLOAD_AND_EXTRACT_DOTNET_PS1= ^ +Set-StrictMode -Version 3.0; ^ +$ErrorActionPreference = 'Stop'; ^ + ^ +$createdNew = $false; ^ +$lock = New-Object System.Threading.Mutex($true, 'Global\dotnet-cmd-lock', [ref]$createdNew); ^ +if (-not $createdNew) { ^ + Write-Host 'Waiting for the other process to finish bootstrap dotnet.cmd'; ^ + [void]$lock.WaitOne(); ^ +} ^ + ^ +try { ^ + if ((Get-Content '%DOTNET_TARGET_DIR%.flag' -ErrorAction Ignore) -ne '%DOTNET_URL%') { ^ + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^ + Write-Host 'Downloading %DOTNET_URL% to %DOTNET_TEMP_FILE%'; ^ + [void](New-Item '%TARGET_DIR%' -ItemType Directory -Force); ^ + (New-Object Net.WebClient).DownloadFile('%DOTNET_URL%', '%DOTNET_TEMP_FILE%'); ^ + ^ + Write-Host 'Extracting %DOTNET_TEMP_FILE% to %DOTNET_TARGET_DIR%'; ^ + if (Test-Path '%DOTNET_TARGET_DIR%') { ^ + Remove-Item '%DOTNET_TARGET_DIR%' -Recurse; ^ + } ^ + Add-Type -A 'System.IO.Compression.FileSystem'; ^ + [IO.Compression.ZipFile]::ExtractToDirectory('%DOTNET_TEMP_FILE%', '%DOTNET_TARGET_DIR%'); ^ + Remove-Item '%DOTNET_TEMP_FILE%'; ^ + ^ + Set-Content '%DOTNET_TARGET_DIR%.flag' -Value '%DOTNET_URL%'; ^ + } ^ +} ^ +finally { ^ + $lock.ReleaseMutex(); ^ +} + +"%POWERSHELL%" -nologo -noprofile -Command %DOWNLOAD_AND_EXTRACT_DOTNET_PS1% +if errorlevel 1 goto fail + +:continueWithDotNet + +if not exist "%DOTNET_TARGET_DIR%\dotnet.exe" ( + echo Unable to find dotnet.exe under %DOTNET_TARGET_DIR% + goto fail +) + +REM Prevent globally installed .NET Core from leaking into this runtime's lookup +SET DOTNET_MULTILEVEL_LOOKUP=0 + +for /f "tokens=2 delims=:." %%c in ('chcp') do set /a PREV_CODE_PAGE=%%c +chcp 65001 >nul && call "%DOTNET_TARGET_DIR%\dotnet.exe" %* +set /a DOTNET_EXIT_CODE=%ERRORLEVEL% +chcp %PREV_CODE_PAGE% >nul + +exit /B %DOTNET_EXIT_CODE% +endlocal + +:fail +echo "FAIL" +exit /b 1 \ No newline at end of file diff --git a/resharper/src/ProjectModel/GodotTracker.cs b/resharper/src/ProjectModel/GodotTracker.cs index adc5c25e..2d2d5f19 100644 --- a/resharper/src/ProjectModel/GodotTracker.cs +++ b/resharper/src/ProjectModel/GodotTracker.cs @@ -1,5 +1,6 @@ using System.Linq; using JetBrains.ProjectModel; +using JetBrains.ProjectModel.Tasks; using JetBrains.Rd.Base; using JetBrains.ReSharper.Feature.Services.Protocol; using JetBrains.Rider.Model.Godot.FrontendBackend; @@ -11,19 +12,23 @@ namespace JetBrains.ReSharper.Plugins.Godot.ProjectModel public class GodotTracker { public VirtualFileSystemPath MainProjectBasePath { get; private set; } - public GodotTracker(ISolution solution, ILogger logger) + public GodotTracker(ISolution solution, ILogger logger, ISolutionLoadTasksScheduler tasksScheduler) { var model = solution.GetProtocolSolution().GetGodotFrontendBackendModel(); - - foreach (var project in solution.GetTopLevelProjects().Where(project => project.IsGodotProject())) - { - var file = project.Location.Combine("project.godot"); - if (!file.ExistsFile) continue; - MainProjectBasePath = file.Directory; - logger.Verbose($"Godot MainProjectBasePath: {file.Directory}"); - model.MainProjectBasePath.SetValue(MainProjectBasePath.FullPath); - break; - } + tasksScheduler.EnqueueTask(new SolutionLoadTask("UnityTechnologyDescriptionCollector", + SolutionLoadTaskKinds.Done, + () => + { + foreach (var project in solution.GetTopLevelProjects().Where(project => project.IsGodotProject())) + { + var file = project.Location.Combine("project.godot"); + if (!file.ExistsFile) continue; + MainProjectBasePath = file.Directory; + logger.Verbose($"Godot MainProjectBasePath: {file.Directory}"); + model.MainProjectBasePath.SetValue(file.Directory.FullPath); + break; + } + })); } } } \ No newline at end of file diff --git a/resharper/src/ZoneMarker.cs b/resharper/src/ZoneMarker.cs index 33089862..4ecf062a 100644 --- a/resharper/src/ZoneMarker.cs +++ b/resharper/src/ZoneMarker.cs @@ -1,14 +1,11 @@ using JetBrains.Application.BuildScript.Application.Zones; -using JetBrains.DocumentModel; -using JetBrains.ProjectModel; -using JetBrains.ReSharper.Resources.Shell; +using JetBrains.Rider.Backend.Env; using JetBrains.Rider.Backend.Product; -using JetBrains.Rider.Model; namespace JetBrains.ReSharper.Plugins.Godot { [ZoneMarker] - public class ZoneMarker : IRequire, IRequire, IRequire, IRequire, IRequire + public class ZoneMarker : IRequire, IRequire { } } \ No newline at end of file diff --git a/rider/build.gradle.kts b/rider/build.gradle.kts index 66d266b3..95f078ca 100644 --- a/rider/build.gradle.kts +++ b/rider/build.gradle.kts @@ -312,8 +312,9 @@ tasks { group = riderGodotTargetsGroup dependsOn("prepare") doLast { + val dotNetCliPath = projectDir.parentFile.resolve("dotnet-sdk.cmd") exec { - executable = "dotnet" + executable = dotNetCliPath.canonicalPath args("build", "$resharperPluginPath/godot-support.sln", "-c", buildConfiguration) } } diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/GodotProjectDiscoverer.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/GodotProjectDiscoverer.kt index 1ac413b6..b8bdb3ba 100644 --- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/GodotProjectDiscoverer.kt +++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/GodotProjectDiscoverer.kt @@ -1,11 +1,12 @@ package com.jetbrains.rider.plugins.godot import com.intellij.execution.RunManager +import com.intellij.openapi.client.ClientProjectSession +import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project -import com.jetbrains.rd.platform.client.ProtocolProjectSession import com.jetbrains.rd.platform.util.idea.LifetimedService -import com.jetbrains.rd.platform.util.lifetime +import com.intellij.openapi.rd.util.lifetime import com.jetbrains.rd.protocol.SolutionExtListener import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.reactive.IProperty @@ -20,6 +21,7 @@ import com.jetbrains.rider.run.configurations.dotNetExe.DotNetExeConfigurationTy import com.jetbrains.rider.util.idea.getService import java.io.File +@Service(Service.Level.PROJECT) class GodotProjectDiscoverer(project: Project) : LifetimedService() { val mainProjectBasePath : IProperty = Property(null) @@ -69,7 +71,11 @@ class GodotProjectDiscoverer(project: Project) : LifetimedService() { class ProtocolListener : SolutionExtListener { - override fun extensionCreated(lifetime: Lifetime, session: ProtocolProjectSession, model: GodotFrontendBackendModel) { + override fun extensionCreated( + lifetime: Lifetime, + session: ClientProjectSession, + model: GodotFrontendBackendModel + ) { model.mainProjectBasePath.adviseNotNull(lifetime) { getInstance(session.project).mainProjectBasePath.set(it) } diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataCoreFileWatcher.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataCoreFileWatcher.kt index d0feddb6..e8a6f744 100644 --- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataCoreFileWatcher.kt +++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataCoreFileWatcher.kt @@ -4,13 +4,11 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.rd.util.launchBackground import com.intellij.util.application -import com.intellij.util.io.isDirectory import com.jetbrains.rd.util.lifetime.isAlive import com.jetbrains.rd.util.reactive.viewNotNull import com.jetbrains.rd.util.reactive.whenTrue import com.jetbrains.rdclient.util.idea.LifetimedProjectComponent import com.jetbrains.rider.projectView.solution -import com.jetbrains.rider.projectView.solutionDirectory import kotlinx.coroutines.delay import kotlinx.coroutines.time.withTimeout import java.io.File @@ -18,6 +16,7 @@ import java.nio.file.* import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY import java.time.Duration +import kotlin.io.path.isDirectory class MetadataCoreFileWatcher(project: Project) : LifetimedProjectComponent(project) { diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataMonoFileWatcher.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataMonoFileWatcher.kt index b40fc9a9..e46fd50b 100644 --- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataMonoFileWatcher.kt +++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/godot/MetadataMonoFileWatcher.kt @@ -4,7 +4,6 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.util.SystemInfo import com.intellij.util.application -import com.intellij.util.io.isDirectory import com.jetbrains.rd.util.lifetime.isAlive import com.jetbrains.rd.util.reactive.viewNotNull import com.jetbrains.rd.util.reactive.whenTrue @@ -17,6 +16,7 @@ import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY import java.security.MessageDigest import kotlin.concurrent.thread +import kotlin.io.path.isDirectory class MetadataMonoFileWatcher(project: Project) : LifetimedProjectComponent(project) { diff --git a/rider/src/main/resources/META-INF/plugin.xml b/rider/src/main/resources/META-INF/plugin.xml index e28dcb13..f87ddb42 100644 --- a/rider/src/main/resources/META-INF/plugin.xml +++ b/rider/src/main/resources/META-INF/plugin.xml @@ -4,7 +4,7 @@ 2023.3.0.9999 JetBrains - + com.intellij.modules.rider @@ -24,7 +24,6 @@ -