From f6444c620ded602e407b6093d08ad8c582043c18 Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Thu, 1 Jul 2021 00:00:00 +0000 Subject: [PATCH] com.unity.ide.visualstudio@2.0.11 ## [2.0.11] - 2021-07-01 Integration: - Added support for Visual Studio and Visual Studio For Mac 2022. - Fixed an issue when the package was enabled for background processes. Project generation: - Use absolute paths for Analyzers and rulesets. ## [2.0.10] - 2021-06-10 Project generation: - Improved project generation performance when a file is moved, deleted or modified. Integration: - Improved Inner-loop performance by avoiding to call the package manager when looking up `vswhere` utility. - Fixed a network issue preventing the communication between Visual Studio and Unity on Windows. --- CHANGELOG.md | 32 +- .../AppleEventIntegration/main.mm | 7 +- Editor/Discovery.cs | 11 +- Editor/FileUtility.cs | 29 +- Editor/Messaging/Messenger.cs | 24 +- .../Contents/Info.plist | 2 +- .../Contents/MacOS/AppleEventIntegration | Bin 153488 -> 153536 bytes Editor/ProjectGeneration/ProjectGeneration.cs | 443 +++++++++--------- Editor/ProjectGeneration/ProjectProperties.cs | 27 ++ .../ProjectProperties.cs.meta | 11 + Editor/Testing/TestAdaptor.cs | 2 +- Editor/Testing/TestRunnerApiListener.cs | 13 +- Editor/UnityInstallation.cs | 27 ++ Editor/UsageUtility.cs | 2 +- Editor/VisualStudioEditor.cs | 12 +- Editor/VisualStudioInstallation.cs | 4 + Editor/VisualStudioIntegration.cs | 3 +- ValidationConfig.json | 19 + ValidationConfig.json.meta | 7 + ValidationExceptions.json | 10 + ValidationExceptions.json.meta | 7 + package.json | 10 +- 22 files changed, 428 insertions(+), 274 deletions(-) create mode 100644 Editor/ProjectGeneration/ProjectProperties.cs create mode 100644 Editor/ProjectGeneration/ProjectProperties.cs.meta create mode 100644 ValidationConfig.json create mode 100644 ValidationConfig.json.meta create mode 100644 ValidationExceptions.json create mode 100644 ValidationExceptions.json.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f1b41..a1a1395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,39 @@ # Code Editor Package for Visual Studio +## [2.0.11] - 2021-07-01 + +Integration: + +- Added support for Visual Studio and Visual Studio For Mac 2022. +- Fixed an issue when the package was enabled for background processes. + +Project generation: + +- Use absolute paths for Analyzers and rulesets. + + +## [2.0.10] - 2021-06-10 + +Project generation: + +- Improved project generation performance when a file is moved, deleted or modified. + +Integration: + +- Improved Inner-loop performance by avoiding to call the package manager when looking up `vswhere` utility. +- Fixed a network issue preventing the communication between Visual Studio and Unity on Windows. + ## [2.0.9] - 2021-05-04 Project generation: -Added support for CLI. +- Added support for CLI. Integration: -Improved performance when discovering Visual Studio installations. -Warn when legacy assemblies are present in the project. -Warn when the package version is not up-to-date. - +- Improved performance when discovering Visual Studio installations. +- Warn when legacy assemblies are present in the project. +- Warn when the package version is not up-to-date. ## [2.0.8] - 2021-04-09 diff --git a/Editor/AppleEventIntegration~/AppleEventIntegration/main.mm b/Editor/AppleEventIntegration~/AppleEventIntegration/main.mm index a53cf84..678b765 100644 --- a/Editor/AppleEventIntegration~/AppleEventIntegration/main.mm +++ b/Editor/AppleEventIntegration~/AppleEventIntegration/main.mm @@ -118,12 +118,7 @@ static BOOL ApplicationSupportsQueryOpenedSolution(NSString* appPath) return NO; NSString* version = (NSString*)versionValue; - NSArray* components = [version componentsSeparatedByString:@"."]; - if (!components || components.count < 2) - return NO; - - return [components[0] integerValue] >= 8 - && [components[1] integerValue] >= 6; + return [version compare:@"8.6" options:NSNumericSearch] != NSOrderedAscending; } static NSArray* QueryRunningInstances(NSString *appPath) diff --git a/Editor/Discovery.cs b/Editor/Discovery.cs index 6c4a676..cdb64e1 100644 --- a/Editor/Discovery.cs +++ b/Editor/Discovery.cs @@ -21,9 +21,7 @@ namespace Microsoft.Unity.VisualStudio.Editor public static void FindVSWhere() { - _vsWherePath = FileUtility - .FindPackageAssetFullPath("VSWhere a:packages", "vswhere.exe") - .FirstOrDefault(); + _vsWherePath = FileUtility.GetPackageAssetFullPath("Editor", "VSWhere", "vswhere.exe"); } public static IEnumerable GetVisualStudioInstallations() @@ -71,7 +69,12 @@ namespace Microsoft.Unity.VisualStudio.Editor // On Mac we use the .app folder, so we need to access to main assembly if (VisualStudioEditor.IsOSX) - fvi = Path.Combine(editorPath, "Contents", "Resources", "lib", "monodevelop", "bin", "VisualStudio.exe"); + { + fvi = Path.Combine(editorPath, "Contents/Resources/lib/monodevelop/bin/VisualStudio.exe"); + + if (!File.Exists(fvi)) + fvi = Path.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe"); + } if (!File.Exists(fvi)) return false; diff --git a/Editor/FileUtility.cs b/Editor/FileUtility.cs index f23b720..073d280 100644 --- a/Editor/FileUtility.cs +++ b/Editor/FileUtility.cs @@ -5,8 +5,6 @@ *--------------------------------------------------------------------------------------------*/ using System; using System.IO; -using System.Linq; -using UnityEditor; using UnityEngine; namespace Microsoft.Unity.VisualStudio.Editor @@ -16,27 +14,19 @@ namespace Microsoft.Unity.VisualStudio.Editor public const char WinSeparator = '\\'; public const char UnixSeparator = '/'; - // Safe for packages as we use packageInfo.resolvedPath, so this should work in library package cache as well - public static string[] FindPackageAssetFullPath(string assetfilter, string filefilter) + public static string GetPackageAssetFullPath(params string[] components) { - return AssetDatabase.FindAssets(assetfilter) - .Select(AssetDatabase.GUIDToAssetPath) - .Where(assetPath => assetPath.Contains(filefilter)) - .Select(asset => - { - var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(asset); - return Normalize(packageInfo.resolvedPath + asset.Substring(packageInfo.assetPath.Length)); - }) - .ToArray(); + // Unity has special IO handling of Packages and will resolve those path to the right package location + return Path.GetFullPath(Path.Combine("Packages", "com.unity.ide.visualstudio", Path.Combine(components))); } public static string GetAssetFullPath(string asset) { var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, "..")); - return Path.GetFullPath(Path.Combine(basePath, Normalize(asset))); + return Path.GetFullPath(Path.Combine(basePath, NormalizePathSeparators(asset))); } - public static string Normalize(string path) + public static string NormalizePathSeparators(this string path) { if (string.IsNullOrEmpty(path)) return path; @@ -65,12 +55,18 @@ namespace Microsoft.Unity.VisualStudio.Editor return relative == Path.GetFileName(relative); } + + public static string MakeAbsolutePath(this string path, string projectDirectory) + { + if (string.IsNullOrEmpty(path)) { return string.Empty; } + return Path.IsPathRooted(path) ? path : Path.Combine(projectDirectory, path); + } // returns null if outside of the project scope internal static string MakeRelativeToProjectPath(string fileName) { var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, "..")); - fileName = Normalize(fileName); + fileName = NormalizePathSeparators(fileName); if (!Path.IsPathRooted(fileName)) fileName = Path.Combine(basePath, fileName); @@ -82,6 +78,5 @@ namespace Microsoft.Unity.VisualStudio.Editor .Substring(basePath.Length) .Trim(Path.DirectorySeparatorChar); } - } } diff --git a/Editor/Messaging/Messenger.cs b/Editor/Messaging/Messenger.cs index d88071e..2d8f621 100644 --- a/Editor/Messaging/Messenger.cs +++ b/Editor/Messaging/Messenger.cs @@ -17,11 +17,31 @@ namespace Microsoft.Unity.VisualStudio.Editor.Messaging private readonly object _disposeLock = new object(); private bool _disposed; +#if UNITY_EDITOR_WIN + [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetHandleInformation(IntPtr hObject, HandleFlags dwMask, HandleFlags dwFlags); + + [Flags] + private enum HandleFlags: uint + { + None = 0, + Inherit = 1, + ProtectFromClose = 2 + } +#endif + protected Messager(int port) { _socket = new UdpSocket(); _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, false); _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + +#if UNITY_EDITOR_WIN + // Explicitely disable inheritance for our UDP socket handle + // We found that Unity is creating a fork when importing new assets that can clone our socket + SetHandleInformation(_socket.Handle, HandleFlags.Inherit, HandleFlags.None); +#endif + _socket.Bind(IPAddress.Any, port); BeginReceiveMessage(); @@ -72,9 +92,7 @@ namespace Microsoft.Unity.VisualStudio.Editor.Messaging { message.Origin = (IPEndPoint)endPoint; - int port; - int bufferSize; - if (IsValidTcpMessage(message, out port, out bufferSize)) + if (IsValidTcpMessage(message, out var port, out var bufferSize)) { // switch to TCP mode to handle big messages TcpClient.Queue(message.Origin.Address, port, bufferSize, buffer => diff --git a/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist b/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist index 3d8975b..6c5ba04 100644 --- a/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist +++ b/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 19H1030 + 19H1217 CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration b/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration index 3ffd3e000723a2935b00e2a4e7ad7a2c141a9047..f0a8a0d8ffb9baffa89af848e7543074a7f0fe85 100644 GIT binary patch delta 13774 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.*)\.dll$)", - RegexOptions.Compiled | RegexOptions.IgnoreCase); - string[] m_ProjectSupportedExtensions = Array.Empty(); string[] m_BuiltinSupportedExtensions = Array.Empty(); @@ -102,15 +98,50 @@ namespace Microsoft.Unity.VisualStudio.Editor /// public bool SyncIfNeeded(IEnumerable affectedFiles, IEnumerable reimportedFiles) { - SetupProjectSupportedExtensions(); - - // Don't sync if we haven't synced before - if (HasSolutionBeenGenerated() && HasFilesBeenModified(affectedFiles, reimportedFiles)) + using (solutionSyncMarker.Auto()) { - Sync(); + SetupProjectSupportedExtensions(); + + // See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/ + // We create a .vsconfig file to make sure our ManagedGame workload is installed + CreateVsConfigIfNotFound(); + + // Don't sync if we haven't synced before + var affected = affectedFiles as ICollection ?? affectedFiles.ToArray(); + var reimported = reimportedFiles as ICollection ?? reimportedFiles.ToArray(); + if (!HasFilesBeenModified(affected, reimported)) + { + return false; + } + + var assemblies = m_AssemblyNameProvider.GetAssemblies(ShouldFileBePartOfSolution); + var allProjectAssemblies = RelevantAssembliesForMode(assemblies).ToList(); + SyncSolution(allProjectAssemblies); + + var allAssetProjectParts = GenerateAllAssetProjectParts(); + + var affectedNames = affected + .Select(asset => m_AssemblyNameProvider.GetAssemblyNameFromScriptPath(asset)) + .Where(name => !string.IsNullOrWhiteSpace(name)).Select(name => + name.Split(new[] {".dll"}, StringSplitOptions.RemoveEmptyEntries)[0]); + var reimportedNames = reimported + .Select(asset => m_AssemblyNameProvider.GetAssemblyNameFromScriptPath(asset)) + .Where(name => !string.IsNullOrWhiteSpace(name)).Select(name => + name.Split(new[] {".dll"}, StringSplitOptions.RemoveEmptyEntries)[0]); + var affectedAndReimported = new HashSet(affectedNames.Concat(reimportedNames)); + + foreach (var assembly in allProjectAssemblies) + { + if (!affectedAndReimported.Contains(assembly.name)) + continue; + + SyncProject(assembly, + allAssetProjectParts, + responseFilesData: ParseResponseFileData(assembly).ToArray()); + } + return true; } - return false; } private void CreateVsConfigIfNotFound() @@ -123,10 +154,10 @@ namespace Microsoft.Unity.VisualStudio.Editor var content = $@"{{ ""version"": ""1.0"", - ""components"": [ + ""components"": [ ""{Discovery.ManagedWorkload}"" ] -}} +}} "; m_FileIOProvider.WriteAllText(vsConfigFile, content); } @@ -151,6 +182,8 @@ namespace Microsoft.Unity.VisualStudio.Editor editor?.TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, searchInstallations: true, out m_CurrentInstallation); } + static ProfilerMarker solutionSyncMarker = new ProfilerMarker("SolutionSynchronizerSync"); + public void Sync() { // We need the exact VS version/capabilities to tweak project generation (analyzers/langversion) @@ -170,6 +203,7 @@ namespace Microsoft.Unity.VisualStudio.Editor { GenerateAndWriteSolutionAndProjects(); } + OnGeneratedCSProjectFiles(); } @@ -246,27 +280,19 @@ namespace Microsoft.Unity.VisualStudio.Editor { // Only synchronize assemblies that have associated source files and ones that we actually want in the project. // This also filters out DLLs coming from .asmdef files in packages. - var assemblies = m_AssemblyNameProvider.GetAssemblies(ShouldFileBePartOfSolution); + var assemblies = m_AssemblyNameProvider.GetAssemblies(ShouldFileBePartOfSolution).ToList(); var allAssetProjectParts = GenerateAllAssetProjectParts(); - var assemblyList = assemblies.ToList(); + SyncSolution(assemblies); - SyncSolution(assemblyList); + var allProjectAssemblies = RelevantAssembliesForMode(assemblies); - var allProjectAssemblies = RelevantAssembliesForMode(assemblyList).ToList(); - - foreach (Assembly assembly in allProjectAssemblies) + foreach (var assembly in allProjectAssemblies) { SyncProject(assembly, allAssetProjectParts, - responseFilesData: ParseResponseFileData(assembly), - allProjectAssemblies, -#if UNITY_2020_2_OR_NEWER - assembly.compilerOptions.RoslynAnalyzerDllPaths); -#else - Array.Empty()); -#endif + responseFilesData: ParseResponseFileData(assembly).ToArray()); } } @@ -310,7 +336,7 @@ namespace Microsoft.Unity.VisualStudio.Editor if (IsSupportedFile(asset) && ScriptingLanguage.None == ScriptingLanguageFor(asset)) { // Find assembly the asset belongs to by adding script extension and using compilation pipeline. - var assemblyName = m_AssemblyNameProvider.GetAssemblyNameFromScriptPath(asset + ".cs"); + var assemblyName = m_AssemblyNameProvider.GetAssemblyNameFromScriptPath(asset); if (string.IsNullOrEmpty(assemblyName)) { @@ -340,13 +366,11 @@ namespace Microsoft.Unity.VisualStudio.Editor private void SyncProject( Assembly assembly, Dictionary allAssetsProjectParts, - IEnumerable responseFilesData, - List allProjectAssemblies, - string[] roslynAnalyzerDllPaths) + ResponseFileData[] responseFilesData) { SyncProjectFileIfNotChanged( ProjectFile(assembly), - ProjectText(assembly, allAssetsProjectParts, responseFilesData, allProjectAssemblies, roslynAnalyzerDllPaths)); + ProjectText(assembly, allAssetsProjectParts, responseFilesData)); } private void SyncProjectFileIfNotChanged(string path, string newContents) @@ -444,11 +468,9 @@ namespace Microsoft.Unity.VisualStudio.Editor private string ProjectText(Assembly assembly, Dictionary allAssetsProjectParts, - IEnumerable responseFilesData, - List allProjectAssemblies, - string[] roslynAnalyzerDllPaths) + ResponseFileData[] responseFilesData) { - var projectBuilder = new StringBuilder(ProjectHeader(assembly, responseFilesData, roslynAnalyzerDllPaths)); + var projectBuilder = new StringBuilder(ProjectHeader(assembly, responseFilesData)); var references = new List(); projectBuilder.Append(@" ").Append(k_WindowsNewline); @@ -485,12 +507,12 @@ namespace Microsoft.Unity.VisualStudio.Editor var responseRefs = responseFilesData.SelectMany(x => x.FullPathReferences.Select(r => r)); var internalAssemblyReferences = assembly.assemblyReferences - .Where(i => !i.sourceFiles.Any(ShouldFileBePartOfSolution)).Select(i => i.outputPath); + .Where(i => !i.sourceFiles.Any(ShouldFileBePartOfSolution)).Select(i => i.outputPath); var allReferences = - assembly.compiledAssemblyReferences - .Union(responseRefs) - .Union(references) - .Union(internalAssemblyReferences); + assembly.compiledAssemblyReferences + .Union(responseRefs) + .Union(references) + .Union(internalAssemblyReferences); foreach (var reference in allReferences) { @@ -503,7 +525,7 @@ namespace Microsoft.Unity.VisualStudio.Editor if (0 < assembly.assemblyReferences.Length) { projectBuilder.Append(" ").Append(k_WindowsNewline); - foreach (Assembly reference in assembly.assemblyReferences.Where(i => i.sourceFiles.Any(ShouldFileBePartOfSolution))) + foreach (var reference in assembly.assemblyReferences.Where(i => i.sourceFiles.Any(ShouldFileBePartOfSolution))) { projectBuilder.Append(" ").Append(k_WindowsNewline); projectBuilder.Append(" {").Append(ProjectGuid(reference)).Append("}").Append(k_WindowsNewline); @@ -514,7 +536,7 @@ namespace Microsoft.Unity.VisualStudio.Editor projectBuilder.Append(@" ").Append(k_WindowsNewline); } - projectBuilder.Append(ProjectFooter()); + projectBuilder.Append(GetProjectFooter()); return projectBuilder.ToString(); } @@ -548,29 +570,20 @@ namespace Microsoft.Unity.VisualStudio.Editor } private static readonly Regex InvalidCharactersRegexPattern = new Regex(@"\?|&|\*|""|<|>|\||#|%|\^|;" + (VisualStudioEditor.IsWindows ? "" : "|:")); + public string SolutionFile() { - return Path.Combine(FileUtility.Normalize(ProjectDirectory), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName, "_")}.sln"); + return Path.Combine(ProjectDirectory.NormalizePathSeparators(), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName, "_")}.sln"); } internal string VsConfigFile() { - return Path.Combine(FileUtility.Normalize(ProjectDirectory), ".vsconfig"); + return Path.Combine(ProjectDirectory.NormalizePathSeparators(), ".vsconfig"); } - private string ProjectHeader( - Assembly assembly, - IEnumerable responseFilesData, - string[] roslynAnalyzerDllPaths - ) + internal string GetLangVersion(Assembly assembly) { - var toolsVersion = "4.0"; - var productVersion = "10.0.20506"; - const string baseDirectory = "."; - - var targetFrameworkVersion = "v4.7.1"; var targetLanguageVersion = "latest"; // danger: latest is not the same absolute value depending on the VS version. - if (m_CurrentInstallation != null) { var vsLanguageSupport = m_CurrentInstallation.LatestLanguageVersionSupported; @@ -580,43 +593,48 @@ namespace Microsoft.Unity.VisualStudio.Editor targetLanguageVersion = (vsLanguageSupport <= unityLanguageSupport ? vsLanguageSupport : unityLanguageSupport).ToString(2); // (major, minor) only } + return targetLanguageVersion; + } + + private string ProjectHeader( + Assembly assembly, + ResponseFileData[] responseFilesData + ) + { var projectType = ProjectTypeOf(assembly.name); + string rulesetPath = null; + var analyzers = Array.Empty(); - var arguments = new object[] - { - toolsVersion, - productVersion, - ProjectGuid(assembly), - XmlFilename(FileUtility.Normalize(InternalEditorUtility.GetEngineAssemblyPath())), - XmlFilename(FileUtility.Normalize(InternalEditorUtility.GetEditorAssemblyPath())), - string.Join(";", assembly.defines.Concat(responseFilesData.SelectMany(x => x.Defines)).Distinct().ToArray()), - MSBuildNamespaceUri, - assembly.name, - assembly.outputPath, - GetRootNamespace(assembly), - targetFrameworkVersion, - targetLanguageVersion, - baseDirectory, - assembly.compilerOptions.AllowUnsafeCode | responseFilesData.Any(x => x.Unsafe), - // flavoring - projectType + ":" + (int)projectType, - EditorUserBuildSettings.activeBuildTarget + ":" + (int)EditorUserBuildSettings.activeBuildTarget, - Application.unityVersion, - VisualStudioIntegration.PackageVersion() - }; - - try + if (m_CurrentInstallation != null && m_CurrentInstallation.SupportsAnalyzers) { + analyzers = m_CurrentInstallation.GetAnalyzers(); #if UNITY_2020_2_OR_NEWER - return string.Format(GetProjectHeaderTemplate(roslynAnalyzerDllPaths, assembly.compilerOptions.RoslynAnalyzerRulesetPath), arguments); -#else - return string.Format(GetProjectHeaderTemplate(Array.Empty(), null), arguments); + analyzers = analyzers != null ? analyzers.Concat(assembly.compilerOptions.RoslynAnalyzerDllPaths).ToArray() : assembly.compilerOptions.RoslynAnalyzerDllPaths; + rulesetPath = assembly.compilerOptions.RoslynAnalyzerRulesetPath; #endif } - catch (Exception) + + var projectProperties = new ProjectProperties() { - throw new NotSupportedException("Failed creating c# project because the c# project header did not have the correct amount of arguments, which is " + arguments.Length); - } + ProjectGuid = ProjectGuid(assembly), + LangVersion = GetLangVersion(assembly), + AssemblyName = assembly.name, + RootNamespace = GetRootNamespace(assembly), + OutputPath = assembly.outputPath, + // Analyzers + Analyzers = analyzers, + RulesetPath = rulesetPath, + // RSP alterable + Defines = assembly.defines.Concat(responseFilesData.SelectMany(x => x.Defines)).Distinct().ToArray(), + Unsafe = assembly.compilerOptions.AllowUnsafeCode | responseFilesData.Any(x => x.Unsafe), + // VSTU Flavoring + FlavoringProjectType = projectType + ":" + (int)projectType, + FlavoringBuildTarget = EditorUserBuildSettings.activeBuildTarget + ":" + (int)EditorUserBuildSettings.activeBuildTarget, + FlavoringUnityVersion = Application.unityVersion, + FlavoringPackageVersion = VisualStudioIntegration.PackageVersion(), + }; + + return GetProjectHeader(projectProperties); } private enum ProjectType @@ -642,9 +660,123 @@ namespace Microsoft.Unity.VisualStudio.Editor return ProjectType.Game; } + private string GetProjectHeader(ProjectProperties properties) + { + var header = new[] + { + $@"", + $@"", + $@" ", + $@" {properties.LangVersion}", + $@" ", + $@" ", + $@" Debug", + $@" AnyCPU", + $@" 10.0.20506", + $@" 2.0", + $@" {properties.RootNamespace}", + $@" {{{properties.ProjectGuid}}}", + $@" Library", + $@" Properties", + $@" {properties.AssemblyName}", + $@" v4.7.1", + $@" 512", + $@" .", + $@" ", + $@" ", + $@" true", + $@" full", + $@" false", + $@" {properties.OutputPath}", + $@" {string.Join(";", properties.Defines)}", + $@" prompt", + $@" 4", + $@" 0169", + $@" {properties.Unsafe}", + $@" ", + $@" ", + $@" pdbonly", + $@" true", + $@" Temp\bin\Release\", + $@" prompt", + $@" 4", + $@" 0169", + $@" {properties.Unsafe}", + $@" " + }; + + var forceExplicitReferences = new[] + { + $@" ", + $@" true", + $@" true", + $@" false", + $@" false", + $@" false", + $@" " + }; + + var flavoring = new[] + { + $@" ", + $@" {{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}};{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}", + $@" Package", + $@" {properties.FlavoringPackageVersion}", + $@" {properties.FlavoringProjectType}", + $@" {properties.FlavoringBuildTarget}", + $@" {properties.FlavoringUnityVersion}", + $@" " + }; + + var footer = new[] + { + @"" + }; + + var lines = header + .Concat(forceExplicitReferences) + .Concat(flavoring) + .ToList(); + + if (!string.IsNullOrEmpty(properties.RulesetPath)) + { + lines.Add(@" "); + lines.Add($" {properties.RulesetPath.MakeAbsolutePath(ProjectDirectory).NormalizePathSeparators()}"); + lines.Add(@" "); + } + + if (properties.Analyzers.Any()) + { + lines.Add(@" "); + foreach (var analyzer in properties.Analyzers) + { + lines.Add($@" "); + } + lines.Add(@" "); + } + + return string.Join(k_WindowsNewline, lines.Concat(footer)); + } + + private static string GetProjectFooter() + { + return string.Join(k_WindowsNewline, + @" ", + @" ", + @" ", + @"", + @""); + } + private static string GetSolutionText() { - return string.Join("\r\n", + return string.Join(k_WindowsNewline, @"", @"Microsoft Visual Studio Solution File, Format Version {0}", @"# Visual Studio {1}", @@ -662,126 +794,6 @@ namespace Microsoft.Unity.VisualStudio.Editor @"").Replace(" ", "\t"); } - private static string GetProjectFooterTemplate() - { - return string.Join("\r\n", - @" ", - @" ", - @" ", - @"", - @""); - } - - private string GetProjectHeaderTemplate(string[] roslynAnalyzerDllPaths, string roslynAnalyzerRulesetPath) - { - var header = new[] - { - @"", - @"", - @" ", - @" {11}", - @" ", - @" ", - @" Debug", - @" AnyCPU", - @" {1}", - @" 2.0", - @" {9}", - @" {{{2}}}", - @" Library", - @" Properties", - @" {7}", - @" {10}", - @" 512", - @" {12}", - @" ", - @" ", - @" true", - @" full", - @" false", - @" {8}", - @" {5}", - @" prompt", - @" 4", - @" 0169", - @" {13}", - @" ", - @" ", - @" pdbonly", - @" true", - @" Temp\bin\Release\", - @" prompt", - @" 4", - @" 0169", - @" {13}", - @" " - }; - - var forceExplicitReferences = new[] - { - @" ", - @" true", - @" true", - @" false", - @" false", - @" false", - @" " - }; - - var flavoring = new[] - { - @" ", - @" {{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}};{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}", - @" Package", - @" {17}", - @" {14}", - @" {15}", - @" {16}", - @" " - }; - - var footer = new[] - { - @"" - }; - - var lines = header.Concat(forceExplicitReferences).Concat(flavoring).ToList(); - - // Only add analyzer block for compatible Visual Studio - if (m_CurrentInstallation != null && m_CurrentInstallation.SupportsAnalyzers) - { -#if UNITY_2020_2_OR_NEWER - if (roslynAnalyzerRulesetPath != null) - { - lines.Add(@" "); - lines.Add($" {roslynAnalyzerRulesetPath}"); - lines.Add(@" "); - } -#endif - - string[] analyzers = m_CurrentInstallation.GetAnalyzers(); - string[] allAnalyzers = analyzers != null ? analyzers.Concat(roslynAnalyzerDllPaths).ToArray() : roslynAnalyzerDllPaths; - - if (allAnalyzers.Any()) - { - lines.Add(@" "); - foreach (var analyzer in allAnalyzers) - { - lines.Add($@" "); - } - lines.Add(@" "); - } - } - - return string.Join("\r\n", lines.Concat(footer)); - } - private void SyncSolution(IEnumerable assemblies) { if (InvalidCharactersRegexPattern.IsMatch(ProjectDirectory)) @@ -837,7 +849,7 @@ namespace Microsoft.Unity.VisualStudio.Editor if (array == null || array.Length == 0) { // HideSolution by default - array = new SolutionProperties[] { + array = new [] { new SolutionProperties() { Name = "SolutionProperties", Type = "preSolution", @@ -908,8 +920,8 @@ namespace Microsoft.Unity.VisualStudio.Editor private string EscapedRelativePathFor(string file) { - var projectDir = FileUtility.Normalize(ProjectDirectory); - file = FileUtility.Normalize(file); + var projectDir = ProjectDirectory.NormalizePathSeparators(); + file = file.NormalizePathSeparators(); var path = SkipPathPrefix(file, projectDir); var packageInfo = m_AssemblyNameProvider.FindForAssetPath(path.Replace('\\', '/')); @@ -917,7 +929,7 @@ namespace Microsoft.Unity.VisualStudio.Editor { // We have to normalize the path, because the PackageManagerRemapper assumes // dir seperators will be os specific. - var absolutePath = Path.GetFullPath(FileUtility.Normalize(path)); + var absolutePath = Path.GetFullPath(path.NormalizePathSeparators()); path = SkipPathPrefix(absolutePath, projectDir); } @@ -931,11 +943,6 @@ namespace Microsoft.Unity.VisualStudio.Editor return path; } - private static string ProjectFooter() - { - return GetProjectFooterTemplate(); - } - static string GetProjectExtension() { return ".csproj"; @@ -956,7 +963,7 @@ namespace Microsoft.Unity.VisualStudio.Editor private static string GetRootNamespace(Assembly assembly) { #if UNITY_2020_2_OR_NEWER - return assembly.rootNamespace; + return assembly.rootNamespace; #else return EditorSettings.projectGenerationRootNamespace; #endif diff --git a/Editor/ProjectGeneration/ProjectProperties.cs b/Editor/ProjectGeneration/ProjectProperties.cs new file mode 100644 index 0000000..6438f24 --- /dev/null +++ b/Editor/ProjectGeneration/ProjectProperties.cs @@ -0,0 +1,27 @@ +using System; + +namespace Microsoft.Unity.VisualStudio.Editor +{ + internal class ProjectProperties + { + public string ProjectGuid { get; set; } = string.Empty; + public string LangVersion { get; set; } = "latest"; + public string AssemblyName { get; set; } = string.Empty; + public string RootNamespace { get; set; } = string.Empty; + public string OutputPath { get; set; } = string.Empty; + + // Analyzers + public string[] Analyzers { get; set; } = Array.Empty(); + public string RulesetPath { get; set; } = string.Empty; + + // RSP alterable + public string[] Defines { get; set; } = Array.Empty(); + public bool Unsafe { get; set; } = false; + + // VSTU Flavouring + public string FlavoringProjectType { get; set; } = string.Empty; + public string FlavoringBuildTarget { get; set; } = string.Empty; + public string FlavoringUnityVersion { get; set; } = string.Empty; + public string FlavoringPackageVersion { get; set; } = string.Empty; + } +} diff --git a/Editor/ProjectGeneration/ProjectProperties.cs.meta b/Editor/ProjectGeneration/ProjectProperties.cs.meta new file mode 100644 index 0000000..d8a750a --- /dev/null +++ b/Editor/ProjectGeneration/ProjectProperties.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa7011e2ea1ff024083fea2179f3df08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Testing/TestAdaptor.cs b/Editor/Testing/TestAdaptor.cs index 63b5320..08238e3 100644 --- a/Editor/Testing/TestAdaptor.cs +++ b/Editor/Testing/TestAdaptor.cs @@ -30,7 +30,7 @@ namespace Microsoft.Unity.VisualStudio.Editor.Testing FullName = testAdaptor.FullName; Type = testAdaptor.TypeInfo?.FullName; - Method = testAdaptor?.Method?.Name; + Method = testAdaptor.Method?.Name; Assembly = testAdaptor.TypeInfo?.Assembly?.Location; Parent = parent; diff --git a/Editor/Testing/TestRunnerApiListener.cs b/Editor/Testing/TestRunnerApiListener.cs index a4db5f0..63198ca 100644 --- a/Editor/Testing/TestRunnerApiListener.cs +++ b/Editor/Testing/TestRunnerApiListener.cs @@ -8,11 +8,14 @@ namespace Microsoft.Unity.VisualStudio.Editor.Testing [InitializeOnLoad] internal class TestRunnerApiListener { - private static TestRunnerApi _testRunnerApi; - private static TestRunnerCallbacks _testRunnerCallbacks; + private static readonly TestRunnerApi _testRunnerApi; + private static readonly TestRunnerCallbacks _testRunnerCallbacks; static TestRunnerApiListener() { + if (!VisualStudioEditor.IsEnabled) + return; + _testRunnerApi = ScriptableObject.CreateInstance(); _testRunnerCallbacks = new TestRunnerCallbacks(); @@ -26,7 +29,7 @@ namespace Microsoft.Unity.VisualStudio.Editor.Testing private static void RetrieveTestList(TestMode mode) { - _testRunnerApi.RetrieveTestList(mode, (ta) => _testRunnerCallbacks.TestListRetrieved(mode, ta)); + _testRunnerApi?.RetrieveTestList(mode, ta => _testRunnerCallbacks.TestListRetrieved(mode, ta)); } public static void ExecuteTests(string command) @@ -41,12 +44,12 @@ namespace Microsoft.Unity.VisualStudio.Editor.Testing var testMode = (TestMode)Enum.Parse(typeof(TestMode), command.Substring(0, index)); var filter = command.Substring(index + 1); - ExecuteTests(new Filter() { testMode = testMode, testNames = new string[] { filter } }); + ExecuteTests(new Filter { testMode = testMode, testNames = new [] { filter } }); } private static void ExecuteTests(Filter filter) { - _testRunnerApi.Execute(new ExecutionSettings(filter)); + _testRunnerApi?.Execute(new ExecutionSettings(filter)); } } } diff --git a/Editor/UnityInstallation.cs b/Editor/UnityInstallation.cs index 74797cd..89f1793 100644 --- a/Editor/UnityInstallation.cs +++ b/Editor/UnityInstallation.cs @@ -10,6 +10,33 @@ namespace Microsoft.Unity.VisualStudio.Editor { internal static class UnityInstallation { + public static bool IsMainUnityEditorProcess + { + get + { +#if UNITY_2020_2_OR_NEWER + if (UnityEditor.AssetDatabase.IsAssetImportWorkerProcess()) + return false; +#elif UNITY_2019_3_OR_NEWER + if (UnityEditor.Experimental.AssetDatabaseExperimental.IsAssetImportWorkerProcess()) + return false; +#endif + +#if UNITY_2021_1_OR_NEWER + if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Secondary) + return false; +#elif UNITY_2020_2_OR_NEWER + if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Slave) + return false; +#elif UNITY_2020_1_OR_NEWER + if (global::Unity.MPE.ProcessService.level == global::Unity.MPE.ProcessLevel.UMP_SLAVE) + return false; +#endif + + return true; + } + } + public static Version LatestLanguageVersionSupported(Assembly assembly) { #if UNITY_2020_2_OR_NEWER diff --git a/Editor/UsageUtility.cs b/Editor/UsageUtility.cs index 9928524..b963d50 100644 --- a/Editor/UsageUtility.cs +++ b/Editor/UsageUtility.cs @@ -64,7 +64,7 @@ namespace Microsoft.Unity.VisualStudio.Editor var scene = SceneManager.GetSceneByPath(scenePath.Replace(Path.DirectorySeparatorChar, '/')); if (!scene.isLoaded) { - var result = UnityEditor.EditorUtility.DisplayDialogComplex("Show Usage", + var result = EditorUtility.DisplayDialogComplex("Show Usage", $"Do you want to open \"{Path.GetFileName(scenePath)}\"?", "Open Scene", "Cancel", diff --git a/Editor/VisualStudioEditor.cs b/Editor/VisualStudioEditor.cs index 60f5410..20e3d3b 100644 --- a/Editor/VisualStudioEditor.cs +++ b/Editor/VisualStudioEditor.cs @@ -35,6 +35,9 @@ namespace Microsoft.Unity.VisualStudio.Editor static VisualStudioEditor() { + if (!UnityInstallation.IsMainUnityEditorProcess) + return; + if (IsWindows) Discovery.FindVSWhere(); @@ -54,11 +57,11 @@ namespace Microsoft.Unity.VisualStudio.Editor catch (Exception ex) { UnityEngine.Debug.LogError($"Error detecting Visual Studio installations: {ex}"); - return Array.Empty(); + return Array.Empty(); } } - internal static bool IsEnabled => CodeEditor.CurrentEditor is VisualStudioEditor; + internal static bool IsEnabled => CodeEditor.CurrentEditor is VisualStudioEditor && UnityInstallation.IsMainUnityEditorProcess; public void CreateIfDoesntExist() { @@ -243,7 +246,6 @@ namespace Microsoft.Unity.VisualStudio.Editor return "Registry packages"; case ProjectGenerationFlag.Unknown: return "Packages from unknown sources"; - case ProjectGenerationFlag.None: default: return string.Empty; } @@ -286,9 +288,7 @@ namespace Microsoft.Unity.VisualStudio.Editor private bool OpenWindowsApp(string path, int line) { - var progpath = FileUtility - .FindPackageAssetFullPath("COMIntegration a:packages", "COMIntegration.exe") - .FirstOrDefault(); + var progpath = FileUtility.GetPackageAssetFullPath("Editor", "COMIntegration", "Release", "COMIntegration.exe"); if (string.IsNullOrWhiteSpace(progpath)) return false; diff --git a/Editor/VisualStudioInstallation.cs b/Editor/VisualStudioInstallation.cs index e1602c7..07c3e70 100644 --- a/Editor/VisualStudioInstallation.cs +++ b/Editor/VisualStudioInstallation.cs @@ -158,6 +158,10 @@ namespace Microsoft.Unity.VisualStudio.Editor var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}"); if (File.Exists(IOPath.Combine(addinPath, addinAssembly))) return addinPath; + + addinPath = IOPath.Combine(Path, $"Contents/MonoBundle/Addins/{addinName}"); + if (File.Exists(IOPath.Combine(addinPath, addinAssembly))) + return addinPath; } return null; diff --git a/Editor/VisualStudioIntegration.cs b/Editor/VisualStudioIntegration.cs index 50fbaf5..5683170 100644 --- a/Editor/VisualStudioIntegration.cs +++ b/Editor/VisualStudioIntegration.cs @@ -240,10 +240,9 @@ namespace Microsoft.Unity.VisualStudio.Editor private static void CheckClient(Message message) { - Client client; var endPoint = message.Origin; - if (!_clients.TryGetValue(endPoint, out client)) + if (!_clients.TryGetValue(endPoint, out var client)) { client = new Client { diff --git a/ValidationConfig.json b/ValidationConfig.json new file mode 100644 index 0000000..6a638b8 --- /dev/null +++ b/ValidationConfig.json @@ -0,0 +1,19 @@ +{ + "FileContentKeywordValidation": + { + "Keywords": + [ + { + "Targets": "+", + "Files": + [ + "Editor/Discovery.cs" + ], + "Patterns": + [ + "vswhere\\.exe" + ] + } + ] + } +} \ No newline at end of file diff --git a/ValidationConfig.json.meta b/ValidationConfig.json.meta new file mode 100644 index 0000000..5f57eee --- /dev/null +++ b/ValidationConfig.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a3f71b076c36a204a864f8c44142b3a9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ValidationExceptions.json b/ValidationExceptions.json new file mode 100644 index 0000000..4db0381 --- /dev/null +++ b/ValidationExceptions.json @@ -0,0 +1,10 @@ +{ + "ErrorExceptions": [ + { + "ValidationTest": "API Validation", + "ExceptionMessage": "Failed comparing against assemblies of previously promoted version of package. \nThis is most likely because the assemblies that were compared against were built with a different version of Unity. \nIf you are certain that there are no API changes warranting bumping the package version then you can add an exception for this error:\nRead more about this error and potential solutions at https://docs.unity3d.com/Packages/com.unity.package-validation-suite@latest/index.html?preview=1&subfolder=/manual/validation_exceptions.html#", + "PackageVersion": "2.0.11" + } + ], + "WarningExceptions": [] +} \ No newline at end of file diff --git a/ValidationExceptions.json.meta b/ValidationExceptions.json.meta new file mode 100644 index 0000000..b0b2427 --- /dev/null +++ b/ValidationExceptions.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 788fbcfc0abc6c54b9504cfee3176c22 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index 60df51b..792e140 100644 --- a/package.json +++ b/package.json @@ -2,21 +2,21 @@ "name": "com.unity.ide.visualstudio", "displayName": "Visual Studio Editor", "description": "Code editor integration for supporting Visual Studio as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.", - "version": "2.0.9", + "version": "2.0.11", "unity": "2019.4", - "unityRelease": "21f1", + "unityRelease": "25f1", "dependencies": { "com.unity.test-framework": "1.1.9" }, "relatedPackages": { - "com.unity.ide.visualstudio.tests": "2.0.9" + "com.unity.ide.visualstudio.tests": "2.0.11" }, "upmCi": { - "footprint": "b1cf463b7fca9fa8a65053f6120bce69640f5cea" + "footprint": "321c90e37eb1ab86fe68d5f7e838c7e4553fc37e" }, "repository": { "url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git", "type": "git", - "revision": "c7cd4c6319806423b3d918b3e9599244e3993d41" + "revision": "680e06d94ffca728223ae2cb14bb919cd354e2c1" } }