com.unity.ide.visualstudio@2.0.5

## [2.0.5] - 2020-10-30

Integration:

Disable legacy pdb symbol checking for Unity packages
This commit is contained in:
Unity Technologies
2020-10-30 00:00:00 +00:00
parent 32027b422b
commit 08ab0fbd80
25 changed files with 1429 additions and 1105 deletions

View File

@@ -1,5 +1,12 @@
# Code Editor Package for Visual Studio # Code Editor Package for Visual Studio
## [2.0.5] - 2020-10-30
Integration:
Disable legacy pdb symbol checking for Unity packages
## [2.0.3] - 2020-09-09 ## [2.0.3] - 2020-09-09
Project generation: Project generation:

4
Editor/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d791d407901442e49862d3aa783ce8af
timeCreated: 1602756877

View File

@@ -15,7 +15,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
internal static class Discovery internal static class Discovery
{ {
public static IEnumerable<VisualStudioInstallation> GetVisualStudioInstallations() internal const string ManagedWorkload = "Microsoft.VisualStudio.Workload.ManagedGame";
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
{ {
if (VisualStudioEditor.IsWindows) if (VisualStudioEditor.IsWindows)
{ {
@@ -45,7 +48,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return false; return false;
} }
public static bool TryDiscoverInstallation(string editorPath, out VisualStudioInstallation installation) public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
{ {
installation = null; installation = null;
@@ -82,7 +85,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
} }
#region VsWhere Json Schema #region VsWhere Json Schema
#pragma warning disable CS0649 #pragma warning disable CS0649
[Serializable] [Serializable]
internal class VsWhereResult internal class VsWhereResult
{ {
@@ -95,7 +98,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
public IEnumerable<VisualStudioInstallation> ToVisualStudioInstallations() public IEnumerable<VisualStudioInstallation> ToVisualStudioInstallations()
{ {
foreach(var entry in entries) foreach (var entry in entries)
{ {
yield return new VisualStudioInstallation() yield return new VisualStudioInstallation()
{ {
@@ -123,7 +126,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0" public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0"
public string buildVersion; public string buildVersion;
} }
#pragma warning restore CS3021 #pragma warning restore CS3021
#endregion #endregion
private static IEnumerable<VisualStudioInstallation> QueryVsWhere() private static IEnumerable<VisualStudioInstallation> QueryVsWhere()

View File

@@ -49,6 +49,14 @@ namespace Microsoft.Unity.VisualStudio.Editor
return path.Replace(string.Concat(WinSeparator, WinSeparator), WinSeparator.ToString()); return path.Replace(string.Concat(WinSeparator, WinSeparator), WinSeparator.ToString());
} }
public static string NormalizeWindowsToUnix(string path)
{
if (string.IsNullOrEmpty(path))
return path;
return path.Replace(WinSeparator, UnixSeparator);
}
internal static bool IsFileInProjectDirectory(string fileName) internal static bool IsFileInProjectDirectory(string fileName)
{ {
var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, "..")); var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));

View File

@@ -7,28 +7,29 @@ using System.IO;
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor
{ {
public sealed class Image : IDisposable { public sealed class Image : IDisposable
{
long position; long position;
Stream stream; Stream stream;
Image (Stream stream) Image(Stream stream)
{ {
this.stream = stream; this.stream = stream;
this.position = stream.Position; this.position = stream.Position;
this.stream.Position = 0; this.stream.Position = 0;
} }
bool Advance (int length) bool Advance(int length)
{ {
if (stream.Position + length >= stream.Length) if (stream.Position + length >= stream.Length)
return false; return false;
stream.Seek (length, SeekOrigin.Current); stream.Seek(length, SeekOrigin.Current);
return true; return true;
} }
bool MoveTo (uint position) bool MoveTo(uint position)
{ {
if (position >= stream.Length) if (position >= stream.Length)
return false; return false;
@@ -37,65 +38,65 @@ namespace Microsoft.Unity.VisualStudio.Editor
return true; return true;
} }
void IDisposable.Dispose () void IDisposable.Dispose()
{ {
stream.Position = position; stream.Position = position;
} }
ushort ReadUInt16 () ushort ReadUInt16()
{ {
return (ushort) (stream.ReadByte () return (ushort)(stream.ReadByte()
| (stream.ReadByte () << 8)); | (stream.ReadByte() << 8));
} }
uint ReadUInt32 () uint ReadUInt32()
{ {
return (uint) (stream.ReadByte () return (uint)(stream.ReadByte()
| (stream.ReadByte () << 8) | (stream.ReadByte() << 8)
| (stream.ReadByte () << 16) | (stream.ReadByte() << 16)
| (stream.ReadByte () << 24)); | (stream.ReadByte() << 24));
} }
bool IsManagedAssembly () bool IsManagedAssembly()
{ {
if (stream.Length < 318) if (stream.Length < 318)
return false; return false;
if (ReadUInt16 () != 0x5a4d) if (ReadUInt16() != 0x5a4d)
return false; return false;
if (!Advance (58)) if (!Advance(58))
return false; return false;
if (!MoveTo (ReadUInt32 ())) if (!MoveTo(ReadUInt32()))
return false; return false;
if (ReadUInt32 () != 0x00004550) if (ReadUInt32() != 0x00004550)
return false; return false;
if (!Advance (20)) if (!Advance(20))
return false; return false;
if (!Advance (ReadUInt16 () == 0x20b ? 222 : 206)) if (!Advance(ReadUInt16() == 0x20b ? 222 : 206))
return false; return false;
return ReadUInt32 () != 0; return ReadUInt32() != 0;
} }
public static bool IsAssembly (string file) public static bool IsAssembly(string file)
{ {
if (file == null) if (file == null)
throw new ArgumentNullException ("file"); throw new ArgumentNullException("file");
using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
return IsAssembly (stream); return IsAssembly(stream);
} }
public static bool IsAssembly (Stream stream) public static bool IsAssembly(Stream stream)
{ {
if (stream == null) if (stream == null)
throw new ArgumentNullException (nameof(stream)); throw new ArgumentNullException(nameof(stream));
if (!stream.CanRead) if (!stream.CanRead)
throw new ArgumentException (nameof(stream)); throw new ArgumentException(nameof(stream));
if (!stream.CanSeek) if (!stream.CanSeek)
throw new ArgumentException (nameof(stream)); throw new ArgumentException(nameof(stream));
using (var image = new Image (stream)) using (var image = new Image(stream))
return image.IsManagedAssembly (); return image.IsManagedAssembly();
} }
} }
} }

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 058b02c03ea09473aab4dc5fc50af1ef guid: 543eb5eeeb1d5424ca8876b93fad5326
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BuildMachineOSBuild</key> <key>BuildMachineOSBuild</key>
<string>19B88</string> <string>19H2</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@@ -14,6 +14,8 @@
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>AppleEventIntegration</string> <string>AppleEventIntegration</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
@@ -25,17 +27,21 @@
<key>DTCompiler</key> <key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string> <string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key> <key>DTPlatformBuild</key>
<string>10E125</string> <string>12A7300</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key> <key>DTPlatformVersion</key>
<string>GM</string> <string>10.15.6</string>
<key>DTSDKBuild</key> <key>DTSDKBuild</key>
<string>18E219</string> <string>19G68</string>
<key>DTSDKName</key> <key>DTSDKName</key>
<string>macosx10.14</string> <string>macosx10.15</string>
<key>DTXcode</key> <key>DTXcode</key>
<string>1020</string> <string>1201</string>
<key>DTXcodeBuild</key> <key>DTXcodeBuild</key>
<string>10E125</string> <string>12A7300</string>
<key>LSMinimumSystemVersion</key>
<string>10.13</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Unity. All rights reserved.</string> <string>Copyright © 2019 Unity. All rights reserved.</string>
</dict> </dict>

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 680cf1008b4284eddbb82ec4d76644a1 guid: 29239d79a3471495e9d270601006dad7
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: ba4355216f6c44abbb17503872c42c97 guid: e811c7e1c1e9a4b50b237772d317959f
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 4e53c7f7b5c7e4a3d890cb596ed56c22 guid: 9c3599bc139404df2955d3ffd39d60d6
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 11ca2399a9422473eb66bca747f3ad52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict/>
<key>files2</key>
<dict/>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3379e8bd711774041a330f218af69b7a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Unity Technologies.
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -54,7 +59,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
if (assembly.sourceFiles.Any(shouldFileBePartOfSolution)) if (assembly.sourceFiles.Any(shouldFileBePartOfSolution))
{ {
var options = new ScriptCompilerOptions() var options = new ScriptCompilerOptions
{ {
ResponseFiles = assembly.compilerOptions.ResponseFiles, ResponseFiles = assembly.compilerOptions.ResponseFiles,
AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode, AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode,
@@ -79,14 +84,15 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.Player).Where(assembly => assembly.sourceFiles.Any(shouldFileBePartOfSolution))) foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.Player).Where(assembly => assembly.sourceFiles.Any(shouldFileBePartOfSolution)))
{ {
var options = new ScriptCompilerOptions() var options = new ScriptCompilerOptions
{ {
ResponseFiles = assembly.compilerOptions.ResponseFiles, ResponseFiles = assembly.compilerOptions.ResponseFiles,
AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode, AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode,
ApiCompatibilityLevel = assembly.compilerOptions.ApiCompatibilityLevel ApiCompatibilityLevel = assembly.compilerOptions.ApiCompatibilityLevel
}; };
yield return new Assembly(assembly.name, @"Temp\Bin\Debug\Player\", yield return
new Assembly(assembly.name, @"Temp\Bin\Debug\Player\",
assembly.sourceFiles, assembly.sourceFiles,
new[] { "DEBUG", "TRACE" }.Concat(assembly.defines).ToArray(), new[] { "DEBUG", "TRACE" }.Concat(assembly.defines).ToArray(),
assembly.assemblyReferences, assembly.assemblyReferences,
@@ -104,14 +110,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
public string GetCompileOutputPath(string assemblyName) public string GetCompileOutputPath(string assemblyName)
{ {
if (assemblyName.EndsWith(".Player", StringComparison.Ordinal)) return assemblyName.EndsWith(".Player", StringComparison.Ordinal) ? @"Temp\Bin\Debug\Player\" : @"Temp\Bin\Debug\";
{
return @"Temp\Bin\Debug\Player\";
}
else
{
return @"Temp\Bin\Debug\";
}
} }
public IEnumerable<string> GetAllAssetPaths() public IEnumerable<string> GetAllAssetPaths()

View File

@@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Unity Technologies.
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
using System.IO; using System.IO;
using System.Text; using System.Text;

View File

@@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Unity Technologies.
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor
{ {
public interface IGUIDGenerator public interface IGUIDGenerator

View File

@@ -15,7 +15,6 @@ using System.Text.RegularExpressions;
using Unity.CodeEditor; using Unity.CodeEditor;
using UnityEditor; using UnityEditor;
using UnityEditor.Compilation; using UnityEditor.Compilation;
using UnityEditor.PackageManager;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
@@ -41,6 +40,8 @@ namespace Microsoft.Unity.VisualStudio.Editor
public class ProjectGeneration : IGenerator public class ProjectGeneration : IGenerator
{ {
public static readonly string MSBuildNamespaceUri = "http://schemas.microsoft.com/developer/msbuild/2003"; public static readonly string MSBuildNamespaceUri = "http://schemas.microsoft.com/developer/msbuild/2003";
public IAssemblyNameProvider AssemblyNameProvider => m_AssemblyNameProvider;
public string ProjectDirectory { get; }
const string k_WindowsNewline = "\r\n"; const string k_WindowsNewline = "\r\n";
@@ -58,17 +59,15 @@ namespace Microsoft.Unity.VisualStudio.Editor
@"^Library.ScriptAssemblies.(?<dllname>(?<project>.*)\.dll$)", @"^Library.ScriptAssemblies.(?<dllname>(?<project>.*)\.dll$)",
RegexOptions.Compiled | RegexOptions.IgnoreCase); RegexOptions.Compiled | RegexOptions.IgnoreCase);
string[] m_ProjectSupportedExtensions = new string[0]; string[] m_ProjectSupportedExtensions = Array.Empty<string>();
string[] m_BuiltinSupportedExtensions = new string[0]; string[] m_BuiltinSupportedExtensions = Array.Empty<string>();
public string ProjectDirectory { get; }
readonly string m_ProjectName; readonly string m_ProjectName;
readonly IAssemblyNameProvider m_AssemblyNameProvider; readonly IAssemblyNameProvider m_AssemblyNameProvider;
readonly IFileIO m_FileIOProvider; readonly IFileIO m_FileIOProvider;
readonly IGUIDGenerator m_GUIDGenerator; readonly IGUIDGenerator m_GUIDGenerator;
VisualStudioInstallation m_CurrentInstallation; bool m_ShouldGenerateAll;
public IAssemblyNameProvider AssemblyNameProvider => m_AssemblyNameProvider; IVisualStudioInstallation m_CurrentInstallation;
public ProjectGeneration() : this(Directory.GetParent(Application.dataPath).FullName) public ProjectGeneration() : this(Directory.GetParent(Application.dataPath).FullName)
{ {
@@ -80,7 +79,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
public ProjectGeneration(string tempDirectory, IAssemblyNameProvider assemblyNameProvider, IFileIO fileIoProvider, IGUIDGenerator guidGenerator) public ProjectGeneration(string tempDirectory, IAssemblyNameProvider assemblyNameProvider, IFileIO fileIoProvider, IGUIDGenerator guidGenerator)
{ {
ProjectDirectory = tempDirectory.Replace('\\', '/'); ProjectDirectory = FileUtility.NormalizeWindowsToUnix(tempDirectory);
m_ProjectName = Path.GetFileName(ProjectDirectory); m_ProjectName = Path.GetFileName(ProjectDirectory);
m_AssemblyNameProvider = assemblyNameProvider; m_AssemblyNameProvider = assemblyNameProvider;
m_FileIOProvider = fileIoProvider; m_FileIOProvider = fileIoProvider;
@@ -105,6 +104,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
SetupProjectSupportedExtensions(); 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 // Don't sync if we haven't synced before
if (HasSolutionBeenGenerated() && HasFilesBeenModified(affectedFiles, reimportedFiles)) if (HasSolutionBeenGenerated() && HasFilesBeenModified(affectedFiles, reimportedFiles))
{ {
@@ -114,12 +117,34 @@ namespace Microsoft.Unity.VisualStudio.Editor
return false; return false;
} }
bool HasFilesBeenModified(IEnumerable<string> affectedFiles, IEnumerable<string> reimportedFiles) private void CreateVsConfigIfNotFound()
{
try
{
var vsConfigFile = VsConfigFile();
if (m_FileIOProvider.Exists(vsConfigFile))
return;
var content = $@"{{
""version"": ""1.0"",
""components"": [
""{Discovery.ManagedWorkload}""
]
}}
";
m_FileIOProvider.WriteAllText(vsConfigFile, content);
}
catch (IOException)
{
}
}
private bool HasFilesBeenModified(IEnumerable<string> affectedFiles, IEnumerable<string> reimportedFiles)
{ {
return affectedFiles.Any(ShouldFileBePartOfSolution) || reimportedFiles.Any(ShouldSyncOnReimportedAsset); return affectedFiles.Any(ShouldFileBePartOfSolution) || reimportedFiles.Any(ShouldSyncOnReimportedAsset);
} }
static bool ShouldSyncOnReimportedAsset(string asset) private static bool ShouldSyncOnReimportedAsset(string asset)
{ {
return k_ReimportSyncExtensions.Contains(new FileInfo(asset).Extension); return k_ReimportSyncExtensions.Contains(new FileInfo(asset).Extension);
} }
@@ -150,13 +175,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
return m_FileIOProvider.Exists(SolutionFile()); return m_FileIOProvider.Exists(SolutionFile());
} }
void SetupProjectSupportedExtensions() private void SetupProjectSupportedExtensions()
{ {
m_ProjectSupportedExtensions = m_AssemblyNameProvider.ProjectSupportedExtensions; m_ProjectSupportedExtensions = m_AssemblyNameProvider.ProjectSupportedExtensions;
m_BuiltinSupportedExtensions = EditorSettings.projectGenerationBuiltinExtensions; m_BuiltinSupportedExtensions = EditorSettings.projectGenerationBuiltinExtensions;
} }
bool ShouldFileBePartOfSolution(string file) private bool ShouldFileBePartOfSolution(string file)
{ {
// Exclude files coming from packages except if they are internalized. // Exclude files coming from packages except if they are internalized.
if (m_AssemblyNameProvider.IsInternalizedPackagePath(file)) if (m_AssemblyNameProvider.IsInternalizedPackagePath(file))
@@ -167,7 +192,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return IsSupportedFile(file); return IsSupportedFile(file);
} }
static string GetExtensionWithoutDot(string path) private static string GetExtensionWithoutDot(string path)
{ {
// Prevent re-processing and information loss // Prevent re-processing and information loss
if (!Path.HasExtension(path)) if (!Path.HasExtension(path))
@@ -199,7 +224,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return false; return false;
} }
static ScriptingLanguage ScriptingLanguageFor(Assembly assembly) private static ScriptingLanguage ScriptingLanguageFor(Assembly assembly)
{ {
var files = assembly.sourceFiles; var files = assembly.sourceFiles;
@@ -209,7 +234,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return ScriptingLanguageFor(files[0]); return ScriptingLanguageFor(files[0]);
} }
static ScriptingLanguage ScriptingLanguageFor(string path) internal static ScriptingLanguage ScriptingLanguageFor(string path)
{ {
return GetExtensionWithoutDot(path) == "cs" ? ScriptingLanguage.CSharp : ScriptingLanguage.None; return GetExtensionWithoutDot(path) == "cs" ? ScriptingLanguage.CSharp : ScriptingLanguage.None;
} }
@@ -225,15 +250,24 @@ namespace Microsoft.Unity.VisualStudio.Editor
var assemblyList = assemblies.ToList(); var assemblyList = assemblies.ToList();
SyncSolution(assemblyList); SyncSolution(assemblyList);
var allProjectAssemblies = RelevantAssembliesForMode(assemblyList).ToList(); var allProjectAssemblies = RelevantAssembliesForMode(assemblyList).ToList();
foreach (Assembly assembly in allProjectAssemblies) foreach (Assembly assembly in allProjectAssemblies)
{ {
var responseFileData = ParseResponseFileData(assembly); SyncProject(assembly,
SyncProject(assembly, allAssetProjectParts, responseFileData, allProjectAssemblies); allAssetProjectParts,
responseFilesData: ParseResponseFileData(assembly),
allProjectAssemblies,
#if UNITY_2020_2_OR_NEWER
assembly.compilerOptions.RoslynAnalyzerDllPaths);
#else
Array.Empty<string>());
#endif
} }
} }
IEnumerable<ResponseFileData> ParseResponseFileData(Assembly assembly) private IEnumerable<ResponseFileData> ParseResponseFileData(Assembly assembly)
{ {
var systemReferenceDirectories = CompilationPipeline.GetSystemAssemblyDirectories(assembly.compilerOptions.ApiCompatibilityLevel); var systemReferenceDirectories = CompilationPipeline.GetSystemAssemblyDirectories(assembly.compilerOptions.ApiCompatibilityLevel);
@@ -258,7 +292,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return responseFilesData.Select(x => x.Value); return responseFilesData.Select(x => x.Value);
} }
Dictionary<string, string> GenerateAllAssetProjectParts() private Dictionary<string, string> GenerateAllAssetProjectParts()
{ {
Dictionary<string, StringBuilder> stringBuilders = new Dictionary<string, StringBuilder>(); Dictionary<string, StringBuilder> stringBuilders = new Dictionary<string, StringBuilder>();
@@ -300,16 +334,19 @@ namespace Microsoft.Unity.VisualStudio.Editor
return result; return result;
} }
void SyncProject( private void SyncProject(
Assembly assembly, Assembly assembly,
Dictionary<string, string> allAssetsProjectParts, Dictionary<string, string> allAssetsProjectParts,
IEnumerable<ResponseFileData> responseFilesData, IEnumerable<ResponseFileData> responseFilesData,
List<Assembly> allProjectAssemblies) List<Assembly> allProjectAssemblies,
string[] roslynAnalyzerDllPaths)
{ {
SyncProjectFileIfNotChanged(ProjectFile(assembly), ProjectText(assembly, allAssetsProjectParts, responseFilesData, allProjectAssemblies)); SyncProjectFileIfNotChanged(
ProjectFile(assembly),
ProjectText(assembly, allAssetsProjectParts, responseFilesData, allProjectAssemblies, roslynAnalyzerDllPaths));
} }
void SyncProjectFileIfNotChanged(string path, string newContents) private void SyncProjectFileIfNotChanged(string path, string newContents)
{ {
if (Path.GetExtension(path) == ".csproj") if (Path.GetExtension(path) == ".csproj")
{ {
@@ -319,34 +356,34 @@ namespace Microsoft.Unity.VisualStudio.Editor
SyncFileIfNotChanged(path, newContents); SyncFileIfNotChanged(path, newContents);
} }
void SyncSolutionFileIfNotChanged(string path, string newContents) private void SyncSolutionFileIfNotChanged(string path, string newContents)
{ {
newContents = OnGeneratedSlnSolution(path, newContents); newContents = OnGeneratedSlnSolution(path, newContents);
SyncFileIfNotChanged(path, newContents); SyncFileIfNotChanged(path, newContents);
} }
static IEnumerable<SR.MethodInfo> GetPostProcessorCallbacks(string name) private static IEnumerable<SR.MethodInfo> GetPostProcessorCallbacks(string name)
{ {
return TypeCache return TypeCache
.GetTypesDerivedFrom<AssetPostprocessor>() .GetTypesDerivedFrom<AssetPostprocessor>()
.Select(t => t.GetMethod(name, SR.BindingFlags.Public | SR.BindingFlags.NonPublic | SR.BindingFlags.Static)) .Select(t => t.GetMethod(name, SR.BindingFlags.Public | SR.BindingFlags.NonPublic | SR.BindingFlags.Static))
.Where(m => m!= null); .Where(m => m != null);
} }
static void OnGeneratedCSProjectFiles() static void OnGeneratedCSProjectFiles()
{ {
foreach(var method in GetPostProcessorCallbacks(nameof(OnGeneratedCSProjectFiles))) foreach (var method in GetPostProcessorCallbacks(nameof(OnGeneratedCSProjectFiles)))
{ {
method.Invoke(null, Array.Empty<object>()); method.Invoke(null, Array.Empty<object>());
} }
} }
static bool OnPreGeneratingCSProjectFiles() private static bool OnPreGeneratingCSProjectFiles()
{ {
bool result = false; bool result = false;
foreach(var method in GetPostProcessorCallbacks(nameof(OnPreGeneratingCSProjectFiles))) foreach (var method in GetPostProcessorCallbacks(nameof(OnPreGeneratingCSProjectFiles)))
{ {
var retValue = method.Invoke(null, Array.Empty<object>()); var retValue = method.Invoke(null, Array.Empty<object>());
if (method.ReturnType == typeof(bool)) if (method.ReturnType == typeof(bool))
@@ -358,11 +395,11 @@ namespace Microsoft.Unity.VisualStudio.Editor
return result; return result;
} }
static string InvokeAssetPostProcessorGenerationCallbacks(string name, string path, string content) private static string InvokeAssetPostProcessorGenerationCallbacks(string name, string path, string content)
{ {
foreach(var method in GetPostProcessorCallbacks(name)) foreach (var method in GetPostProcessorCallbacks(name))
{ {
var args = new [] { path, content }; var args = new[] { path, content };
var returnValue = method.Invoke(null, args); var returnValue = method.Invoke(null, args);
if (method.ReturnType == typeof(string)) if (method.ReturnType == typeof(string))
{ {
@@ -374,17 +411,17 @@ namespace Microsoft.Unity.VisualStudio.Editor
return content; return content;
} }
static string OnGeneratedCSProject(string path, string content) private static string OnGeneratedCSProject(string path, string content)
{ {
return InvokeAssetPostProcessorGenerationCallbacks(nameof(OnGeneratedCSProject), path, content); return InvokeAssetPostProcessorGenerationCallbacks(nameof(OnGeneratedCSProject), path, content);
} }
static string OnGeneratedSlnSolution(string path, string content) private static string OnGeneratedSlnSolution(string path, string content)
{ {
return InvokeAssetPostProcessorGenerationCallbacks(nameof(OnGeneratedSlnSolution), path, content); return InvokeAssetPostProcessorGenerationCallbacks(nameof(OnGeneratedSlnSolution), path, content);
} }
void SyncFileIfNotChanged(string filename, string newContents) private void SyncFileIfNotChanged(string filename, string newContents)
{ {
try try
{ {
@@ -401,12 +438,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
m_FileIOProvider.WriteAllText(filename, newContents); m_FileIOProvider.WriteAllText(filename, newContents);
} }
string ProjectText(Assembly assembly, private string ProjectText(Assembly assembly,
Dictionary<string, string> allAssetsProjectParts, Dictionary<string, string> allAssetsProjectParts,
IEnumerable<ResponseFileData> responseFilesData, IEnumerable<ResponseFileData> responseFilesData,
List<Assembly> allProjectAsemblies) List<Assembly> allProjectAssemblies,
string[] roslynAnalyzerDllPaths)
{ {
var projectBuilder = new StringBuilder(ProjectHeader(assembly, responseFilesData)); var projectBuilder = new StringBuilder(ProjectHeader(assembly, responseFilesData, roslynAnalyzerDllPaths));
var references = new List<string>(); var references = new List<string>();
projectBuilder.Append(@" <ItemGroup>").Append(k_WindowsNewline); projectBuilder.Append(@" <ItemGroup>").Append(k_WindowsNewline);
@@ -442,6 +480,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
.Union(responseRefs) .Union(responseRefs)
.Union(references) .Union(references)
.Union(internalAssemblyReferences); .Union(internalAssemblyReferences);
foreach (var reference in allReferences) foreach (var reference in allReferences)
{ {
string fullReference = Path.IsPathRooted(reference) ? reference : Path.Combine(ProjectDirectory, reference); string fullReference = Path.IsPathRooted(reference) ? reference : Path.Combine(ProjectDirectory, reference);
@@ -468,7 +507,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return projectBuilder.ToString(); return projectBuilder.ToString();
} }
static string XmlFilename(string path) private static string XmlFilename(string path)
{ {
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
return path; return path;
@@ -479,12 +518,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
return XmlEscape(path); return XmlEscape(path);
} }
static string XmlEscape(string s) private static string XmlEscape(string s)
{ {
return SecurityElement.Escape(s); return SecurityElement.Escape(s);
} }
void AppendReference(string fullReference, StringBuilder projectBuilder) private void AppendReference(string fullReference, StringBuilder projectBuilder)
{ {
var escapedFullPath = EscapedRelativePathFor(fullReference); var escapedFullPath = EscapedRelativePathFor(fullReference);
projectBuilder.Append(" <Reference Include=\"").Append(Path.GetFileNameWithoutExtension(escapedFullPath)).Append("\">").Append(k_WindowsNewline); projectBuilder.Append(" <Reference Include=\"").Append(Path.GetFileNameWithoutExtension(escapedFullPath)).Append("\">").Append(k_WindowsNewline);
@@ -500,12 +539,18 @@ namespace Microsoft.Unity.VisualStudio.Editor
private static readonly Regex InvalidCharactersRegexPattern = new Regex(@"\?|&|\*|""|<|>|\||#|%|\^|;" + (VisualStudioEditor.IsWindows ? "" : "|:")); private static readonly Regex InvalidCharactersRegexPattern = new Regex(@"\?|&|\*|""|<|>|\||#|%|\^|;" + (VisualStudioEditor.IsWindows ? "" : "|:"));
public string SolutionFile() public string SolutionFile()
{ {
return Path.Combine(FileUtility.Normalize(ProjectDirectory), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName,"_")}.sln"); return Path.Combine(FileUtility.Normalize(ProjectDirectory), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName, "_")}.sln");
} }
string ProjectHeader( internal string VsConfigFile()
{
return Path.Combine(FileUtility.Normalize(ProjectDirectory), ".vsconfig");
}
private string ProjectHeader(
Assembly assembly, Assembly assembly,
IEnumerable<ResponseFileData> responseFilesData IEnumerable<ResponseFileData> responseFilesData,
string[] roslynAnalyzerDllPaths
) )
{ {
var toolsVersion = "4.0"; var toolsVersion = "4.0";
@@ -553,7 +598,11 @@ namespace Microsoft.Unity.VisualStudio.Editor
try try
{ {
return string.Format(GetProjectHeaderTemplate(), arguments); #if UNITY_2020_2_OR_NEWER
return string.Format(GetProjectHeaderTemplate(roslynAnalyzerDllPaths, assembly.compilerOptions.RoslynAnalyzerRulesetPath), arguments);
#else
return string.Format(GetProjectHeaderTemplate(Array.Empty<string>(), null), arguments);
#endif
} }
catch (Exception) catch (Exception)
{ {
@@ -584,7 +633,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return ProjectType.Game; return ProjectType.Game;
} }
static string GetSolutionText() private static string GetSolutionText()
{ {
return string.Join("\r\n", return string.Join("\r\n",
@"", @"",
@@ -604,7 +653,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
@"").Replace(" ", "\t"); @"").Replace(" ", "\t");
} }
static string GetProjectFooterTemplate() private static string GetProjectFooterTemplate()
{ {
return string.Join("\r\n", return string.Join("\r\n",
@" <Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />", @" <Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />",
@@ -620,7 +669,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
@""); @"");
} }
string GetProjectHeaderTemplate() private string GetProjectHeaderTemplate(string[] roslynAnalyzerDllPaths, string roslynAnalyzerRulesetPath)
{ {
var header = new[] var header = new[]
{ {
@@ -693,29 +742,38 @@ namespace Microsoft.Unity.VisualStudio.Editor
@"" @""
}; };
var lines = header var lines = header.Concat(forceExplicitReferences).Concat(flavoring).ToList();
.Concat(forceExplicitReferences)
.Concat(flavoring)
.ToList();
// Only add analyzer block for compatible Visual Studio // Only add analyzer block for compatible Visual Studio
if (m_CurrentInstallation != null && m_CurrentInstallation.SupportsAnalyzers) if (m_CurrentInstallation != null && m_CurrentInstallation.SupportsAnalyzers)
{ {
var analyzers = m_CurrentInstallation.GetAnalyzers(); #if UNITY_2020_2_OR_NEWER
if (analyzers != null && analyzers.Length > 0) if (roslynAnalyzerRulesetPath != null)
{
lines.Add(@" <PropertyGroup>");
lines.Add($" <CodeAnalysisRuleSet>{roslynAnalyzerRulesetPath}</CodeAnalysisRuleSet>");
lines.Add(@" </PropertyGroup>");
}
#endif
string[] analyzers = m_CurrentInstallation.GetAnalyzers();
string[] allAnalyzers = analyzers != null ? analyzers.Concat(roslynAnalyzerDllPaths).ToArray() : roslynAnalyzerDllPaths;
if (allAnalyzers.Any())
{ {
lines.Add(@" <ItemGroup>"); lines.Add(@" <ItemGroup>");
foreach (var analyzer in analyzers) foreach (var analyzer in allAnalyzers)
lines.Add(string.Format(@" <Analyzer Include=""{0}"" />", EscapedRelativePathFor(analyzer))); {
lines.Add($@" <Analyzer Include=""{EscapedRelativePathFor(analyzer)}"" />");
}
lines.Add(@" </ItemGroup>"); lines.Add(@" </ItemGroup>");
} }
} }
return string.Join("\r\n", lines return string.Join("\r\n", lines.Concat(footer));
.Concat(footer));
} }
void SyncSolution(IEnumerable<Assembly> assemblies) private void SyncSolution(IEnumerable<Assembly> assemblies)
{ {
if (InvalidCharactersRegexPattern.IsMatch(ProjectDirectory)) if (InvalidCharactersRegexPattern.IsMatch(ProjectDirectory))
Debug.LogWarning("Project path contains special characters, which can be an issue when opening Visual Studio"); Debug.LogWarning("Project path contains special characters, which can be an issue when opening Visual Studio");
@@ -725,7 +783,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
SyncSolutionFileIfNotChanged(solutionFile, SolutionText(assemblies, previousSolution)); SyncSolutionFileIfNotChanged(solutionFile, SolutionText(assemblies, previousSolution));
} }
string SolutionText(IEnumerable<Assembly> assemblies, Solution previousSolution = null) private string SolutionText(IEnumerable<Assembly> assemblies, Solution previousSolution = null)
{ {
const string fileversion = "12.00"; const string fileversion = "12.00";
const string vsversion = "15"; const string vsversion = "15";
@@ -760,12 +818,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
return string.Format(GetSolutionText(), fileversion, vsversion, projectEntriesText, projectConfigurationsText, propertiesText); return string.Format(GetSolutionText(), fileversion, vsversion, projectEntriesText, projectConfigurationsText, propertiesText);
} }
static IEnumerable<Assembly> RelevantAssembliesForMode(IEnumerable<Assembly> assemblies) private static IEnumerable<Assembly> RelevantAssembliesForMode(IEnumerable<Assembly> assemblies)
{ {
return assemblies.Where(i => ScriptingLanguage.CSharp == ScriptingLanguageFor(i)); return assemblies.Where(i => ScriptingLanguage.CSharp == ScriptingLanguageFor(i));
} }
private string GetPropertiesText(SolutionProperties[] array) private static string GetPropertiesText(SolutionProperties[] array)
{ {
if (array == null || array.Length == 0) if (array == null || array.Length == 0)
{ {
@@ -780,7 +838,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
} }
var result = new StringBuilder(); var result = new StringBuilder();
for (var i = 0; i<array.Length; i++) for (var i = 0; i < array.Length; i++)
{ {
if (i > 0) if (i > 0)
result.Append(k_WindowsNewline); result.Append(k_WindowsNewline);
@@ -806,7 +864,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
/// Get a Project("{guid}") = "MyProject", "MyProject.unityproj", "{projectguid}" /// Get a Project("{guid}") = "MyProject", "MyProject.unityproj", "{projectguid}"
/// entry for each relevant language /// entry for each relevant language
/// </summary> /// </summary>
string GetProjectEntriesText(IEnumerable<SolutionProjectEntry> entries) private string GetProjectEntriesText(IEnumerable<SolutionProjectEntry> entries)
{ {
var projectEntries = entries.Select(entry => string.Format( var projectEntries = entries.Select(entry => string.Format(
m_SolutionProjectEntryTemplate, m_SolutionProjectEntryTemplate,
@@ -816,7 +874,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return string.Join(k_WindowsNewline, projectEntries.ToArray()); return string.Join(k_WindowsNewline, projectEntries.ToArray());
} }
IEnumerable<SolutionProjectEntry> ToProjectEntries(IEnumerable<Assembly> assemblies) private IEnumerable<SolutionProjectEntry> ToProjectEntries(IEnumerable<Assembly> assemblies)
{ {
foreach (var assembly in assemblies) foreach (var assembly in assemblies)
yield return new SolutionProjectEntry() yield return new SolutionProjectEntry()
@@ -832,21 +890,22 @@ namespace Microsoft.Unity.VisualStudio.Editor
/// <summary> /// <summary>
/// Generate the active configuration string for a given project guid /// Generate the active configuration string for a given project guid
/// </summary> /// </summary>
string GetProjectActiveConfigurations(string projectGuid) private string GetProjectActiveConfigurations(string projectGuid)
{ {
return string.Format( return string.Format(
m_SolutionProjectConfigurationTemplate, m_SolutionProjectConfigurationTemplate,
projectGuid); projectGuid);
} }
string EscapedRelativePathFor(string file) private string EscapedRelativePathFor(string file)
{ {
var projectDir = FileUtility.Normalize(ProjectDirectory); var projectDir = FileUtility.Normalize(ProjectDirectory);
file = FileUtility.Normalize(file); file = FileUtility.Normalize(file);
var path = SkipPathPrefix(file, projectDir); var path = SkipPathPrefix(file, projectDir);
var packageInfo = m_AssemblyNameProvider.FindForAssetPath(path.Replace('\\', '/')); var packageInfo = m_AssemblyNameProvider.FindForAssetPath(path.Replace('\\', '/'));
if (packageInfo != null) { if (packageInfo != null)
{
// We have to normalize the path, because the PackageManagerRemapper assumes // We have to normalize the path, because the PackageManagerRemapper assumes
// dir seperators will be os specific. // dir seperators will be os specific.
var absolutePath = Path.GetFullPath(FileUtility.Normalize(path)); var absolutePath = Path.GetFullPath(FileUtility.Normalize(path));
@@ -856,14 +915,14 @@ namespace Microsoft.Unity.VisualStudio.Editor
return XmlFilename(path); return XmlFilename(path);
} }
static string SkipPathPrefix(string path, string prefix) private static string SkipPathPrefix(string path, string prefix)
{ {
if (path.StartsWith($"{prefix}{Path.DirectorySeparatorChar}") && (path.Length > prefix.Length)) if (path.StartsWith($"{prefix}{Path.DirectorySeparatorChar}") && (path.Length > prefix.Length))
return path.Substring(prefix.Length + 1); return path.Substring(prefix.Length + 1);
return path; return path;
} }
static string ProjectFooter() private static string ProjectFooter()
{ {
return GetProjectFooterTemplate(); return GetProjectFooterTemplate();
} }
@@ -873,21 +932,21 @@ namespace Microsoft.Unity.VisualStudio.Editor
return ".csproj"; return ".csproj";
} }
string ProjectGuid(Assembly assembly) private string ProjectGuid(Assembly assembly)
{ {
return m_GUIDGenerator.ProjectGuid( return m_GUIDGenerator.ProjectGuid(
m_ProjectName, m_ProjectName,
m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, assembly.name)); m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, assembly.name));
} }
string SolutionGuid(Assembly assembly) private string SolutionGuid(Assembly assembly)
{ {
return m_GUIDGenerator.SolutionGuid(m_ProjectName, ScriptingLanguageFor(assembly)); return m_GUIDGenerator.SolutionGuid(m_ProjectName, ScriptingLanguageFor(assembly));
} }
static string GetRootNamespace(Assembly assembly) private static string GetRootNamespace(Assembly assembly)
{ {
#if UNITY_2020_2_OR_NEWER #if UNITY_2020_2_OR_NEWER
return assembly.rootNamespace; return assembly.rootNamespace;
#else #else
return EditorSettings.projectGenerationRootNamespace; return EditorSettings.projectGenerationRootNamespace;
@@ -913,19 +972,19 @@ namespace Microsoft.Unity.VisualStudio.Editor
return ComputeGuidHashFor(projectName); return ComputeGuidHashFor(projectName);
} }
static string ComputeGuidHashFor(string input) private static string ComputeGuidHashFor(string input)
{ {
var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(input)); var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(input));
return HashAsGuid(HashToString(hash)); return HashAsGuid(HashToString(hash));
} }
static string HashAsGuid(string hash) private static string HashAsGuid(string hash)
{ {
var guid = hash.Substring(0, 8) + "-" + hash.Substring(8, 4) + "-" + hash.Substring(12, 4) + "-" + hash.Substring(16, 4) + "-" + hash.Substring(20, 12); var guid = hash.Substring(0, 8) + "-" + hash.Substring(8, 4) + "-" + hash.Substring(12, 4) + "-" + hash.Substring(16, 4) + "-" + hash.Substring(20, 12);
return guid.ToUpper(); return guid.ToUpper();
} }
static string HashToString(byte[] bs) private static string HashToString(byte[] bs)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (byte b in bs) foreach (byte b in bs)

View File

@@ -1,8 +1,9 @@
using System; /*---------------------------------------------------------------------------------------------
using System.Collections.Generic; * Copyright (c) Unity Technologies.
using System.Linq; * Copyright (c) Microsoft Corporation. All rights reserved.
using System.Text; * Licensed under the MIT License. See License.txt in the project root for license information.
using System.Threading.Tasks; *--------------------------------------------------------------------------------------------*/
using System;
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor
{ {

View File

@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor

View File

@@ -7,7 +7,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using Unity.CodeEditor; using Unity.CodeEditor;
@@ -18,10 +17,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
[InitializeOnLoad] [InitializeOnLoad]
public class VisualStudioEditor : IExternalCodeEditor public class VisualStudioEditor : IExternalCodeEditor
{ {
private static readonly VisualStudioInstallation[] _installations; private static readonly IVisualStudioInstallation[] _installations;
internal static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor; internal static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor;
internal static bool IsWindows => !IsOSX && Path.DirectorySeparatorChar == '\\' && Environment.NewLine == "\r\n"; internal static bool IsWindows => !IsOSX && Path.DirectorySeparatorChar == FileUtility.WinSeparator && Environment.NewLine == "\r\n";
CodeEditor.Installation[] IExternalCodeEditor.Installations => _installations CodeEditor.Installation[] IExternalCodeEditor.Installations => _installations
.Select(i => i.ToCodeEditorInstallation()) .Select(i => i.ToCodeEditorInstallation())
@@ -39,7 +38,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
} }
catch (Exception ex) catch (Exception ex)
{ {
UnityEngine.Debug.Log($"Error detecting Visual Studio installations: {ex}"); UnityEngine.Debug.LogError($"Error detecting Visual Studio installations: {ex}");
_installations = Array.Empty<VisualStudioInstallation>(); _installations = Array.Empty<VisualStudioInstallation>();
} }
@@ -64,7 +63,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
} }
internal bool TryGetVisualStudioInstallationForPath(string editorPath, out VisualStudioInstallation installation) internal virtual bool TryGetVisualStudioInstallationForPath(string editorPath, out IVisualStudioInstallation installation)
{ {
// lookup for well known installations // lookup for well known installations
foreach (var candidate in _installations) foreach (var candidate in _installations)
@@ -79,7 +78,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return Discovery.TryDiscoverInstallation(editorPath, out installation); return Discovery.TryDiscoverInstallation(editorPath, out installation);
} }
public bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation) public virtual bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
{ {
var result = TryGetVisualStudioInstallationForPath(editorPath, out var vsi); var result = TryGetVisualStudioInstallationForPath(editorPath, out var vsi);
installation = vsi == null ? default : vsi.ToCodeEditorInstallation(); installation = vsi == null ? default : vsi.ToCodeEditorInstallation();
@@ -118,7 +117,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
void RegenerateProjectFiles() void RegenerateProjectFiles()
{ {
var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(new GUILayoutOption[] {})); var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(new GUILayoutOption[] { }));
rect.width = 252; rect.width = 252;
if (GUI.Button(rect, "Regenerate project files")) if (GUI.Button(rect, "Regenerate project files"))
{ {
@@ -143,8 +142,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb")) foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb"))
{ {
var pdbFile = FileUtility.GetAssetFullPath(file); var pdbFile = FileUtility.GetAssetFullPath(file);
var asmFile = Path.ChangeExtension(pdbFile, ".dll");
// skip Unity packages like com.unity.ext.nunit
if (pdbFile.IndexOf($"{Path.DirectorySeparatorChar}com.unity.", StringComparison.OrdinalIgnoreCase) > 0)
continue;
var asmFile = Path.ChangeExtension(pdbFile, ".dll");
if (!File.Exists(asmFile) || !Image.IsAssembly(asmFile)) if (!File.Exists(asmFile) || !Image.IsAssembly(asmFile))
continue; continue;
@@ -176,11 +179,31 @@ namespace Microsoft.Unity.VisualStudio.Editor
return false; return false;
} }
private static void CheckCurrentEditorInstallation()
{
var editorPath = CodeEditor.CurrentEditorInstallation;
try
{
if (Discovery.TryDiscoverInstallation(editorPath, out _))
return;
}
catch (IOException)
{
}
UnityEngine.Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools.");
}
public bool OpenProject(string path, int line, int column) public bool OpenProject(string path, int line, int column)
{ {
CheckCurrentEditorInstallation();
if (!IsSupportedPath(path)) if (!IsSupportedPath(path))
return false; return false;
if (!IsProjectGeneratedFor(path, out var missingFlag))
UnityEngine.Debug.LogWarning($"You are trying to open {path} outside a generated project. This might cause problems with IntelliSense and debugging. To avoid this, you can change your .csproj preferences in Edit > Preferences > External Tools and enable {GetProjectGenerationFlagDescription(missingFlag)} generation.");
if (IsOSX) if (IsOSX)
return OpenOSXApp(path, line, column); return OpenOSXApp(path, line, column);
@@ -190,6 +213,67 @@ namespace Microsoft.Unity.VisualStudio.Editor
return false; return false;
} }
private static string GetProjectGenerationFlagDescription(ProjectGenerationFlag flag)
{
switch (flag)
{
case ProjectGenerationFlag.BuiltIn:
return "Built-in packages";
case ProjectGenerationFlag.Embedded:
return "Embedded packages";
case ProjectGenerationFlag.Git:
return "Git packages";
case ProjectGenerationFlag.Local:
return "Local packages";
case ProjectGenerationFlag.LocalTarBall:
return "Local tarball";
case ProjectGenerationFlag.PlayerAssemblies:
return "Player projects";
case ProjectGenerationFlag.Registry:
return "Registry packages";
case ProjectGenerationFlag.Unknown:
return "Packages from unknown sources";
case ProjectGenerationFlag.None:
default:
return string.Empty;
}
}
private bool IsProjectGeneratedFor(string path, out ProjectGenerationFlag missingFlag)
{
missingFlag = ProjectGenerationFlag.None;
// No need to check when opening the whole solution
if (string.IsNullOrEmpty(path))
return true;
// We only want to check for cs scripts
if (ProjectGeneration.ScriptingLanguageFor(path) != ScriptingLanguage.CSharp)
return true;
// Even on windows, the package manager requires relative path + unix style separators for queries
var basePath = _generator.ProjectDirectory;
var relativePath = FileUtility
.NormalizeWindowsToUnix(path)
.Replace(basePath, string.Empty)
.Trim(FileUtility.UnixSeparator);
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(relativePath);
if (packageInfo == null)
return true;
var source = packageInfo.source;
if (!Enum.TryParse<ProjectGenerationFlag>(source.ToString(), out var flag))
return true;
if (_generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(flag))
return true;
// Return false if we found a source not flagged for generation
missingFlag = flag;
return false;
}
private bool OpenWindowsApp(string path, int line) private bool OpenWindowsApp(string path, int line)
{ {
var progpath = FileUtility var progpath = FileUtility

View File

@@ -10,7 +10,16 @@ using IOPath = System.IO.Path;
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor
{ {
internal class VisualStudioInstallation internal interface IVisualStudioInstallation
{
string Path { get; }
bool SupportsAnalyzers { get; }
bool SupportsCSharp8 { get; }
string[] GetAnalyzers();
CodeEditor.Installation ToCodeEditorInstallation();
}
internal class VisualStudioInstallation : IVisualStudioInstallation
{ {
public string Name { get; set; } public string Name { get; set; }
public string Path { get; set; } public string Path { get; set; }

View File

@@ -34,7 +34,8 @@ namespace Microsoft.Unity.VisualStudio.Editor
// - or if the firewall is not properly configured // - or if the firewall is not properly configured
var messagingPort = MessagingPort(); var messagingPort = MessagingPort();
try { try
{
_messager = Messager.BindTo(messagingPort); _messager = Messager.BindTo(messagingPort);
_messager.ReceiveMessage += ReceiveMessage; _messager.ReceiveMessage += ReceiveMessage;
} }

View File

@@ -2,18 +2,18 @@
"name": "com.unity.ide.visualstudio", "name": "com.unity.ide.visualstudio",
"displayName": "Visual Studio Editor", "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.", "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.3", "version": "2.0.5",
"unity": "2020.1", "unity": "2020.1",
"unityRelease": "0a12", "unityRelease": "0a12",
"relatedPackages": { "relatedPackages": {
"com.unity.ide.visualstudio.tests": "2.0.3" "com.unity.ide.visualstudio.tests": "2.0.5"
}, },
"upmCi": { "upmCi": {
"footprint": "a528d76d0398bf543d3597af18e87e2f3a13d40a" "footprint": "848c02b3f0fe476a599004cd972346a89e39d26f"
}, },
"repository": { "repository": {
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git", "url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git",
"type": "git", "type": "git",
"revision": "869804bff6e99d0d0e9c1867aebaa070fad875e5" "revision": "83ca94e82bb6da515dc57e0d860b6b2224f56991"
} }
} }