mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
352 lines
14 KiB
C#
352 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using Microsoft.VisualStudio;
|
|
using Microsoft.VisualStudio.Shell;
|
|
using Microsoft.VisualStudio.Shell.Interop;
|
|
|
|
namespace Cosmos.VS.ProjectSystem
|
|
{
|
|
internal class MigrateCosmosProjectFactory : IVsProjectFactory, IVsProjectUpgradeViaFactory, IVsProjectUpgradeViaFactory4
|
|
{
|
|
private const string PropertyGroup = nameof(PropertyGroup);
|
|
private const string ProjectGuid = nameof(ProjectGuid);
|
|
private const string TargetFramework = nameof(TargetFramework);
|
|
|
|
private const string ItemGroup = nameof(ItemGroup);
|
|
private const string Include = nameof(Include);
|
|
private const string Remove = nameof(Remove);
|
|
private const string None = nameof(None);
|
|
private const string PackageReference = nameof(PackageReference);
|
|
private const string ProjectReference = nameof(ProjectReference);
|
|
|
|
private static readonly List<string> CommonProjectProperties = new List<string>()
|
|
{
|
|
"TargetFramework",
|
|
"OutputType",
|
|
"AppDesignerFolder",
|
|
"ProjectGuid",
|
|
"RootNamespace",
|
|
"AssemblyName"
|
|
};
|
|
|
|
private static readonly List<string> PlugsProjects = new List<string>()
|
|
{
|
|
"Cosmos.Core.Plugs.Asm",
|
|
"Cosmos.Core.Plugs",
|
|
"Cosmos.Core_Asm",
|
|
"Cosmos.Core_Plugs",
|
|
"Cosmos.Debug.Kernel.Plugs.Asm",
|
|
"Cosmos.System.Plugs",
|
|
"Cosmos.System_Plugs",
|
|
"Cosmos.System2.Plugs",
|
|
"Cosmos.System2_Plugs"
|
|
};
|
|
|
|
#region IVsProjectFactory
|
|
|
|
public int CanCreateProject(string pszFilename, uint grfCreateFlags, out int pfCanCreate)
|
|
{
|
|
pfCanCreate = !String.IsNullOrEmpty(pszFilename) && !PackageUtilities.IsFileNameInvalid(pszFilename) ? 1 : 0;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
public int CreateProject(string pszFilename, string pszLocation, string pszName, uint grfCreateFlags,
|
|
ref Guid iidProject, out IntPtr ppvProject, out int pfCanceled)
|
|
{
|
|
ppvProject = IntPtr.Zero;
|
|
pfCanceled = 0;
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) => VSConstants.S_OK;
|
|
|
|
public int Close() => VSConstants.S_OK;
|
|
|
|
#endregion
|
|
|
|
#region IVsProjectUpgradeViaFactory
|
|
|
|
public int UpgradeProject(string bstrFileName, uint fUpgradeFlag, string bstrCopyLocation,
|
|
out string pbstrUpgradedFullyQualifiedFileName, IVsUpgradeLogger pLogger, out int pUpgradeRequired,
|
|
out Guid pguidNewProjectFactory)
|
|
{
|
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
|
|
var projectDir = new DirectoryInfo(Path.GetDirectoryName(bstrFileName));
|
|
var projectName = Path.GetFileNameWithoutExtension(bstrFileName);
|
|
|
|
string codeProjectPath;
|
|
|
|
UpgradeProject_CheckOnly(bstrFileName, pLogger, out pUpgradeRequired, out pguidNewProjectFactory, out var flags);
|
|
|
|
using (var projectStream = File.OpenRead(bstrFileName))
|
|
{
|
|
var document = XDocument.Load(projectStream);
|
|
var itemGroups = document.Root.Descendants().Where(e => XNameEqualsString(e.Name, ItemGroup));
|
|
var projectReferences = itemGroups.Descendants().Where(e => XNameEqualsString(e.Name, ProjectReference));
|
|
|
|
if (!projectReferences.Any())
|
|
{
|
|
pLogger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, bstrFileName, "Invalid project!");
|
|
|
|
pbstrUpgradedFullyQualifiedFileName = bstrFileName;
|
|
return VSConstants.VS_E_PROJECTMIGRATIONFAILED;
|
|
}
|
|
else if (projectReferences.Count() == 1)
|
|
{
|
|
var codeProject = projectReferences.Single().Attributes().Where(
|
|
a => XNameEqualsString(a.Name, Include)).Single().Value;
|
|
codeProjectPath = Path.IsPathRooted(codeProject) ? codeProject
|
|
: Path.GetFullPath(Path.Combine(projectDir.FullName, codeProject));
|
|
}
|
|
else
|
|
{
|
|
var projectReferencesWithoutPlugsProjects = projectReferences.Where(
|
|
p => !PlugsProjects.Contains(
|
|
Path.GetFileNameWithoutExtension(
|
|
p.Attributes().Where(
|
|
a => XNameEqualsString(a.Name, Include)).Single().Value), StringComparer.OrdinalIgnoreCase));
|
|
|
|
if (projectReferencesWithoutPlugsProjects.Count() > 1)
|
|
{
|
|
pLogger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_WARNING, projectName, bstrFileName,
|
|
"Selecting project reference based on Cosmos project name!");
|
|
|
|
var codeProjectName = projectName.EndsWith("Boot") ? projectName.TrimSuffix("Boot") : projectName;
|
|
var codeProjectPathWithoutExtension = Path.Combine(projectDir.FullName, codeProjectName);
|
|
|
|
var possibleCodeProjects = projectReferences.Where(
|
|
p => StringEquals(
|
|
Path.GetFileNameWithoutExtension(
|
|
p.Attributes().Where(
|
|
a => XNameEqualsString(a.Name, Include)).Single().Value), codeProjectName));
|
|
|
|
if (possibleCodeProjects.Count() != 1)
|
|
{
|
|
pLogger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, bstrFileName,
|
|
"Cannot find the code project!");
|
|
|
|
pbstrUpgradedFullyQualifiedFileName = bstrFileName;
|
|
return VSConstants.VS_E_PROJECTMIGRATIONFAILED;
|
|
}
|
|
|
|
var codeProject =
|
|
possibleCodeProjects.Single().Attributes().Where(a => XNameEqualsString(a.Name, Include)).Single().Value;
|
|
codeProjectPath = Path.IsPathRooted(codeProject) ? codeProject
|
|
: Path.GetFullPath(Path.Combine(projectDir.FullName, codeProject));
|
|
}
|
|
else
|
|
{
|
|
var codeProject = projectReferencesWithoutPlugsProjects
|
|
.Single().Attributes().Where(a => XNameEqualsString(a.Name, Include)).Single().Value;
|
|
codeProjectPath = Path.IsPathRooted(codeProject) ? codeProject
|
|
: Path.GetFullPath(Path.Combine(projectDir.FullName, codeProject));
|
|
}
|
|
}
|
|
}
|
|
|
|
File.Copy(bstrFileName, Path.Combine(bstrCopyLocation, Path.GetFileName(bstrFileName)));
|
|
File.Copy(codeProjectPath, Path.Combine(bstrCopyLocation, Path.GetFileName(codeProjectPath)));
|
|
|
|
MigrateProject(bstrFileName, codeProjectPath);
|
|
|
|
pbstrUpgradedFullyQualifiedFileName = null;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
public int UpgradeProject_CheckOnly(string bstrFileName, IVsUpgradeLogger pLogger, out int pUpgradeRequired,
|
|
out Guid pguidNewProjectFactory, out uint pUpgradeProjectCapabilityFlags)
|
|
{
|
|
var isCosmos = String.Equals(Path.GetExtension(bstrFileName), ".Cosmos", StringComparison.OrdinalIgnoreCase);
|
|
|
|
pUpgradeRequired = isCosmos ? (int)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_ONEWAYUPGRADE
|
|
: (int)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_NOREPAIR;
|
|
|
|
if (isCosmos)
|
|
{
|
|
pguidNewProjectFactory = Guid.Empty;
|
|
pUpgradeProjectCapabilityFlags = (uint)(__VSPPROJECTUPGRADEVIAFACTORYFLAGS.PUVFF_BACKUPSUPPORTED | __VSPPROJECTUPGRADEVIAFACTORYFLAGS.PUVFF_COPYBACKUP);
|
|
}
|
|
else
|
|
{
|
|
pguidNewProjectFactory = GetType().GUID;
|
|
pUpgradeProjectCapabilityFlags = 0;
|
|
}
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
public int GetSccInfo(string bstrProjectFileName, out string pbstrSccProjectName, out string pbstrSccAuxPath,
|
|
out string pbstrSccLocalPath, out string pbstrProvider) => throw new NotImplementedException();
|
|
|
|
#endregion
|
|
|
|
#region IVsProjectUpgradeViaFactory4
|
|
|
|
public void UpgradeProject_CheckOnly(string pszFileName, IVsUpgradeLogger pLogger, out uint pUpgradeRequired,
|
|
out Guid pguidNewProjectFactory, out uint pUpgradeProjectCapabilityFlags)
|
|
{
|
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
|
|
UpgradeProject_CheckOnly(pszFileName, pLogger, out int upgradeRequired, out pguidNewProjectFactory,
|
|
out pUpgradeProjectCapabilityFlags);
|
|
|
|
pUpgradeRequired = (uint)upgradeRequired;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void MigrateProject(string cosmosProject, string codeProject)
|
|
{
|
|
var cosmosProjectProperties = XDocument.Parse(File.ReadAllText(cosmosProject)).Root.Descendants()
|
|
.Where(d => XNameEqualsString(d.Name, PropertyGroup)).Select(p => p.Descendants()
|
|
.Where(e => !CommonProjectProperties.Contains(e.Name.LocalName, StringComparer.OrdinalIgnoreCase)));
|
|
|
|
XDocument document;
|
|
|
|
using (var codeProjectStream = File.OpenRead(codeProject))
|
|
{
|
|
document = XDocument.Load(codeProjectStream);
|
|
}
|
|
|
|
var root = document.Root;
|
|
|
|
root.Descendants().Where(
|
|
e => XNameEqualsString(e.Name, PropertyGroup)).Last().AddAfterSelf(
|
|
new XElement(PropertyGroup, cosmosProjectProperties));
|
|
|
|
var packageReferencesItemGroups = root.Descendants().Where(
|
|
e => XNameEqualsString(e.Name, ItemGroup) && e.Descendants().Where(
|
|
i => XNameEqualsString(i.Name, PackageReference)).Any());
|
|
|
|
if (packageReferencesItemGroups.Descendants().Any(
|
|
i => XNameEqualsString(i.Name, PackageReference) && i.Attributes().Any(
|
|
a => XNameEqualsString(a.Name, Include) && XNameEqualsString(a.Value, "Cosmos.Build"))))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var properties = root.Descendants().Where(
|
|
e => XNameEqualsString(e.Name, PropertyGroup)).Descendants();
|
|
|
|
foreach(var targetFrameworkProperty in properties.Where(
|
|
p => XNameEqualsString(p.Name, TargetFramework)))
|
|
{
|
|
targetFrameworkProperty.Value = "netcoreapp2.0";
|
|
}
|
|
|
|
properties.Where(p => XNameEqualsString(p.Name, ProjectGuid)).Remove();
|
|
|
|
var itemGroups = root.Descendants().Where(
|
|
e => XNameEqualsString(e.Name, ItemGroup));
|
|
|
|
itemGroups.Where(e =>
|
|
{
|
|
var items = e.Descendants();
|
|
|
|
if (items.Count() != 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var singleItem = items.Single();
|
|
|
|
if (!XNameEqualsString(singleItem.Name, None))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var removeAttribute = singleItem.Attributes().Where(a => XNameEqualsString(a.Name, Remove)).FirstOrDefault();
|
|
|
|
if (removeAttribute == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (removeAttribute.Value.IndexOf(Path.GetFileName(cosmosProject), StringComparison.OrdinalIgnoreCase) != -1)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}).Remove();
|
|
|
|
var count = packageReferencesItemGroups.Count();
|
|
XElement itemGroup;
|
|
|
|
if (count == 0)
|
|
{
|
|
itemGroup = new XElement(ItemGroup);
|
|
root.Add(itemGroup);
|
|
}
|
|
else if (count == 1)
|
|
{
|
|
itemGroup = packageReferencesItemGroups.Single();
|
|
}
|
|
else
|
|
{
|
|
var packageReferencesOnlyItemGroups = packageReferencesItemGroups.Where(
|
|
e => e.Descendants().All(i => XNameEqualsString(i.Name, PackageReference)));
|
|
|
|
count = packageReferencesOnlyItemGroups.Count();
|
|
|
|
if (count == 1)
|
|
{
|
|
itemGroup = packageReferencesOnlyItemGroups.Single();
|
|
}
|
|
else
|
|
{
|
|
if (count == 0)
|
|
{
|
|
itemGroup = packageReferencesItemGroups.First();
|
|
}
|
|
else
|
|
{
|
|
var cosmosPackageReferencesItemGroups = packageReferencesOnlyItemGroups.Where(
|
|
e => e.Descendants().Any(
|
|
p => p.Attributes().First(
|
|
a => XNameEqualsString(a.Name, Include)).Value.Contains("Cosmos")));
|
|
|
|
if (cosmosPackageReferencesItemGroups.Any())
|
|
{
|
|
itemGroup = cosmosPackageReferencesItemGroups.First();
|
|
}
|
|
else
|
|
{
|
|
itemGroup = packageReferencesItemGroups.First();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var cosmosBuildPackageReference = new XElement("PackageReference");
|
|
cosmosBuildPackageReference.Add(new XAttribute(Include, "Cosmos.Build"));
|
|
cosmosBuildPackageReference.Add(new XAttribute("Version", "*"));
|
|
cosmosBuildPackageReference.Add(new XAttribute("NoWarn", "NU1604"));
|
|
|
|
itemGroup.AddFirst(cosmosBuildPackageReference);
|
|
|
|
using (var xmlWriter = XmlWriter.Create(
|
|
codeProject,
|
|
new XmlWriterSettings()
|
|
{
|
|
Indent = true,
|
|
OmitXmlDeclaration = true
|
|
}))
|
|
{
|
|
document.Save(xmlWriter);
|
|
}
|
|
|
|
File.Delete(cosmosProject);
|
|
}
|
|
|
|
private static bool XNameEqualsString(XName xName, string name) => StringEquals(xName.LocalName, name);
|
|
|
|
private static bool StringEquals(string a, string b) => String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|