From a6bc5de976fc265baa7190e9994d23b125b50ced Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 16 May 2025 12:02:53 -0700 Subject: [PATCH 1/2] hotfix to fix restore and stop including bin/obj artifacts in directory with loose files --- .../HostWorkspace/VirtualProject.cs | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VirtualProject.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VirtualProject.cs index e2cd47a2169f2..96d1ab5c389f3 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VirtualProject.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VirtualProject.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.InteropServices; using System.Security; +using System.Security.Cryptography; +using System.Text; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -21,6 +24,46 @@ internal static class VirtualCSharpFileBasedProgramProject internal static string GetVirtualProjectPath(string documentFilePath) => Path.ChangeExtension(documentFilePath, ".csproj"); +#region TODO: Copy-pasted from dotnet run-api. Delete when run-api is adopted. + private static class Sha256Hasher + { + /// + /// The hashed mac address needs to be the same hashed value as produced by the other distinct sources given the same input. (e.g. VsCode) + /// + public static string Hash(string text) + { + byte[] bytes = Encoding.UTF8.GetBytes(text); + byte[] hash = SHA256.HashData(bytes); +#if NET9_0_OR_GREATER + return Convert.ToHexStringLower(hash); +#else + return Convert.ToHexString(hash).ToLowerInvariant(); +#endif + } + + public static string HashWithNormalizedCasing(string text) + { + return Hash(text.ToUpperInvariant()); + } + } + + // TODO: this is a copy of SDK run-api code. Must delete when adopting run-api. + internal static string GetArtifactsPath(string entryPointFileFullPath) + { + // We want a location where permissions are expected to be restricted to the current user. + string directory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Path.GetTempPath() + : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + + // Include entry point file name so the directory name is not completely opaque. + string fileName = Path.GetFileNameWithoutExtension(entryPointFileFullPath); + string hash = Sha256Hasher.HashWithNormalizedCasing(entryPointFileFullPath); + string directoryName = $"{fileName}-{hash}"; + + return Path.Join(directory, "dotnet", "runfile", directoryName); + } +#endregion + internal static (string virtualProjectXml, bool isFileBasedProgram) MakeVirtualProjectContent(string documentFilePath, SourceText text) { Contract.ThrowIfFalse(PathUtilities.IsAbsolute(documentFilePath)); @@ -30,22 +73,29 @@ internal static (string virtualProjectXml, bool isFileBasedProgram) MakeVirtualP var root = tree.GetRoot(); var isFileBasedProgram = root.GetLeadingTrivia().Any(SyntaxKind.IgnoredDirectiveTrivia) || root.ChildNodes().Any(node => node.IsKind(SyntaxKind.GlobalStatement)); - var virtualProjectXml = $""" - + var artifactsPath = GetArtifactsPath(documentFilePath); + var virtualProjectXml = $""" + Exe - net8.0 + net10.0 enable enable $(Features);FileBasedProgram false + false + {SecurityElement.Escape(artifactsPath)} + + + + + + + + Exe + {SecurityElement.Escape(targetFramework)} + enable + enable + + + + false + + + + preview + + + + $(Features);FileBasedProgram + + + + + + + + + + + + - + + + + + + <_RestoreProjectPathItems Include="@(FilteredRestoreGraphProjectInputItems)" /> + + + + + + - - - - - - - - - - - - - <_RestoreProjectPathItems Include="@(FilteredRestoreGraphProjectInputItems)" /> - - - - - - """;