You've already forked com.unity.ide.cursor
mirror of
https://github.com/boxqkrtm/com.unity.ide.cursor.git
synced 2026-05-14 22:30:10 +00:00
com.unity.ide.visualstudio@2.0.20
## [2.0.20] - 2023-06-27 Integration: - Internal API refactoring. ## [2.0.19] - 2023-06-14 Integration: - Add support for Visual Studio Code. Project generation: - Add support for Sdk Style poject generation. - Fix an issue related to missing properties with 2021.3.
This commit is contained in:
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,5 +1,25 @@
|
|||||||
# Code Editor Package for Visual Studio
|
# Code Editor Package for Visual Studio
|
||||||
|
|
||||||
|
## [2.0.20] - 2023-06-27
|
||||||
|
|
||||||
|
Integration:
|
||||||
|
|
||||||
|
- Internal API refactoring.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.0.19] - 2023-06-14
|
||||||
|
|
||||||
|
Integration:
|
||||||
|
|
||||||
|
- Add support for Visual Studio Code.
|
||||||
|
|
||||||
|
Project generation:
|
||||||
|
|
||||||
|
- Add support for Sdk Style poject generation.
|
||||||
|
- Fix an issue related to missing properties with 2021.3.
|
||||||
|
|
||||||
|
|
||||||
## [2.0.18] - 2023-03-17
|
## [2.0.18] - 2023-03-17
|
||||||
|
|
||||||
Integration:
|
Integration:
|
||||||
@@ -10,7 +30,6 @@ Project generation:
|
|||||||
|
|
||||||
- Add extra compiler options for analyzers and source generators.
|
- Add extra compiler options for analyzers and source generators.
|
||||||
|
|
||||||
|
|
||||||
## [2.0.17] - 2022-12-06
|
## [2.0.17] - 2022-12-06
|
||||||
|
|
||||||
Integration:
|
Integration:
|
||||||
@@ -24,7 +43,6 @@ Project generation:
|
|||||||
- Update supported C# versions.
|
- Update supported C# versions.
|
||||||
- Performance improvements.
|
- Performance improvements.
|
||||||
|
|
||||||
|
|
||||||
## [2.0.16] - 2022-06-08
|
## [2.0.16] - 2022-06-08
|
||||||
|
|
||||||
Integration:
|
Integration:
|
||||||
|
|||||||
Binary file not shown.
@@ -23,7 +23,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
internal static void GenerateSolutionWith(VisualStudioEditor vse, string installationPath)
|
internal static void GenerateSolutionWith(VisualStudioEditor vse, string installationPath)
|
||||||
{
|
{
|
||||||
if (vse != null && vse.TryGetVisualStudioInstallationForPath(installationPath, searchInstallations: true, out var vsi))
|
if (vse != null && vse.TryGetVisualStudioInstallationForPath(installationPath, lookupDiscoveredInstallations: true, out var vsi))
|
||||||
{
|
{
|
||||||
Log($"Using {GetInstallationDetails(vsi)}");
|
Log($"Using {GetInstallationDetails(vsi)}");
|
||||||
vse.SyncAll();
|
vse.SyncAll();
|
||||||
|
|||||||
@@ -3,162 +3,52 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* 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;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Microsoft.Unity.VisualStudio.Editor
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
{
|
{
|
||||||
internal static class Discovery
|
internal static class Discovery
|
||||||
{
|
{
|
||||||
internal const string ManagedWorkload = "Microsoft.VisualStudio.Workload.ManagedGame";
|
|
||||||
|
|
||||||
internal static string _vsWherePath;
|
|
||||||
|
|
||||||
public static void FindVSWhere()
|
|
||||||
{
|
|
||||||
_vsWherePath = FileUtility.GetPackageAssetFullPath("Editor", "VSWhere", "vswhere.exe");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
||||||
{
|
{
|
||||||
if (VisualStudioEditor.IsWindows)
|
foreach (var installation in VisualStudioForWindowsInstallation.GetVisualStudioInstallations())
|
||||||
{
|
|
||||||
foreach (var installation in QueryVsWhere())
|
|
||||||
yield return installation;
|
yield return installation;
|
||||||
}
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsOSX)
|
foreach (var installation in VisualStudioForMacInstallation.GetVisualStudioInstallations())
|
||||||
{
|
|
||||||
var candidates = Directory.EnumerateDirectories("/Applications", "*.app");
|
|
||||||
foreach (var candidate in candidates)
|
|
||||||
{
|
|
||||||
if (TryDiscoverInstallation(candidate, out var installation))
|
|
||||||
yield return installation;
|
yield return installation;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCandidateForDiscovery(string path)
|
foreach (var installation in VisualStudioCodeInstallation.GetVisualStudioInstallations())
|
||||||
{
|
yield return installation;
|
||||||
if (File.Exists(path) && VisualStudioEditor.IsWindows && Regex.IsMatch(path, "devenv.exe$", RegexOptions.IgnoreCase))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (Directory.Exists(path) && VisualStudioEditor.IsOSX && Regex.IsMatch(path, "Visual\\s?Studio(?!.*Code.*).*.app$", RegexOptions.IgnoreCase))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
||||||
{
|
{
|
||||||
installation = null;
|
try
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(editorPath))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!IsCandidateForDiscovery(editorPath))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// On windows we use the executable directly, so we can query extra information
|
|
||||||
var fvi = editorPath;
|
|
||||||
|
|
||||||
// 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");
|
if (VisualStudioForWindowsInstallation.TryDiscoverInstallation(editorPath, out installation))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!File.Exists(fvi))
|
if (VisualStudioForMacInstallation.TryDiscoverInstallation(editorPath, out installation))
|
||||||
fvi = Path.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe");
|
return true;
|
||||||
|
|
||||||
if (!File.Exists(fvi))
|
if (VisualStudioCodeInstallation.TryDiscoverInstallation(editorPath, out installation))
|
||||||
fvi = Path.Combine(editorPath, "Contents/MonoBundle/VisualStudio.dll");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(fvi))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// VS preview are not using the isPrerelease flag so far
|
|
||||||
// On Windows FileDescription contains "Preview", but not on Mac
|
|
||||||
var vi = FileVersionInfo.GetVersionInfo(fvi);
|
|
||||||
var version = new Version(vi.ProductVersion);
|
|
||||||
var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview");
|
|
||||||
|
|
||||||
installation = new VisualStudioInstallation()
|
|
||||||
{
|
|
||||||
IsPrerelease = isPrerelease,
|
|
||||||
Name = $"{vi.FileDescription}{(isPrerelease && VisualStudioEditor.IsOSX ? " Preview" : string.Empty)} [{version.ToString(3)}]",
|
|
||||||
Path = editorPath,
|
|
||||||
Version = version
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch (IOException)
|
||||||
#region VsWhere Json Schema
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
[Serializable]
|
|
||||||
internal class VsWhereResult
|
|
||||||
{
|
{
|
||||||
public VsWhereEntry[] entries;
|
installation = null;
|
||||||
|
|
||||||
public static VsWhereResult FromJson(string json)
|
|
||||||
{
|
|
||||||
return JsonUtility.FromJson<VsWhereResult>("{ \"" + nameof(VsWhereResult.entries) + "\": " + json + " }");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<VisualStudioInstallation> ToVisualStudioInstallations()
|
return false;
|
||||||
{
|
|
||||||
foreach (var entry in entries)
|
|
||||||
{
|
|
||||||
yield return new VisualStudioInstallation()
|
|
||||||
{
|
|
||||||
Name = $"{entry.displayName} [{entry.catalog.productDisplayVersion}]",
|
|
||||||
Path = entry.productPath,
|
|
||||||
IsPrerelease = entry.isPrerelease,
|
|
||||||
Version = Version.Parse(entry.catalog.buildVersion)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
public static void Initialize()
|
||||||
internal class VsWhereEntry
|
|
||||||
{
|
{
|
||||||
public string displayName;
|
VisualStudioForWindowsInstallation.Initialize();
|
||||||
public bool isPrerelease;
|
VisualStudioForMacInstallation.Initialize();
|
||||||
public string productPath;
|
VisualStudioCodeInstallation.Initialize();
|
||||||
public VsWhereCatalog catalog;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
internal class VsWhereCatalog
|
|
||||||
{
|
|
||||||
public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0"
|
|
||||||
public string buildVersion;
|
|
||||||
}
|
|
||||||
#pragma warning restore CS3021
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private static IEnumerable<VisualStudioInstallation> QueryVsWhere()
|
|
||||||
{
|
|
||||||
var progpath = _vsWherePath;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(progpath))
|
|
||||||
return Enumerable.Empty<VisualStudioInstallation>();
|
|
||||||
|
|
||||||
var result = ProcessRunner.StartAndWaitForExit(progpath, "-prerelease -format json -utf8");
|
|
||||||
|
|
||||||
if (!result.Success)
|
|
||||||
throw new Exception($"Failure while running vswhere: {result.Error}");
|
|
||||||
|
|
||||||
// Do not catch any JsonException here, this will be handled by the caller
|
|
||||||
return VsWhereResult
|
|
||||||
.FromJson(result.Output)
|
|
||||||
.ToVisualStudioInstallations();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.Unity.VisualStudio.Editor
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
@@ -21,19 +22,34 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
{
|
{
|
||||||
public const int DefaultTimeoutInMilliseconds = 300000;
|
public const int DefaultTimeoutInMilliseconds = 300000;
|
||||||
|
|
||||||
public static ProcessStartInfo ProcessStartInfoFor(string filename, string arguments)
|
public static ProcessStartInfo ProcessStartInfoFor(string filename, string arguments, bool redirect = true, bool shell = false)
|
||||||
{
|
{
|
||||||
return new ProcessStartInfo
|
return new ProcessStartInfo
|
||||||
{
|
{
|
||||||
UseShellExecute = false,
|
UseShellExecute = shell,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = redirect,
|
||||||
RedirectStandardError = true,
|
RedirectStandardError = redirect,
|
||||||
FileName = filename,
|
FileName = filename,
|
||||||
Arguments = arguments
|
Arguments = arguments
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Start(string filename, string arguments)
|
||||||
|
{
|
||||||
|
Start(ProcessStartInfoFor(filename, arguments, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Start(ProcessStartInfo processStartInfo)
|
||||||
|
{
|
||||||
|
var process = new Process { StartInfo = processStartInfo };
|
||||||
|
|
||||||
|
using (process)
|
||||||
|
{
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static ProcessRunnerResult StartAndWaitForExit(string filename, string arguments, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null)
|
public static ProcessRunnerResult StartAndWaitForExit(string filename, string arguments, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null)
|
||||||
{
|
{
|
||||||
return StartAndWaitForExit(ProcessStartInfoFor(filename, arguments), timeoutms, onOutputReceived);
|
return StartAndWaitForExit(ProcessStartInfoFor(filename, arguments), timeoutms, onOutputReceived);
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace;
|
public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace;
|
||||||
|
|
||||||
public ProjectGenerationFlag ProjectGenerationFlag
|
public ProjectGenerationFlag ProjectGenerationFlag
|
||||||
|
{
|
||||||
|
get { return ProjectGenerationFlagImpl; }
|
||||||
|
private set { ProjectGenerationFlagImpl = value;}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual ProjectGenerationFlag ProjectGenerationFlagImpl
|
||||||
{
|
{
|
||||||
get => m_ProjectGenerationFlag;
|
get => m_ProjectGenerationFlag;
|
||||||
private set
|
private set
|
||||||
|
|||||||
98
Editor/ProjectGeneration/LegacyStyleProjectGeneration.cs
Normal file
98
Editor/ProjectGeneration/LegacyStyleProjectGeneration.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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.Text;
|
||||||
|
using UnityEditor.Compilation;
|
||||||
|
|
||||||
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
|
{
|
||||||
|
|
||||||
|
internal class LegacyStyleProjectGeneration : ProjectGeneration
|
||||||
|
{
|
||||||
|
public LegacyStyleProjectGeneration(string tempDirectory, IAssemblyNameProvider assemblyNameProvider, IFileIO fileIoProvider, IGUIDGenerator guidGenerator) : base(tempDirectory, assemblyNameProvider, fileIoProvider, guidGenerator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyStyleProjectGeneration(string tempDirectory) : base(tempDirectory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyStyleProjectGeneration()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
|
||||||
|
{
|
||||||
|
headerBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
//Header
|
||||||
|
headerBuilder.Append(@"<?xml version=""1.0"" encoding=""utf-8""?>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append($@"<Project ToolsVersion=""4.0"" DefaultTargets=""Build"" xmlns=""{MSBuildNamespaceUri}"">").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <!-- Generated file, do not modify, your changes will be overwritten (use AssetPostprocessor.OnGeneratedCSProject) -->").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <LangVersion>").Append(properties.LangVersion).Append(@"</LangVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <ProductVersion>10.0.20506</ProductVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <SchemaVersion>2.0</SchemaVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <RootNamespace>").Append(properties.RootNamespace).Append(@"</RootNamespace>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <ProjectGuid>{").Append(properties.ProjectGuid).Append(@"}</ProjectGuid>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <OutputType>Library</OutputType>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AssemblyName>").Append(properties.AssemblyName).Append(@"</AssemblyName>").Append(k_WindowsNewline);
|
||||||
|
// In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version with the legacy format will have no impact, even when targeting netstandard/net48 from Unity.
|
||||||
|
// And VSTU/Unity Game workload has a dependency towards net471 reference assemblies, so IDE will not complain that this specific SDK is not available.
|
||||||
|
// Unity already selected proper API surface through referenced DLLs for us.
|
||||||
|
headerBuilder.Append(@" <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <FileAlignment>512</FileAlignment>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <BaseDirectory>.</BaseDirectory>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
|
||||||
|
GetProjectHeaderConfigurations(properties, headerBuilder);
|
||||||
|
|
||||||
|
// Explicit references
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <NoConfig>true</NoConfig>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <NoStdLib>true</NoStdLib>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
|
||||||
|
GetProjectHeaderVstuFlavoring(properties, headerBuilder);
|
||||||
|
GetProjectHeaderAnalyzers(properties, headerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void AppendProjectReference(Assembly assembly, Assembly reference, StringBuilder projectBuilder)
|
||||||
|
{
|
||||||
|
// If the current assembly is a Player project, we want to project-reference the corresponding Player project
|
||||||
|
var referenceName = m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, reference.name);
|
||||||
|
|
||||||
|
projectBuilder.Append(@" <ProjectReference Include=""").Append(referenceName).Append(GetProjectExtension()).Append(@""">").Append(k_WindowsNewline);
|
||||||
|
projectBuilder.Append(" <Project>{").Append(ProjectGuid(referenceName)).Append("}</Project>").Append(k_WindowsNewline);
|
||||||
|
projectBuilder.Append(" <Name>").Append(referenceName).Append("</Name>").Append(k_WindowsNewline);
|
||||||
|
projectBuilder.Append(" </ProjectReference>").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void GetProjectFooter(StringBuilder footerBuilder)
|
||||||
|
{
|
||||||
|
footerBuilder.Append(string.Join(k_WindowsNewline,
|
||||||
|
@" <Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />",
|
||||||
|
@" <Target Name=""GenerateTargetFrameworkMonikerAttribute"" />",
|
||||||
|
@" <!-- To modify your build process, add your task inside one of the targets below and uncomment it.",
|
||||||
|
@" Other similar extension points exist, see Microsoft.Common.targets.",
|
||||||
|
@" <Target Name=""BeforeBuild"">",
|
||||||
|
@" </Target>",
|
||||||
|
@" <Target Name=""AfterBuild"">",
|
||||||
|
@" </Target>",
|
||||||
|
@" -->",
|
||||||
|
@"</Project>",
|
||||||
|
@""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3070b6395b0f5f04faaab13164c6256d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -39,12 +39,14 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
public class ProjectGeneration : IGenerator
|
public class ProjectGeneration : IGenerator
|
||||||
{
|
{
|
||||||
|
// do not remove because of the Validation API, used in LegacyStyleProjectGeneration
|
||||||
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 IAssemblyNameProvider AssemblyNameProvider => m_AssemblyNameProvider;
|
||||||
public string ProjectDirectory { get; }
|
public string ProjectDirectory { get; }
|
||||||
|
|
||||||
// Use this to have the same newline ending on all platforms for consistency.
|
// Use this to have the same newline ending on all platforms for consistency.
|
||||||
const string k_WindowsNewline = "\r\n";
|
internal const string k_WindowsNewline = "\r\n";
|
||||||
|
|
||||||
const string m_SolutionProjectEntryTemplate = @"Project(""{{{0}}}"") = ""{1}"", ""{2}"", ""{{{3}}}""{4}EndProject";
|
const string m_SolutionProjectEntryTemplate = @"Project(""{{{0}}}"") = ""{1}"", ""{2}"", ""{{{3}}}""{4}EndProject";
|
||||||
|
|
||||||
@@ -60,7 +62,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
HashSet<string> m_BuiltinSupportedExtensions = new HashSet<string>();
|
HashSet<string> m_BuiltinSupportedExtensions = new HashSet<string>();
|
||||||
|
|
||||||
readonly string m_ProjectName;
|
readonly string m_ProjectName;
|
||||||
readonly IAssemblyNameProvider m_AssemblyNameProvider;
|
internal readonly IAssemblyNameProvider m_AssemblyNameProvider;
|
||||||
readonly IFileIO m_FileIOProvider;
|
readonly IFileIO m_FileIOProvider;
|
||||||
readonly IGUIDGenerator m_GUIDGenerator;
|
readonly IGUIDGenerator m_GUIDGenerator;
|
||||||
bool m_ShouldGenerateAll;
|
bool m_ShouldGenerateAll;
|
||||||
@@ -106,9 +108,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
SetupProjectSupportedExtensions();
|
SetupProjectSupportedExtensions();
|
||||||
|
|
||||||
// See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/
|
CreateExtraFiles(m_CurrentInstallation);
|
||||||
// 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
|
||||||
var affected = affectedFiles as ICollection<string> ?? affectedFiles.ToArray();
|
var affected = affectedFiles as ICollection<string> ?? affectedFiles.ToArray();
|
||||||
@@ -148,26 +148,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateVsConfigIfNotFound()
|
private void CreateExtraFiles(IVisualStudioInstallation installation)
|
||||||
{
|
{
|
||||||
try
|
installation?.CreateExtraFiles(ProjectDirectory);
|
||||||
{
|
|
||||||
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)
|
private bool HasFilesBeenModified(IEnumerable<string> affectedFiles, IEnumerable<string> reimportedFiles)
|
||||||
@@ -183,7 +166,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
private void RefreshCurrentInstallation()
|
private void RefreshCurrentInstallation()
|
||||||
{
|
{
|
||||||
var editor = CodeEditor.CurrentEditor as VisualStudioEditor;
|
var editor = CodeEditor.CurrentEditor as VisualStudioEditor;
|
||||||
editor?.TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, searchInstallations: true, out m_CurrentInstallation);
|
editor?.TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, lookupDiscoveredInstallations: true, out m_CurrentInstallation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ProfilerMarker solutionSyncMarker = new ProfilerMarker("SolutionSynchronizerSync");
|
static ProfilerMarker solutionSyncMarker = new ProfilerMarker("SolutionSynchronizerSync");
|
||||||
@@ -199,7 +182,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
// See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/
|
// 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
|
// We create a .vsconfig file to make sure our ManagedGame workload is installed
|
||||||
CreateVsConfigIfNotFound();
|
CreateExtraFiles(m_CurrentInstallation);
|
||||||
|
|
||||||
var externalCodeAlreadyGeneratedProjects = OnPreGeneratingCSProjectFiles();
|
var externalCodeAlreadyGeneratedProjects = OnPreGeneratingCSProjectFiles();
|
||||||
|
|
||||||
@@ -366,7 +349,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
stringBuilders[assemblyName] = projectBuilder;
|
stringBuilders[assemblyName] = projectBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
IncludeAsset(projectBuilder, "None", asset);
|
IncludeAsset(projectBuilder, IncludeAssetTag.None, asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +361,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void IncludeAsset(StringBuilder builder, string tag, string asset)
|
internal enum IncludeAssetTag
|
||||||
|
{
|
||||||
|
Compile,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual void IncludeAsset(StringBuilder builder, IncludeAssetTag tag, string asset)
|
||||||
{
|
{
|
||||||
var filename = EscapedRelativePathFor(asset, out var packageInfo);
|
var filename = EscapedRelativePathFor(asset, out var packageInfo);
|
||||||
|
|
||||||
@@ -517,7 +506,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
if ("dll" != extensionWithoutDot)
|
if ("dll" != extensionWithoutDot)
|
||||||
{
|
{
|
||||||
IncludeAsset(projectBuilder, "Compile", file);
|
IncludeAsset(projectBuilder, IncludeAssetTag.Compile, file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -562,19 +551,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
projectBuilder.Append(" <ItemGroup>").Append(k_WindowsNewline);
|
projectBuilder.Append(" <ItemGroup>").Append(k_WindowsNewline);
|
||||||
foreach (var reference in assembly.assemblyReferences.Where(i => i.sourceFiles.Any(ShouldFileBePartOfSolution)))
|
foreach (var reference in assembly.assemblyReferences.Where(i => i.sourceFiles.Any(ShouldFileBePartOfSolution)))
|
||||||
{
|
{
|
||||||
// If the current assembly is a Player project, we want to project-reference the corresponding Player project
|
AppendProjectReference(assembly, reference, projectBuilder);
|
||||||
var referenceName = m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, reference.name);
|
|
||||||
|
|
||||||
projectBuilder.Append(@" <ProjectReference Include=""").Append(referenceName).Append(GetProjectExtension()).Append(@""">").Append(k_WindowsNewline);
|
|
||||||
projectBuilder.Append(" <Project>{").Append(ProjectGuid(referenceName)).Append("}</Project>").Append(k_WindowsNewline);
|
|
||||||
projectBuilder.Append(" <Name>").Append(referenceName).Append("</Name>").Append(k_WindowsNewline);
|
|
||||||
projectBuilder.Append(" </ProjectReference>").Append(k_WindowsNewline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
projectBuilder.Append(@" </ItemGroup>").Append(k_WindowsNewline);
|
projectBuilder.Append(@" </ItemGroup>").Append(k_WindowsNewline);
|
||||||
}
|
}
|
||||||
|
|
||||||
projectBuilder.Append(GetProjectFooter());
|
GetProjectFooter(projectBuilder);
|
||||||
return projectBuilder.ToString();
|
return projectBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,6 +577,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return SecurityElement.Escape(s);
|
return SecurityElement.Escape(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal virtual void AppendProjectReference(Assembly assembly, Assembly reference, StringBuilder projectBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private void AppendReference(string fullReference, StringBuilder projectBuilder)
|
private void AppendReference(string fullReference, StringBuilder projectBuilder)
|
||||||
{
|
{
|
||||||
var escapedFullPath = EscapedRelativePathFor(fullReference, out _);
|
var escapedFullPath = EscapedRelativePathFor(fullReference, out _);
|
||||||
@@ -614,11 +601,6 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return Path.Combine(ProjectDirectory.NormalizePathSeparators(), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName, "_")}.sln");
|
return Path.Combine(ProjectDirectory.NormalizePathSeparators(), $"{InvalidCharactersRegexPattern.Replace(m_ProjectName, "_")}.sln");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string VsConfigFile()
|
|
||||||
{
|
|
||||||
return Path.Combine(ProjectDirectory.NormalizePathSeparators(), ".vsconfig");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string GetLangVersion(Assembly assembly)
|
internal string GetLangVersion(Assembly assembly)
|
||||||
{
|
{
|
||||||
var targetLanguageVersion = "latest"; // danger: latest is not the same absolute value depending on the VS version.
|
var targetLanguageVersion = "latest"; // danger: latest is not the same absolute value depending on the VS version.
|
||||||
@@ -674,16 +656,25 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
var additionalFilePaths = new List<string>();
|
var additionalFilePaths = new List<string>();
|
||||||
var rulesetPath = string.Empty;
|
var rulesetPath = string.Empty;
|
||||||
var analyzerConfigPath = string.Empty;
|
var analyzerConfigPath = string.Empty;
|
||||||
|
var compilerOptions = assembly.compilerOptions;
|
||||||
|
|
||||||
#if UNITY_2020_2_OR_NEWER
|
#if UNITY_2020_2_OR_NEWER
|
||||||
// Analyzers + ruleset provided by Unity
|
// Analyzers + ruleset provided by Unity
|
||||||
analyzers.AddRange(assembly.compilerOptions.RoslynAnalyzerDllPaths);
|
analyzers.AddRange(compilerOptions.RoslynAnalyzerDllPaths);
|
||||||
rulesetPath = assembly.compilerOptions.RoslynAnalyzerRulesetPath;
|
rulesetPath = compilerOptions.RoslynAnalyzerRulesetPath;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if UNITY_2021_3_OR_NEWER && !UNITY_2022_1 // we have support in 2021.3, 2022.2 but without a backport in 2022.1
|
// We have support in 2021.3, 2022.2 but without a backport in 2022.1
|
||||||
additionalFilePaths.AddRange(assembly.compilerOptions.RoslynAdditionalFilePaths);
|
#if UNITY_2021_3
|
||||||
analyzerConfigPath = assembly.compilerOptions.AnalyzerConfigPath;
|
// Unfortunately those properties were introduced in a patch release of 2021.3, so not found in 2021.3.2f1 for example
|
||||||
|
var scoType = compilerOptions.GetType();
|
||||||
|
var afpProperty = scoType.GetProperty("RoslynAdditionalFilePaths");
|
||||||
|
var acpProperty = scoType.GetProperty("AnalyzerConfigPath");
|
||||||
|
additionalFilePaths.AddRange(afpProperty?.GetValue(compilerOptions) as string[] ?? Array.Empty<string>());
|
||||||
|
analyzerConfigPath = acpProperty?.GetValue(compilerOptions) as string ?? analyzerConfigPath;
|
||||||
|
#elif UNITY_2022_2_OR_NEWER
|
||||||
|
additionalFilePaths.AddRange(compilerOptions.RoslynAdditionalFilePaths);
|
||||||
|
analyzerConfigPath = compilerOptions.AnalyzerConfigPath;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Analyzers and additional files provided by csc.rsp
|
// Analyzers and additional files provided by csc.rsp
|
||||||
@@ -765,30 +756,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return ProjectType.Game;
|
return ProjectType.Game;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
|
internal virtual void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
|
||||||
{
|
{
|
||||||
headerBuilder = new StringBuilder();
|
headerBuilder = default;
|
||||||
|
}
|
||||||
|
|
||||||
//Header
|
internal static void GetProjectHeaderConfigurations(ProjectProperties properties, StringBuilder headerBuilder)
|
||||||
headerBuilder.Append(@"<?xml version=""1.0"" encoding=""utf-8""?>").Append(k_WindowsNewline);
|
{
|
||||||
headerBuilder.Append(@"<Project ToolsVersion=""4.0"" DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <LangVersion>").Append(properties.LangVersion).Append(@"</LangVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <ProductVersion>10.0.20506</ProductVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <SchemaVersion>2.0</SchemaVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <RootNamespace>").Append(properties.RootNamespace).Append(@"</RootNamespace>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <ProjectGuid>{").Append(properties.ProjectGuid).Append(@"}</ProjectGuid>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <OutputType>Library</OutputType>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <AssemblyName>").Append(properties.AssemblyName).Append(@"</AssemblyName>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <FileAlignment>512</FileAlignment>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <BaseDirectory>.</BaseDirectory>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "">").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "">").Append(k_WindowsNewline);
|
||||||
headerBuilder.Append(@" <DebugSymbols>true</DebugSymbols>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <DebugSymbols>true</DebugSymbols>").Append(k_WindowsNewline);
|
||||||
headerBuilder.Append(@" <DebugType>full</DebugType>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <DebugType>full</DebugType>").Append(k_WindowsNewline);
|
||||||
@@ -809,26 +783,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
headerBuilder.Append(@" <NoWarn>0169</NoWarn>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <NoWarn>0169</NoWarn>").Append(k_WindowsNewline);
|
||||||
headerBuilder.Append(@" <AllowUnsafeBlocks>").Append(properties.Unsafe).Append(@"</AllowUnsafeBlocks>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <AllowUnsafeBlocks>").Append(properties.Unsafe).Append(@"</AllowUnsafeBlocks>").Append(k_WindowsNewline);
|
||||||
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
|
||||||
// Explicit references
|
internal static void GetProjectHeaderAnalyzers(ProjectProperties properties, StringBuilder headerBuilder)
|
||||||
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
{
|
||||||
headerBuilder.Append(@" <NoConfig>true</NoConfig>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <NoStdLib>true</NoStdLib>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
|
|
||||||
// Flavoring
|
|
||||||
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <ProjectTypeGuids>{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <UnityProjectGenerator>Package</UnityProjectGenerator>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <UnityProjectGeneratorVersion>").Append(properties.FlavoringPackageVersion).Append(@"</UnityProjectGeneratorVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <UnityProjectType>").Append(properties.FlavoringProjectType).Append(@"</UnityProjectType>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <UnityBuildTarget>").Append(properties.FlavoringBuildTarget).Append(@"</UnityBuildTarget>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" <UnityVersion>").Append(properties.FlavoringUnityVersion).Append(@"</UnityVersion>").Append(k_WindowsNewline);
|
|
||||||
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(properties.RulesetPath))
|
if (!string.IsNullOrEmpty(properties.RulesetPath))
|
||||||
{
|
{
|
||||||
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
@@ -864,20 +822,26 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetProjectFooter()
|
internal static void GetProjectHeaderVstuFlavoring(ProjectProperties properties, StringBuilder headerBuilder, bool includeProjectTypeGuids = true)
|
||||||
|
{
|
||||||
|
// Flavoring
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
|
||||||
|
if (includeProjectTypeGuids)
|
||||||
|
{
|
||||||
|
headerBuilder.Append(@" <ProjectTypeGuids>{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
headerBuilder.Append(@" <UnityProjectGenerator>Package</UnityProjectGenerator>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <UnityProjectGeneratorVersion>").Append(properties.FlavoringPackageVersion).Append(@"</UnityProjectGeneratorVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <UnityProjectType>").Append(properties.FlavoringProjectType).Append(@"</UnityProjectType>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <UnityBuildTarget>").Append(properties.FlavoringBuildTarget).Append(@"</UnityBuildTarget>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <UnityVersion>").Append(properties.FlavoringUnityVersion).Append(@"</UnityVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual void GetProjectFooter(StringBuilder footerBuilder)
|
||||||
{
|
{
|
||||||
return string.Join(k_WindowsNewline,
|
|
||||||
@" <Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />",
|
|
||||||
@" <Target Name=""GenerateTargetFrameworkMonikerAttribute"" />",
|
|
||||||
@" <!-- To modify your build process, add your task inside one of the targets below and uncomment it.",
|
|
||||||
@" Other similar extension points exist, see Microsoft.Common.targets.",
|
|
||||||
@" <Target Name=""BeforeBuild"">",
|
|
||||||
@" </Target>",
|
|
||||||
@" <Target Name=""AfterBuild"">",
|
|
||||||
@" </Target>",
|
|
||||||
@" -->",
|
|
||||||
@"</Project>",
|
|
||||||
@"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetSolutionText()
|
private static string GetSolutionText()
|
||||||
@@ -1024,7 +988,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
projectGuid);
|
projectGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EscapedRelativePathFor(string file, out UnityEditor.PackageManager.PackageInfo packageInfo)
|
internal string EscapedRelativePathFor(string file, out UnityEditor.PackageManager.PackageInfo packageInfo)
|
||||||
{
|
{
|
||||||
var projectDir = ProjectDirectory.NormalizePathSeparators();
|
var projectDir = ProjectDirectory.NormalizePathSeparators();
|
||||||
file = file.NormalizePathSeparators();
|
file = file.NormalizePathSeparators();
|
||||||
@@ -1042,24 +1006,24 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return XmlFilename(path);
|
return XmlFilename(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SkipPathPrefix(string path, string prefix)
|
internal 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 GetProjectExtension()
|
internal static string GetProjectExtension()
|
||||||
{
|
{
|
||||||
return ".csproj";
|
return ".csproj";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ProjectGuid(string assemblyName)
|
internal string ProjectGuid(string assemblyName)
|
||||||
{
|
{
|
||||||
return m_GUIDGenerator.ProjectGuid(m_ProjectName, assemblyName);
|
return m_GUIDGenerator.ProjectGuid(m_ProjectName, assemblyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ProjectGuid(Assembly assembly)
|
internal string ProjectGuid(Assembly assembly)
|
||||||
{
|
{
|
||||||
return ProjectGuid(m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, assembly.name));
|
return ProjectGuid(m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, assembly.name));
|
||||||
}
|
}
|
||||||
|
|||||||
82
Editor/ProjectGeneration/SdkStyleProjectGeneration.cs
Normal file
82
Editor/ProjectGeneration/SdkStyleProjectGeneration.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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.Text;
|
||||||
|
using UnityEditor.Compilation;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
|
{
|
||||||
|
internal class SdkStyleProjectGeneration : ProjectGeneration
|
||||||
|
{
|
||||||
|
internal class SdkStyleAssemblyNameProvider : AssemblyNameProvider
|
||||||
|
{
|
||||||
|
// disable PlayerGeneration with SdkStyle projects
|
||||||
|
internal override ProjectGenerationFlag ProjectGenerationFlagImpl => base.ProjectGenerationFlagImpl & ~ProjectGenerationFlag.PlayerAssemblies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SdkStyleProjectGeneration() : base(
|
||||||
|
Directory.GetParent(Application.dataPath)?.FullName,
|
||||||
|
new SdkStyleAssemblyNameProvider(),
|
||||||
|
new FileIOProvider(),
|
||||||
|
new GUIDProvider())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
|
||||||
|
{
|
||||||
|
headerBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
headerBuilder.Append(@"<Project ToolsVersion=""Current"" Sdk=""Microsoft.NET.Sdk"">").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <!-- Generated file, do not modify, your changes will be overwritten (use AssetPostprocessor.OnGeneratedCSProject) -->").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <GenerateAssemblyInfo>false</GenerateAssemblyInfo>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <EnableDefaultItems>false</EnableDefaultItems>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <LangVersion>").Append(properties.LangVersion).Append(@"</LangVersion>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <Configurations>Debug;Release</Configurations>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <RootNamespace>").Append(properties.RootNamespace).Append(@"</RootNamespace>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <OutputType>Library</OutputType>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <AssemblyName>").Append(properties.AssemblyName).Append(@"</AssemblyName>").Append(k_WindowsNewline);
|
||||||
|
// In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version will have no impact, even when targeting netstandard/net48 from Unity.
|
||||||
|
// But with SDK style we use netstandard2.0 (net471 for legacy), so 3rd party tools will not fail to work when .NETFW reference assemblies are not installed.
|
||||||
|
// Unity already selected proper API surface through referenced DLLs for us.
|
||||||
|
headerBuilder.Append(@" <TargetFramework>netstandard2.0</TargetFramework>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <BaseDirectory>.</BaseDirectory>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
|
||||||
|
GetProjectHeaderConfigurations(properties, headerBuilder);
|
||||||
|
|
||||||
|
// Explicit references
|
||||||
|
headerBuilder.Append(@" <PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <NoStandardLibraries>true</NoStandardLibraries>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <NoStdLib>true</NoStdLib>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <NoConfig>true</NoConfig>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" <MSBuildWarningsAsMessages>MSB3277</MSBuildWarningsAsMessages>").Append(k_WindowsNewline);
|
||||||
|
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);
|
||||||
|
|
||||||
|
GetProjectHeaderVstuFlavoring(properties, headerBuilder, false);
|
||||||
|
GetProjectHeaderAnalyzers(properties, headerBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void AppendProjectReference(Assembly assembly, Assembly reference, StringBuilder projectBuilder)
|
||||||
|
{
|
||||||
|
// If the current assembly is a Player project, we want to project-reference the corresponding Player project
|
||||||
|
var referenceName = m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, reference.name);
|
||||||
|
projectBuilder.Append(@" <ProjectReference Include=""").Append(referenceName).Append(GetProjectExtension()).Append(@""" />").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void GetProjectFooter(StringBuilder footerBuilder)
|
||||||
|
{
|
||||||
|
footerBuilder.Append("</Project>").Append(k_WindowsNewline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/ProjectGeneration/SdkStyleProjectGeneration.cs.meta
Normal file
11
Editor/ProjectGeneration/SdkStyleProjectGeneration.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e8aed47cdce10fd4cae32fefa2c34f8f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
345
Editor/VisualStudioCodeInstallation.cs
Normal file
345
Editor/VisualStudioCodeInstallation.cs
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using UnityEngine;
|
||||||
|
using IOPath = System.IO.Path;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
|
{
|
||||||
|
internal class VisualStudioCodeInstallation : VisualStudioInstallation
|
||||||
|
{
|
||||||
|
private static readonly IGenerator _generator = new SdkStyleProjectGeneration();
|
||||||
|
|
||||||
|
public override bool SupportsAnalyzers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Version LatestLanguageVersionSupported
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new Version(11, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetExtensionPath()
|
||||||
|
{
|
||||||
|
var vscode = IsPrerelease ? ".vscode-insiders" : ".vscode";
|
||||||
|
var extensionsPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), vscode, "extensions");
|
||||||
|
if (!Directory.Exists(extensionsPath))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Directory
|
||||||
|
.EnumerateDirectories(extensionsPath, "visualstudiotoolsforunity.vstuc*") // publisherid.extensionid
|
||||||
|
.OrderByDescending(n => n)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string[] GetAnalyzers()
|
||||||
|
{
|
||||||
|
var vstuPath = GetExtensionPath();
|
||||||
|
if (string.IsNullOrEmpty(vstuPath))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return GetAnalyzers(vstuPath); }
|
||||||
|
|
||||||
|
public override IGenerator ProjectGenerator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _generator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCandidateForDiscovery(string path)
|
||||||
|
{
|
||||||
|
if (VisualStudioEditor.IsOSX)
|
||||||
|
return Directory.Exists(path) && Regex.IsMatch(path, ".*Code.*.app$", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (VisualStudioEditor.IsWindows)
|
||||||
|
return File.Exists(path) && Regex.IsMatch(path, ".*Code.*.exe$", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
return File.Exists(path) && path.EndsWith("code", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class VisualStudioCodeManifest
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
||||||
|
{
|
||||||
|
installation = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsCandidateForDiscovery(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Version version = null;
|
||||||
|
var isPrerelease = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var manifestBase = GetRealPath(editorPath);
|
||||||
|
|
||||||
|
if (VisualStudioEditor.IsWindows) // on Windows, editorPath is a file, resources as subdirectory
|
||||||
|
manifestBase = IOPath.GetDirectoryName(manifestBase);
|
||||||
|
else if (VisualStudioEditor.IsOSX) // on Mac, editorPath is a directory
|
||||||
|
manifestBase = IOPath.Combine(manifestBase, "Contents");
|
||||||
|
else // on Linux, editorPath is a file, in a bin sub-directory
|
||||||
|
manifestBase = Directory.GetParent(manifestBase)?.Parent?.FullName;
|
||||||
|
|
||||||
|
if (manifestBase == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var manifestFullPath = IOPath.Combine(manifestBase, @"resources", "app", "package.json");
|
||||||
|
if (File.Exists(manifestFullPath))
|
||||||
|
{
|
||||||
|
var manifest = JsonUtility.FromJson<VisualStudioCodeManifest>(File.ReadAllText(manifestFullPath));
|
||||||
|
Version.TryParse(manifest.version.Split('-').First(), out version);
|
||||||
|
isPrerelease = manifest.version.ToLower().Contains("insider");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// do not fail if we are not able to retrieve the exact version number
|
||||||
|
}
|
||||||
|
|
||||||
|
isPrerelease = isPrerelease || editorPath.ToLower().Contains("insider");
|
||||||
|
installation = new VisualStudioCodeInstallation()
|
||||||
|
{
|
||||||
|
IsPrerelease = isPrerelease,
|
||||||
|
Name = "Visual Studio Code" + (isPrerelease ? " - Insider" : string.Empty) + (version != null ? $" [{version.ToString(3)}]" : string.Empty),
|
||||||
|
Path = editorPath,
|
||||||
|
Version = version ?? new Version()
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
||||||
|
{
|
||||||
|
var candidates = new List<string>();
|
||||||
|
|
||||||
|
if (VisualStudioEditor.IsWindows)
|
||||||
|
{
|
||||||
|
var localAppPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs");
|
||||||
|
var programFiles = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles));
|
||||||
|
|
||||||
|
foreach (var basePath in new[] {localAppPath, programFiles})
|
||||||
|
{
|
||||||
|
candidates.Add(IOPath.Combine(basePath, "Microsoft VS Code", "Code.exe"));
|
||||||
|
candidates.Add(IOPath.Combine(basePath, "Microsoft VS Code Insiders", "Code - Insiders.exe"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (VisualStudioEditor.IsOSX)
|
||||||
|
{
|
||||||
|
var appPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles));
|
||||||
|
candidates.AddRange(Directory.EnumerateDirectories(appPath, "Visual Studio Code*.app"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
candidates.Add("/usr/bin/code");
|
||||||
|
candidates.Add("/bin/code");
|
||||||
|
candidates.Add("/usr/local/bin/code");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
if (TryDiscoverInstallation(candidate, out var installation))
|
||||||
|
yield return installation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR_LINUX
|
||||||
|
[DllImport ("libc")]
|
||||||
|
private static extern int readlink(string path, byte[] buffer, int buflen);
|
||||||
|
|
||||||
|
internal static string GetRealPath(string path)
|
||||||
|
{
|
||||||
|
byte[] buf = new byte[512];
|
||||||
|
int ret = readlink(path, buf, buf.Length);
|
||||||
|
if (ret == -1) return path;
|
||||||
|
char[] cbuf = new char[512];
|
||||||
|
int chars = System.Text.Encoding.Default.GetChars(buf, 0, ret, cbuf, 0);
|
||||||
|
return new String(cbuf, 0, chars);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
internal static string GetRealPath(string path)
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public override void CreateExtraFiles(string projectDirectory)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
|
||||||
|
var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode");
|
||||||
|
Directory.CreateDirectory(vscodeDirectory);
|
||||||
|
|
||||||
|
CreateRecommendedExtensionsFile(vscodeDirectory);
|
||||||
|
CreateSettingsFile(vscodeDirectory);
|
||||||
|
CreateLaunchFile(vscodeDirectory);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateLaunchFile(string vscodeDirectory)
|
||||||
|
{
|
||||||
|
var launchFile = IOPath.Combine(vscodeDirectory, "launch.json");
|
||||||
|
if (File.Exists(launchFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const string content = @"{
|
||||||
|
""version"": ""0.2.0"",
|
||||||
|
""configurations"": [
|
||||||
|
{
|
||||||
|
""name"": ""Attach to Unity"",
|
||||||
|
""type"": ""vstuc"",
|
||||||
|
""request"": ""attach"",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}";
|
||||||
|
|
||||||
|
File.WriteAllText(launchFile, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateSettingsFile(string vscodeDirectory)
|
||||||
|
{
|
||||||
|
var settingsFile = IOPath.Combine(vscodeDirectory, "settings.json");
|
||||||
|
if (File.Exists(settingsFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const string content = @"{
|
||||||
|
""files.exclude"":
|
||||||
|
{
|
||||||
|
""**/.DS_Store"":true,
|
||||||
|
""**/.git"":true,
|
||||||
|
""**/.vs"":true,
|
||||||
|
""**/.gitmodules"":true,
|
||||||
|
""**/.vsconfig"":true,
|
||||||
|
""**/*.booproj"":true,
|
||||||
|
""**/*.pidb"":true,
|
||||||
|
""**/*.suo"":true,
|
||||||
|
""**/*.user"":true,
|
||||||
|
""**/*.userprefs"":true,
|
||||||
|
""**/*.unityproj"":true,
|
||||||
|
""**/*.dll"":true,
|
||||||
|
""**/*.exe"":true,
|
||||||
|
""**/*.pdf"":true,
|
||||||
|
""**/*.mid"":true,
|
||||||
|
""**/*.midi"":true,
|
||||||
|
""**/*.wav"":true,
|
||||||
|
""**/*.gif"":true,
|
||||||
|
""**/*.ico"":true,
|
||||||
|
""**/*.jpg"":true,
|
||||||
|
""**/*.jpeg"":true,
|
||||||
|
""**/*.png"":true,
|
||||||
|
""**/*.psd"":true,
|
||||||
|
""**/*.tga"":true,
|
||||||
|
""**/*.tif"":true,
|
||||||
|
""**/*.tiff"":true,
|
||||||
|
""**/*.3ds"":true,
|
||||||
|
""**/*.3DS"":true,
|
||||||
|
""**/*.fbx"":true,
|
||||||
|
""**/*.FBX"":true,
|
||||||
|
""**/*.lxo"":true,
|
||||||
|
""**/*.LXO"":true,
|
||||||
|
""**/*.ma"":true,
|
||||||
|
""**/*.MA"":true,
|
||||||
|
""**/*.obj"":true,
|
||||||
|
""**/*.OBJ"":true,
|
||||||
|
""**/*.asset"":true,
|
||||||
|
""**/*.cubemap"":true,
|
||||||
|
""**/*.flare"":true,
|
||||||
|
""**/*.mat"":true,
|
||||||
|
""**/*.meta"":true,
|
||||||
|
""**/*.prefab"":true,
|
||||||
|
""**/*.unity"":true,
|
||||||
|
""build/"":true,
|
||||||
|
""Build/"":true,
|
||||||
|
""Library/"":true,
|
||||||
|
""library/"":true,
|
||||||
|
""obj/"":true,
|
||||||
|
""Obj/"":true,
|
||||||
|
""Logs/"":true,
|
||||||
|
""logs/"":true,
|
||||||
|
""ProjectSettings/"":true,
|
||||||
|
""UserSettings/"":true,
|
||||||
|
""temp/"":true,
|
||||||
|
""Temp/"":true
|
||||||
|
},
|
||||||
|
""omnisharp.enableRoslynAnalyzers"": true
|
||||||
|
}";
|
||||||
|
|
||||||
|
File.WriteAllText(settingsFile, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateRecommendedExtensionsFile(string vscodeDirectory)
|
||||||
|
{
|
||||||
|
var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json");
|
||||||
|
if (File.Exists(extensionFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const string content = @"{
|
||||||
|
""recommendations"": [
|
||||||
|
""visualstudiotoolsforunity.vstuc""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
";
|
||||||
|
File.WriteAllText(extensionFile, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Open(string path, int line, int column, string solution)
|
||||||
|
{
|
||||||
|
line = Math.Max(1, line);
|
||||||
|
column = Math.Max(0, column);
|
||||||
|
|
||||||
|
var directory = IOPath.GetDirectoryName(solution);
|
||||||
|
var application = Path;
|
||||||
|
|
||||||
|
ProcessRunner.Start(string.IsNullOrEmpty(path) ?
|
||||||
|
ProcessStartInfoFor(application, $"\"{directory}\"") :
|
||||||
|
ProcessStartInfoFor(application, $"\"{directory}\" -g \"{path}\":{line}:{column}"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProcessStartInfo ProcessStartInfoFor(string application, string arguments)
|
||||||
|
{
|
||||||
|
if (!VisualStudioEditor.IsOSX)
|
||||||
|
return ProcessRunner.ProcessStartInfoFor(application, arguments, redirect: false);
|
||||||
|
|
||||||
|
// wrap with built-in OSX open feature
|
||||||
|
arguments = $"-n \"{application}\" --args {arguments}";
|
||||||
|
application = "open";
|
||||||
|
return ProcessRunner.ProcessStartInfoFor(application, arguments, redirect:false, shell: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/VisualStudioCodeInstallation.cs.meta
Normal file
11
Editor/VisualStudioCodeInstallation.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1be5c96ff30e6ec40876f28fd9ab7e24
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -4,15 +4,13 @@
|
|||||||
* 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;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Unity.CodeEditor;
|
using Unity.CodeEditor;
|
||||||
using System.Threading;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
|
[assembly: InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
|
||||||
[assembly: InternalsVisibleTo("Unity.VisualStudio.Standalone.EditorTests")]
|
[assembly: InternalsVisibleTo("Unity.VisualStudio.Standalone.EditorTests")]
|
||||||
@@ -26,39 +24,53 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
internal static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor;
|
internal static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor;
|
||||||
internal static bool IsWindows => !IsOSX && Path.DirectorySeparatorChar == FileUtility.WinSeparator && Environment.NewLine == "\r\n";
|
internal static bool IsWindows => !IsOSX && Path.DirectorySeparatorChar == FileUtility.WinSeparator && Environment.NewLine == "\r\n";
|
||||||
|
|
||||||
CodeEditor.Installation[] IExternalCodeEditor.Installations => _discoverInstallations.Result
|
CodeEditor.Installation[] IExternalCodeEditor.Installations => _discoverInstallations
|
||||||
.Select(i => i.ToCodeEditorInstallation())
|
.Result
|
||||||
|
.Values
|
||||||
|
.Select(v => v.ToCodeEditorInstallation())
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
private static readonly AsyncOperation<IVisualStudioInstallation[]> _discoverInstallations;
|
private static readonly AsyncOperation<Dictionary<string, IVisualStudioInstallation>> _discoverInstallations;
|
||||||
|
|
||||||
private readonly IGenerator _generator = new ProjectGeneration();
|
|
||||||
|
|
||||||
static VisualStudioEditor()
|
static VisualStudioEditor()
|
||||||
{
|
{
|
||||||
if (!UnityInstallation.IsMainUnityEditorProcess)
|
if (!UnityInstallation.IsMainUnityEditorProcess)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (IsWindows)
|
Discovery.Initialize();
|
||||||
Discovery.FindVSWhere();
|
|
||||||
|
|
||||||
CodeEditor.Register(new VisualStudioEditor());
|
CodeEditor.Register(new VisualStudioEditor());
|
||||||
|
|
||||||
_discoverInstallations = AsyncOperation<IVisualStudioInstallation[]>.Run(DiscoverInstallations);
|
_discoverInstallations = AsyncOperation<Dictionary<string, IVisualStudioInstallation>>.Run(DiscoverInstallations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IVisualStudioInstallation[] DiscoverInstallations()
|
#if UNITY_2019_4_OR_NEWER && !UNITY_2020
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
static void LegacyVisualStudioCodePackageDisabler()
|
||||||
|
{
|
||||||
|
// disable legacy Visual Studio Code packages
|
||||||
|
var editor = CodeEditor.Editor.GetCodeEditorForPath("code.cmd");
|
||||||
|
if (editor == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (editor is VisualStudioEditor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CodeEditor.Unregister(editor);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private static Dictionary<string, IVisualStudioInstallation> DiscoverInstallations()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Discovery
|
return Discovery
|
||||||
.GetVisualStudioInstallations()
|
.GetVisualStudioInstallations()
|
||||||
.ToArray();
|
.ToDictionary(i => Path.GetFullPath(i.Path), i => i);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.LogError($"Error detecting Visual Studio installations: {ex}");
|
Debug.LogError($"Error detecting Visual Studio installations: {ex}");
|
||||||
return Array.Empty<IVisualStudioInstallation>();
|
return new Dictionary<string, IVisualStudioInstallation>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,36 +80,33 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
// keeping it for now given it is public, so we need a major bump to remove it
|
// keeping it for now given it is public, so we need a major bump to remove it
|
||||||
public void CreateIfDoesntExist()
|
public void CreateIfDoesntExist()
|
||||||
{
|
{
|
||||||
if (!_generator.HasSolutionBeenGenerated())
|
if (!TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
|
||||||
_generator.Sync();
|
return;
|
||||||
|
|
||||||
|
var generator = installation.ProjectGenerator;
|
||||||
|
if (!generator.HasSolutionBeenGenerated())
|
||||||
|
generator.Sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(string editorInstallationPath)
|
public void Initialize(string editorInstallationPath)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual bool TryGetVisualStudioInstallationForPath(string editorPath, bool searchInstallations, out IVisualStudioInstallation installation)
|
internal virtual bool TryGetVisualStudioInstallationForPath(string editorPath, bool lookupDiscoveredInstallations, out IVisualStudioInstallation installation)
|
||||||
{
|
{
|
||||||
if (searchInstallations)
|
editorPath = Path.GetFullPath(editorPath);
|
||||||
{
|
|
||||||
// lookup for well known installations
|
|
||||||
foreach (var candidate in _discoverInstallations.Result)
|
|
||||||
{
|
|
||||||
if (!string.Equals(Path.GetFullPath(editorPath), Path.GetFullPath(candidate.Path), StringComparison.OrdinalIgnoreCase))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
installation = candidate;
|
// lookup for well known installations
|
||||||
|
if (lookupDiscoveredInstallations && _discoverInstallations.Result.TryGetValue(editorPath, out installation))
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Discovery.TryDiscoverInstallation(editorPath, out installation);
|
return Discovery.TryDiscoverInstallation(editorPath, out installation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
|
public virtual bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
|
||||||
{
|
{
|
||||||
var result = TryGetVisualStudioInstallationForPath(editorPath, searchInstallations: false, out var vsi);
|
var result = TryGetVisualStudioInstallationForPath(editorPath, lookupDiscoveredInstallations: false, out var vsi);
|
||||||
installation = vsi == null ? default : vsi.ToCodeEditorInstallation();
|
installation = vsi?.ToCodeEditorInstallation() ?? default;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +115,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
GUILayout.BeginHorizontal();
|
GUILayout.BeginHorizontal();
|
||||||
GUILayout.FlexibleSpace();
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (!TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
|
||||||
|
return;
|
||||||
|
|
||||||
var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(GetType().Assembly);
|
var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(GetType().Assembly);
|
||||||
|
|
||||||
var style = new GUIStyle
|
var style = new GUIStyle
|
||||||
@@ -119,41 +131,44 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
|
|
||||||
EditorGUILayout.LabelField("Generate .csproj files for:");
|
EditorGUILayout.LabelField("Generate .csproj files for:");
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "");
|
SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.Local, "Local packages", "");
|
SettingsButton(ProjectGenerationFlag.Local, "Local packages", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.Registry, "Registry packages", "");
|
SettingsButton(ProjectGenerationFlag.Registry, "Registry packages", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.Git, "Git packages", "");
|
SettingsButton(ProjectGenerationFlag.Git, "Git packages", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.BuiltIn, "Built-in packages", "");
|
SettingsButton(ProjectGenerationFlag.BuiltIn, "Built-in packages", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.LocalTarBall, "Local tarball", "");
|
SettingsButton(ProjectGenerationFlag.LocalTarBall, "Local tarball", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.Unknown, "Packages from unknown sources", "");
|
SettingsButton(ProjectGenerationFlag.Unknown, "Packages from unknown sources", "", installation);
|
||||||
SettingsButton(ProjectGenerationFlag.PlayerAssemblies, "Player projects", "For each player project generate an additional csproj with the name 'project-player.csproj'");
|
SettingsButton(ProjectGenerationFlag.PlayerAssemblies, "Player projects", "For each player project generate an additional csproj with the name 'project-player.csproj'", installation);
|
||||||
RegenerateProjectFiles();
|
RegenerateProjectFiles(installation);
|
||||||
EditorGUI.indentLevel--;
|
EditorGUI.indentLevel--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegenerateProjectFiles()
|
private static void RegenerateProjectFiles(IVisualStudioInstallation installation)
|
||||||
{
|
{
|
||||||
var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(new GUILayoutOption[] { }));
|
var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect());
|
||||||
rect.width = 252;
|
rect.width = 252;
|
||||||
if (GUI.Button(rect, "Regenerate project files"))
|
if (GUI.Button(rect, "Regenerate project files"))
|
||||||
{
|
{
|
||||||
_generator.Sync();
|
installation.ProjectGenerator.Sync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsButton(ProjectGenerationFlag preference, string guiMessage, string toolTip)
|
private static void SettingsButton(ProjectGenerationFlag preference, string guiMessage, string toolTip, IVisualStudioInstallation installation)
|
||||||
{
|
{
|
||||||
var prevValue = _generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(preference);
|
var generator = installation.ProjectGenerator;
|
||||||
|
var prevValue = generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(preference);
|
||||||
|
|
||||||
var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue);
|
var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue);
|
||||||
if (newValue != prevValue)
|
if (newValue != prevValue)
|
||||||
{
|
generator.AssemblyNameProvider.ToggleProjectGeneration(preference);
|
||||||
_generator.AssemblyNameProvider.ToggleProjectGeneration(preference);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles)
|
public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles)
|
||||||
{
|
{
|
||||||
_generator.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles), importedFiles);
|
if (TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
|
||||||
|
{
|
||||||
|
installation.ProjectGenerator.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles), importedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb"))
|
foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb"))
|
||||||
{
|
{
|
||||||
@@ -170,16 +185,19 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
if (Symbols.IsPortableSymbolFile(pdbFile))
|
if (Symbols.IsPortableSymbolFile(pdbFile))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
UnityEngine.Debug.LogWarning($"Unity is only able to load mdb or portable-pdb symbols. {file} is using a legacy pdb format.");
|
Debug.LogWarning($"Unity is only able to load mdb or portable-pdb symbols. {file} is using a legacy pdb format.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SyncAll()
|
public void SyncAll()
|
||||||
{
|
{
|
||||||
_generator.Sync();
|
if (TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
|
||||||
|
{
|
||||||
|
installation.ProjectGenerator.Sync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSupportedPath(string path)
|
private static bool IsSupportedPath(string path, IGenerator generator)
|
||||||
{
|
{
|
||||||
// Path is empty with "Open C# Project", as we only want to open the solution without specific files
|
// Path is empty with "Open C# Project", as we only want to open the solution without specific files
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
@@ -188,44 +206,27 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
// cs, uxml, uss, shader, compute, cginc, hlsl, glslinc, template are part of Unity builtin extensions
|
// cs, uxml, uss, shader, compute, cginc, hlsl, glslinc, template are part of Unity builtin extensions
|
||||||
// txt, xml, fnt, cd are -often- par of Unity user extensions
|
// txt, xml, fnt, cd are -often- par of Unity user extensions
|
||||||
// asdmdef is mandatory included
|
// asdmdef is mandatory included
|
||||||
if (_generator.IsSupportedFile(path))
|
return generator.IsSupportedFile(path);
|
||||||
return true;
|
|
||||||
|
|
||||||
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();
|
var editorPath = CodeEditor.CurrentEditorInstallation;
|
||||||
|
|
||||||
if (!IsSupportedPath(path))
|
if (!Discovery.TryDiscoverInstallation(editorPath, out var installation)) {
|
||||||
|
Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var generator = installation.ProjectGenerator;
|
||||||
|
if (!IsSupportedPath(path, generator))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsProjectGeneratedFor(path, out var missingFlag))
|
if (!IsProjectGeneratedFor(path, generator, 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.");
|
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)
|
var solution = GetOrGenerateSolutionFile(generator);
|
||||||
return OpenOSXApp(path, line, column);
|
return installation.Open(path, line, column, solution);
|
||||||
|
|
||||||
if (IsWindows)
|
|
||||||
return OpenWindowsApp(path, line);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetProjectGenerationFlagDescription(ProjectGenerationFlag flag)
|
private static string GetProjectGenerationFlagDescription(ProjectGenerationFlag flag)
|
||||||
@@ -253,7 +254,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsProjectGeneratedFor(string path, out ProjectGenerationFlag missingFlag)
|
private static bool IsProjectGeneratedFor(string path, IGenerator generator, out ProjectGenerationFlag missingFlag)
|
||||||
{
|
{
|
||||||
missingFlag = ProjectGenerationFlag.None;
|
missingFlag = ProjectGenerationFlag.None;
|
||||||
|
|
||||||
@@ -266,9 +267,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Even on windows, the package manager requires relative path + unix style separators for queries
|
// Even on windows, the package manager requires relative path + unix style separators for queries
|
||||||
var basePath = _generator.ProjectDirectory;
|
var basePath = generator.ProjectDirectory;
|
||||||
var relativePath = FileUtility
|
var relativePath = path
|
||||||
.NormalizeWindowsToUnix(path)
|
.NormalizeWindowsToUnix()
|
||||||
.Replace(basePath, string.Empty)
|
.Replace(basePath, string.Empty)
|
||||||
.Trim(FileUtility.UnixSeparator);
|
.Trim(FileUtility.UnixSeparator);
|
||||||
|
|
||||||
@@ -280,7 +281,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
if (!Enum.TryParse<ProjectGenerationFlag>(source.ToString(), out var flag))
|
if (!Enum.TryParse<ProjectGenerationFlag>(source.ToString(), out var flag))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (_generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(flag))
|
if (generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(flag))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Return false if we found a source not flagged for generation
|
// Return false if we found a source not flagged for generation
|
||||||
@@ -288,118 +289,10 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum COMIntegrationState
|
private static string GetOrGenerateSolutionFile(IGenerator generator)
|
||||||
{
|
{
|
||||||
Running,
|
generator.Sync();
|
||||||
DisplayProgressBar,
|
return generator.SolutionFile();
|
||||||
ClearProgressBar,
|
|
||||||
Exited
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool OpenWindowsApp(string path, int line)
|
|
||||||
{
|
|
||||||
var progpath = FileUtility.GetPackageAssetFullPath("Editor", "COMIntegration", "Release", "COMIntegration.exe");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(progpath))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string absolutePath = "";
|
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
absolutePath = Path.GetFullPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We remove all invalid chars from the solution filename, but we cannot prevent the user from using a specific path for the Unity project
|
|
||||||
// So process the fullpath to make it compatible with VS
|
|
||||||
var solution = GetOrGenerateSolutionFile(path);
|
|
||||||
if (!string.IsNullOrWhiteSpace(solution))
|
|
||||||
{
|
|
||||||
solution = $"\"{solution}\"";
|
|
||||||
solution = solution.Replace("^", "^^");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var psi = ProcessRunner.ProcessStartInfoFor(progpath, $"\"{CodeEditor.CurrentEditorInstallation}\" {solution} \"{absolutePath}\" {line}");
|
|
||||||
psi.StandardOutputEncoding = System.Text.Encoding.Unicode;
|
|
||||||
psi.StandardErrorEncoding = System.Text.Encoding.Unicode;
|
|
||||||
|
|
||||||
// inter thread communication
|
|
||||||
var messages = new BlockingCollection<COMIntegrationState>();
|
|
||||||
|
|
||||||
var asyncStart = AsyncOperation<ProcessRunnerResult>.Run(
|
|
||||||
() => ProcessRunner.StartAndWaitForExit(psi, onOutputReceived: data => OnOutputReceived(data, messages)),
|
|
||||||
e => new ProcessRunnerResult {Success = false, Error = e.Message, Output = string.Empty},
|
|
||||||
() => messages.Add(COMIntegrationState.Exited)
|
|
||||||
);
|
|
||||||
|
|
||||||
MonitorCOMIntegration(messages);
|
|
||||||
|
|
||||||
var result = asyncStart.Result;
|
|
||||||
|
|
||||||
if (!result.Success && !string.IsNullOrWhiteSpace(result.Error))
|
|
||||||
Debug.LogError($"Error while starting Visual Studio: {result.Error}");
|
|
||||||
|
|
||||||
return result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void MonitorCOMIntegration(BlockingCollection<COMIntegrationState> messages)
|
|
||||||
{
|
|
||||||
var displayingProgress = false;
|
|
||||||
COMIntegrationState state;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
state = messages.Take();
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case COMIntegrationState.ClearProgressBar:
|
|
||||||
EditorUtility.ClearProgressBar();
|
|
||||||
displayingProgress = false;
|
|
||||||
break;
|
|
||||||
case COMIntegrationState.DisplayProgressBar:
|
|
||||||
EditorUtility.DisplayProgressBar("Opening Visual Studio", "Starting up Visual Studio, this might take some time.", .5f);
|
|
||||||
displayingProgress = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (state != COMIntegrationState.Exited);
|
|
||||||
|
|
||||||
// Make sure the progress bar is properly cleared in case of COMIntegration failure
|
|
||||||
if (displayingProgress)
|
|
||||||
EditorUtility.ClearProgressBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly COMIntegrationState[] ProgressBarCommands = {COMIntegrationState.DisplayProgressBar, COMIntegrationState.ClearProgressBar};
|
|
||||||
private static void OnOutputReceived(string data, BlockingCollection<COMIntegrationState> messages)
|
|
||||||
{
|
|
||||||
if (data == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var cmd in ProgressBarCommands)
|
|
||||||
{
|
|
||||||
if (data.IndexOf(cmd.ToString(), StringComparison.OrdinalIgnoreCase) >= 0)
|
|
||||||
messages.Add(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("AppleEventIntegration")]
|
|
||||||
static extern bool OpenVisualStudio(string appPath, string solutionPath, string filePath, int line);
|
|
||||||
|
|
||||||
bool OpenOSXApp(string path, int line, int column)
|
|
||||||
{
|
|
||||||
string absolutePath = "";
|
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
absolutePath = Path.GetFullPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
var solution = GetOrGenerateSolutionFile(path);
|
|
||||||
return OpenVisualStudio(CodeEditor.CurrentEditorInstallation, solution, absolutePath, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetOrGenerateSolutionFile(string path)
|
|
||||||
{
|
|
||||||
_generator.Sync();
|
|
||||||
return _generator.SolutionFile();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
180
Editor/VisualStudioForMacInstallation.cs
Normal file
180
Editor/VisualStudioForMacInstallation.cs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Unity.CodeEditor;
|
||||||
|
using IOPath = System.IO.Path;
|
||||||
|
|
||||||
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
|
{
|
||||||
|
internal class VisualStudioForMacInstallation : VisualStudioInstallation
|
||||||
|
{
|
||||||
|
// C# language version support for Visual Studio for Mac
|
||||||
|
private static readonly VersionPair[] OSXVersionTable =
|
||||||
|
{
|
||||||
|
// VisualStudio for Mac 2022
|
||||||
|
new VersionPair(17,4, /* => */ 11,0),
|
||||||
|
new VersionPair(17,0, /* => */ 10,0),
|
||||||
|
|
||||||
|
// VisualStudio for Mac 8.x
|
||||||
|
new VersionPair(8,8, /* => */ 9,0),
|
||||||
|
new VersionPair(8,3, /* => */ 8,0),
|
||||||
|
new VersionPair(8,0, /* => */ 7,3),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly IGenerator _generator = new LegacyStyleProjectGeneration();
|
||||||
|
|
||||||
|
public override bool SupportsAnalyzers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Version >= new Version(8, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Version LatestLanguageVersionSupported
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetLatestLanguageVersionSupported(OSXVersionTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetExtensionPath()
|
||||||
|
{
|
||||||
|
const string addinName = "MonoDevelop.Unity";
|
||||||
|
const string addinAssembly = addinName + ".dll";
|
||||||
|
|
||||||
|
// user addins repository
|
||||||
|
var localAddins = IOPath.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||||
|
$"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins");
|
||||||
|
|
||||||
|
// In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
|
||||||
|
// When installing another local user addin, MD will remove files inside the folder
|
||||||
|
// So we browse all VSTUM addins, and return the one with an addin assembly
|
||||||
|
if (Directory.Exists(localAddins))
|
||||||
|
{
|
||||||
|
foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
|
||||||
|
{
|
||||||
|
if (File.Exists(IOPath.Combine(folder, addinAssembly)))
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check in Visual Studio.app/
|
||||||
|
// In that case the name of the addin is used
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string[] GetAnalyzers()
|
||||||
|
{
|
||||||
|
var vstuPath = GetExtensionPath();
|
||||||
|
if (string.IsNullOrEmpty(vstuPath))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return GetAnalyzers(vstuPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IGenerator ProjectGenerator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _generator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCandidateForDiscovery(string path)
|
||||||
|
{
|
||||||
|
return Directory.Exists(path) && VisualStudioEditor.IsOSX && Regex.IsMatch(path, "Visual\\s?Studio(?!.*Code.*).*.app$", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
||||||
|
{
|
||||||
|
installation = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsCandidateForDiscovery(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// On Mac we use the .app folder, so we need to access to main assembly
|
||||||
|
var fvi = IOPath.Combine(editorPath, "Contents/Resources/lib/monodevelop/bin/VisualStudio.exe");
|
||||||
|
|
||||||
|
if (!File.Exists(fvi))
|
||||||
|
fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe");
|
||||||
|
|
||||||
|
if (!File.Exists(fvi))
|
||||||
|
fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.dll");
|
||||||
|
|
||||||
|
if (!File.Exists(fvi))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// VS preview are not using the isPrerelease flag so far
|
||||||
|
// On Windows FileDescription contains "Preview", but not on Mac
|
||||||
|
var vi = FileVersionInfo.GetVersionInfo(fvi);
|
||||||
|
var version = new Version(vi.ProductVersion);
|
||||||
|
var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview");
|
||||||
|
|
||||||
|
installation = new VisualStudioForMacInstallation()
|
||||||
|
{
|
||||||
|
IsPrerelease = isPrerelease,
|
||||||
|
Name = $"{vi.FileDescription}{(isPrerelease ? " Preview" : string.Empty)} [{version.ToString(3)}]",
|
||||||
|
Path = editorPath,
|
||||||
|
Version = version
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
||||||
|
{
|
||||||
|
if (!VisualStudioEditor.IsOSX)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var candidates = Directory.EnumerateDirectories("/Applications", "*.app");
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
if (TryDiscoverInstallation(candidate, out var installation))
|
||||||
|
yield return installation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("AppleEventIntegration")]
|
||||||
|
private static extern bool OpenVisualStudio(string appPath, string solutionPath, string filePath, int line);
|
||||||
|
|
||||||
|
public override void CreateExtraFiles(string projectDirectory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Open(string path, int line, int column, string solution)
|
||||||
|
{
|
||||||
|
string absolutePath = "";
|
||||||
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
absolutePath = IOPath.GetFullPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenVisualStudio(CodeEditor.CurrentEditorInstallation, solution, absolutePath, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/VisualStudioForMacInstallation.cs.meta
Normal file
11
Editor/VisualStudioForMacInstallation.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2c64241ee5e302b478d7f2522bbaa4e3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
363
Editor/VisualStudioForWindowsInstallation.cs
Normal file
363
Editor/VisualStudioForWindowsInstallation.cs
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Unity.CodeEditor;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
using IOPath = System.IO.Path;
|
||||||
|
|
||||||
|
namespace Microsoft.Unity.VisualStudio.Editor
|
||||||
|
{
|
||||||
|
internal class VisualStudioForWindowsInstallation : VisualStudioInstallation
|
||||||
|
{
|
||||||
|
// C# language version support for Visual Studio
|
||||||
|
private static readonly VersionPair[] WindowsVersionTable =
|
||||||
|
{
|
||||||
|
// VisualStudio 2022
|
||||||
|
new VersionPair(17,4, /* => */ 11,0),
|
||||||
|
new VersionPair(17,0, /* => */ 10,0),
|
||||||
|
|
||||||
|
// VisualStudio 2019
|
||||||
|
new VersionPair(16,8, /* => */ 9,0),
|
||||||
|
new VersionPair(16,0, /* => */ 8,0),
|
||||||
|
|
||||||
|
// VisualStudio 2017
|
||||||
|
new VersionPair(15,7, /* => */ 7,3),
|
||||||
|
new VersionPair(15,5, /* => */ 7,2),
|
||||||
|
new VersionPair(15,3, /* => */ 7,1),
|
||||||
|
new VersionPair(15,0, /* => */ 7,0),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string _vsWherePath = null;
|
||||||
|
private static readonly IGenerator _generator = new LegacyStyleProjectGeneration();
|
||||||
|
|
||||||
|
public override bool SupportsAnalyzers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Version >= new Version(16, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Version LatestLanguageVersionSupported
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetLatestLanguageVersionSupported(WindowsVersionTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadRegistry(RegistryKey hive, string keyName, string valueName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var unitykey = hive.OpenSubKey(keyName);
|
||||||
|
|
||||||
|
var result = (string)unitykey?.GetValue(valueName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetWindowsBridgeFromRegistry()
|
||||||
|
{
|
||||||
|
var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
|
||||||
|
const string valueName = "UnityExtensionPath";
|
||||||
|
|
||||||
|
var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
|
||||||
|
if (string.IsNullOrEmpty(bridge))
|
||||||
|
bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
|
||||||
|
|
||||||
|
return bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetExtensionPath()
|
||||||
|
{
|
||||||
|
const string extensionName = "Visual Studio Tools for Unity";
|
||||||
|
const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll";
|
||||||
|
|
||||||
|
var vsDirectory = IOPath.GetDirectoryName(Path);
|
||||||
|
var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName);
|
||||||
|
|
||||||
|
if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly)))
|
||||||
|
return vstuDirectory;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string[] GetAnalyzers()
|
||||||
|
{
|
||||||
|
var vstuPath = GetExtensionPath();
|
||||||
|
if (string.IsNullOrEmpty(vstuPath))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
var analyzers = GetAnalyzers(vstuPath);
|
||||||
|
if (analyzers?.Length > 0)
|
||||||
|
return analyzers;
|
||||||
|
|
||||||
|
var bridge = GetWindowsBridgeFromRegistry();
|
||||||
|
if (File.Exists(bridge))
|
||||||
|
return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), ".."));
|
||||||
|
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IGenerator ProjectGenerator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _generator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCandidateForDiscovery(string path)
|
||||||
|
{
|
||||||
|
return File.Exists(path) && VisualStudioEditor.IsWindows && Regex.IsMatch(path, "devenv.exe$", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
|
||||||
|
{
|
||||||
|
installation = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsCandidateForDiscovery(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// On windows we use the executable directly, so we can query extra information
|
||||||
|
if (!File.Exists(editorPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// VS preview are not using the isPrerelease flag so far
|
||||||
|
// On Windows FileDescription contains "Preview", but not on Mac
|
||||||
|
var vi = FileVersionInfo.GetVersionInfo(editorPath);
|
||||||
|
var version = new Version(vi.ProductVersion);
|
||||||
|
var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview");
|
||||||
|
|
||||||
|
installation = new VisualStudioForWindowsInstallation()
|
||||||
|
{
|
||||||
|
IsPrerelease = isPrerelease,
|
||||||
|
Name = $"{FormatProductName(vi.FileDescription)} [{version.ToString(3)}]",
|
||||||
|
Path = editorPath,
|
||||||
|
Version = version
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatProductName(string productName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(productName))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return productName.Replace("Microsoft ", string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
|
||||||
|
{
|
||||||
|
if (!VisualStudioEditor.IsWindows)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var installation in QueryVsWhere())
|
||||||
|
yield return installation;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region VsWhere Json Schema
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
[Serializable]
|
||||||
|
internal class VsWhereResult
|
||||||
|
{
|
||||||
|
public VsWhereEntry[] entries;
|
||||||
|
|
||||||
|
public static VsWhereResult FromJson(string json)
|
||||||
|
{
|
||||||
|
return JsonUtility.FromJson<VsWhereResult>("{ \"" + nameof(VsWhereResult.entries) + "\": " + json + " }");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<VisualStudioInstallation> ToVisualStudioInstallations()
|
||||||
|
{
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
yield return new VisualStudioForWindowsInstallation
|
||||||
|
{
|
||||||
|
Name = $"{FormatProductName(entry.displayName)} [{entry.catalog.productDisplayVersion}]",
|
||||||
|
Path = entry.productPath,
|
||||||
|
IsPrerelease = entry.isPrerelease,
|
||||||
|
Version = Version.Parse(entry.catalog.buildVersion)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class VsWhereEntry
|
||||||
|
{
|
||||||
|
public string displayName;
|
||||||
|
public bool isPrerelease;
|
||||||
|
public string productPath;
|
||||||
|
public VsWhereCatalog catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class VsWhereCatalog
|
||||||
|
{
|
||||||
|
public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0"
|
||||||
|
public string buildVersion;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS3021
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static IEnumerable<VisualStudioInstallation> QueryVsWhere()
|
||||||
|
{
|
||||||
|
var progpath = _vsWherePath;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(progpath))
|
||||||
|
return Enumerable.Empty<VisualStudioInstallation>();
|
||||||
|
|
||||||
|
var result = ProcessRunner.StartAndWaitForExit(progpath, "-prerelease -format json -utf8");
|
||||||
|
|
||||||
|
if (!result.Success)
|
||||||
|
throw new Exception($"Failure while running vswhere: {result.Error}");
|
||||||
|
|
||||||
|
// Do not catch any JsonException here, this will be handled by the caller
|
||||||
|
return VsWhereResult
|
||||||
|
.FromJson(result.Output)
|
||||||
|
.ToVisualStudioInstallations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum COMIntegrationState
|
||||||
|
{
|
||||||
|
Running,
|
||||||
|
DisplayProgressBar,
|
||||||
|
ClearProgressBar,
|
||||||
|
Exited
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CreateExtraFiles(string projectDirectory)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var vsConfigFile = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vsconfig");
|
||||||
|
if (File.Exists(vsConfigFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const string content = @"{
|
||||||
|
""version"": ""1.0"",
|
||||||
|
""components"": [
|
||||||
|
""Microsoft.VisualStudio.Workload.ManagedGame""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
";
|
||||||
|
File.WriteAllText(vsConfigFile, content);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Open(string path, int line, int column, string solution)
|
||||||
|
{
|
||||||
|
var progpath = FileUtility.GetPackageAssetFullPath("Editor", "COMIntegration", "Release", "COMIntegration.exe");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(progpath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string absolutePath = "";
|
||||||
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
absolutePath = IOPath.GetFullPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We remove all invalid chars from the solution filename, but we cannot prevent the user from using a specific path for the Unity project
|
||||||
|
// So process the fullpath to make it compatible with VS
|
||||||
|
if (!string.IsNullOrWhiteSpace(solution))
|
||||||
|
{
|
||||||
|
solution = $"\"{solution}\"";
|
||||||
|
solution = solution.Replace("^", "^^");
|
||||||
|
}
|
||||||
|
|
||||||
|
var psi = ProcessRunner.ProcessStartInfoFor(progpath, $"\"{CodeEditor.CurrentEditorInstallation}\" {solution} \"{absolutePath}\" {line}");
|
||||||
|
psi.StandardOutputEncoding = System.Text.Encoding.Unicode;
|
||||||
|
psi.StandardErrorEncoding = System.Text.Encoding.Unicode;
|
||||||
|
|
||||||
|
// inter thread communication
|
||||||
|
var messages = new BlockingCollection<COMIntegrationState>();
|
||||||
|
|
||||||
|
var asyncStart = AsyncOperation<ProcessRunnerResult>.Run(
|
||||||
|
() => ProcessRunner.StartAndWaitForExit(psi, onOutputReceived: data => OnOutputReceived(data, messages)),
|
||||||
|
e => new ProcessRunnerResult {Success = false, Error = e.Message, Output = string.Empty},
|
||||||
|
() => messages.Add(COMIntegrationState.Exited)
|
||||||
|
);
|
||||||
|
|
||||||
|
MonitorCOMIntegration(messages);
|
||||||
|
|
||||||
|
var result = asyncStart.Result;
|
||||||
|
|
||||||
|
if (!result.Success && !string.IsNullOrWhiteSpace(result.Error))
|
||||||
|
Debug.LogError($"Error while starting Visual Studio: {result.Error}");
|
||||||
|
|
||||||
|
return result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MonitorCOMIntegration(BlockingCollection<COMIntegrationState> messages)
|
||||||
|
{
|
||||||
|
var displayingProgress = false;
|
||||||
|
COMIntegrationState state;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
state = messages.Take();
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case COMIntegrationState.ClearProgressBar:
|
||||||
|
EditorUtility.ClearProgressBar();
|
||||||
|
displayingProgress = false;
|
||||||
|
break;
|
||||||
|
case COMIntegrationState.DisplayProgressBar:
|
||||||
|
EditorUtility.DisplayProgressBar("Opening Visual Studio", "Starting up Visual Studio, this might take some time.", .5f);
|
||||||
|
displayingProgress = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (state != COMIntegrationState.Exited);
|
||||||
|
|
||||||
|
// Make sure the progress bar is properly cleared in case of COMIntegration failure
|
||||||
|
if (displayingProgress)
|
||||||
|
EditorUtility.ClearProgressBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly COMIntegrationState[] ProgressBarCommands = {COMIntegrationState.DisplayProgressBar, COMIntegrationState.ClearProgressBar};
|
||||||
|
private static void OnOutputReceived(string data, BlockingCollection<COMIntegrationState> messages)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var cmd in ProgressBarCommands)
|
||||||
|
{
|
||||||
|
if (data.IndexOf(cmd.ToString(), StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
messages.Add(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
if (VisualStudioEditor.IsWindows)
|
||||||
|
_vsWherePath = FileUtility.GetPackageAssetFullPath("Editor", "VSWhere", "vswhere.exe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/VisualStudioForWindowsInstallation.cs.meta
Normal file
11
Editor/VisualStudioForWindowsInstallation.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: be7ef402265a7a549b2e43c11d1a22c5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Win32;
|
|
||||||
using Unity.CodeEditor;
|
using Unity.CodeEditor;
|
||||||
using IOPath = System.IO.Path;
|
using IOPath = System.IO.Path;
|
||||||
|
|
||||||
@@ -17,72 +16,27 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
Version LatestLanguageVersionSupported { get; }
|
Version LatestLanguageVersionSupported { get; }
|
||||||
string[] GetAnalyzers();
|
string[] GetAnalyzers();
|
||||||
CodeEditor.Installation ToCodeEditorInstallation();
|
CodeEditor.Installation ToCodeEditorInstallation();
|
||||||
|
bool Open(string path, int line, int column, string solutionPath);
|
||||||
|
IGenerator ProjectGenerator { get; }
|
||||||
|
void CreateExtraFiles(string projectDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class VisualStudioInstallation : IVisualStudioInstallation
|
internal abstract class VisualStudioInstallation : IVisualStudioInstallation
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public Version Version { get; set; }
|
public Version Version { get; set; }
|
||||||
public bool IsPrerelease { get; set; }
|
public bool IsPrerelease { get; set; }
|
||||||
|
|
||||||
public bool SupportsAnalyzers
|
public abstract bool SupportsAnalyzers { get; }
|
||||||
|
public abstract Version LatestLanguageVersionSupported { get; }
|
||||||
|
public abstract string[] GetAnalyzers();
|
||||||
|
public abstract IGenerator ProjectGenerator { get; }
|
||||||
|
public abstract void CreateExtraFiles(string projectDirectory);
|
||||||
|
public abstract bool Open(string path, int line, int column, string solutionPath);
|
||||||
|
|
||||||
|
protected Version GetLatestLanguageVersionSupported(VersionPair[] versions)
|
||||||
{
|
{
|
||||||
get
|
|
||||||
{
|
|
||||||
if (VisualStudioEditor.IsWindows)
|
|
||||||
return Version >= new Version(16, 3);
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsOSX)
|
|
||||||
return Version >= new Version(8, 3);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// C# language version support for Visual Studio
|
|
||||||
private static VersionPair[] WindowsVersionTable =
|
|
||||||
{
|
|
||||||
// VisualStudio 2022
|
|
||||||
new VersionPair(17,4, /* => */ 11,0),
|
|
||||||
new VersionPair(17,0, /* => */ 10,0),
|
|
||||||
|
|
||||||
// VisualStudio 2019
|
|
||||||
new VersionPair(16,8, /* => */ 9,0),
|
|
||||||
new VersionPair(16,0, /* => */ 8,0),
|
|
||||||
|
|
||||||
// VisualStudio 2017
|
|
||||||
new VersionPair(15,7, /* => */ 7,3),
|
|
||||||
new VersionPair(15,5, /* => */ 7,2),
|
|
||||||
new VersionPair(15,3, /* => */ 7,1),
|
|
||||||
new VersionPair(15,0, /* => */ 7,0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// C# language version support for Visual Studio for Mac
|
|
||||||
private static VersionPair[] OSXVersionTable =
|
|
||||||
{
|
|
||||||
// VisualStudio for Mac 2022
|
|
||||||
new VersionPair(17,4, /* => */ 11,0),
|
|
||||||
new VersionPair(17,0, /* => */ 10,0),
|
|
||||||
|
|
||||||
// VisualStudio for Mac 8.x
|
|
||||||
new VersionPair(8,8, /* => */ 9,0),
|
|
||||||
new VersionPair(8,3, /* => */ 8,0),
|
|
||||||
new VersionPair(8,0, /* => */ 7,3),
|
|
||||||
};
|
|
||||||
|
|
||||||
public Version LatestLanguageVersionSupported
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
VersionPair[] versions = null;
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsWindows)
|
|
||||||
versions = WindowsVersionTable;
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsOSX)
|
|
||||||
versions = OSXVersionTable;
|
|
||||||
|
|
||||||
if (versions != null)
|
if (versions != null)
|
||||||
{
|
{
|
||||||
foreach (var entry in versions)
|
foreach (var entry in versions)
|
||||||
@@ -92,90 +46,11 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// default to 7.0 given we support at least VS 2017
|
// default to 7.0
|
||||||
return new Version(7, 0);
|
return new Version(7, 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static string ReadRegistry(RegistryKey hive, string keyName, string valueName)
|
protected static string[] GetAnalyzers(string path)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var unitykey = hive.OpenSubKey(keyName);
|
|
||||||
|
|
||||||
var result = (string)unitykey?.GetValue(valueName);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetWindowsBridgeFromRegistry()
|
|
||||||
{
|
|
||||||
var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
|
|
||||||
const string valueName = "UnityExtensionPath";
|
|
||||||
|
|
||||||
var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
|
|
||||||
if (string.IsNullOrEmpty(bridge))
|
|
||||||
bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
|
|
||||||
|
|
||||||
return bridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only use this to find analyzers, we do not need to load this assembly anymore
|
|
||||||
private string GetExtensionPath()
|
|
||||||
{
|
|
||||||
if (VisualStudioEditor.IsWindows)
|
|
||||||
{
|
|
||||||
const string extensionName = "Visual Studio Tools for Unity";
|
|
||||||
const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll";
|
|
||||||
|
|
||||||
var vsDirectory = IOPath.GetDirectoryName(Path);
|
|
||||||
var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName);
|
|
||||||
|
|
||||||
if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly)))
|
|
||||||
return vstuDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsOSX)
|
|
||||||
{
|
|
||||||
const string addinName = "MonoDevelop.Unity";
|
|
||||||
const string addinAssembly = addinName + ".dll";
|
|
||||||
|
|
||||||
// user addins repository
|
|
||||||
var localAddins = IOPath.Combine(
|
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
|
||||||
$"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins");
|
|
||||||
|
|
||||||
// In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
|
|
||||||
// When installing another local user addin, MD will remove files inside the folder
|
|
||||||
// So we browse all VSTUM addins, and return the one with an addin assembly
|
|
||||||
if (Directory.Exists(localAddins))
|
|
||||||
{
|
|
||||||
foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
|
|
||||||
{
|
|
||||||
if (File.Exists(IOPath.Combine(folder, addinAssembly)))
|
|
||||||
return folder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check in Visual Studio.app/
|
|
||||||
// In that case the name of the addin is used
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string[] GetAnalyzers(string path)
|
|
||||||
{
|
{
|
||||||
var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
|
var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
|
||||||
|
|
||||||
@@ -185,31 +60,6 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
|||||||
return Array.Empty<string>();
|
return Array.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] GetAnalyzers()
|
|
||||||
{
|
|
||||||
var vstuPath = GetExtensionPath();
|
|
||||||
if (string.IsNullOrEmpty(vstuPath))
|
|
||||||
return Array.Empty<string>();
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsOSX)
|
|
||||||
return GetAnalyzers(vstuPath);
|
|
||||||
|
|
||||||
if (VisualStudioEditor.IsWindows)
|
|
||||||
{
|
|
||||||
var analyzers = GetAnalyzers(vstuPath);
|
|
||||||
if (analyzers?.Length > 0)
|
|
||||||
return analyzers;
|
|
||||||
|
|
||||||
var bridge = GetWindowsBridgeFromRegistry();
|
|
||||||
if (File.Exists(bridge))
|
|
||||||
return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), ".."));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local assets
|
|
||||||
// return FileUtility.FindPackageAssetFullPath("Analyzers a:packages", ".Analyzers.dll");
|
|
||||||
return Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CodeEditor.Installation ToCodeEditorInstallation()
|
public CodeEditor.Installation ToCodeEditorInstallation()
|
||||||
{
|
{
|
||||||
return new CodeEditor.Installation() { Name = Name, Path = Path };
|
return new CodeEditor.Installation() { Name = Name, Path = Path };
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"Targets": "+",
|
"Targets": "+",
|
||||||
"Files":
|
"Files":
|
||||||
[
|
[
|
||||||
"Editor/Discovery.cs"
|
"Editor/VisualStudioForWindowsInstallation.cs"
|
||||||
],
|
],
|
||||||
"Patterns":
|
"Patterns":
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
"ErrorExceptions": [
|
"ErrorExceptions": [
|
||||||
{
|
{
|
||||||
"ValidationTest": "API Validation",
|
"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#",
|
"ExceptionMessage": "Breaking changes require a new major version.",
|
||||||
"PackageVersion": "2.0.11"
|
"PackageVersion": "2.0.18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidationTest": "API Validation",
|
||||||
|
"ExceptionMessage": "Additions require a new minor or major version.",
|
||||||
|
"PackageVersion": "2.0.18"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"WarningExceptions": []
|
"WarningExceptions": []
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -2,25 +2,25 @@
|
|||||||
"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.18",
|
"version": "2.0.20",
|
||||||
"unity": "2019.4",
|
"unity": "2019.4",
|
||||||
"unityRelease": "25f1",
|
"unityRelease": "25f1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.unity.test-framework": "1.1.9"
|
"com.unity.test-framework": "1.1.9"
|
||||||
},
|
},
|
||||||
"relatedPackages": {
|
"relatedPackages": {
|
||||||
"com.unity.ide.visualstudio.tests": "2.0.18"
|
"com.unity.ide.visualstudio.tests": "2.0.20"
|
||||||
},
|
},
|
||||||
"_upm": {
|
"_upm": {
|
||||||
"changelog": "Integration:\n\n- Performance improvements with `EditorApplication.update` callbacks.\n \nProject generation:\n\n- Add extra compiler options for analyzers and source generators."
|
"changelog": "Integration:\n\n- Internal API refactoring."
|
||||||
},
|
},
|
||||||
"upmCi": {
|
"upmCi": {
|
||||||
"footprint": "1d7ac8985c088423201e27b93ccdc6292ff941c9"
|
"footprint": "7d769a8558c7768417b16fc2ac8477cf69234049"
|
||||||
},
|
},
|
||||||
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html",
|
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html",
|
||||||
"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": "9d3c07127cbe1916b8abbfd18f71fb8d9df8008c"
|
"revision": "b7bf23d23806ac75645bfa12acadcfc11468a383"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user