Last active
May 18, 2017 05:27
-
-
Save andy-uq/436d9b16af4c683e6d8e0f0d3650e57e to your computer and use it in GitHub Desktop.
Convert VS2015 project to 2017
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference>NuGet.Core</NuGetReference> | |
<Namespace>NuGet</Namespace> | |
<Namespace>System.Runtime.Versioning</Namespace> | |
</Query> | |
const string folder = @"C:\Git\_InternalTools\Nephele"; | |
void Main() | |
{ | |
foreach (var filename in Directory.GetFiles(folder, "*.csproj", SearchOption.AllDirectories)) | |
{ | |
if (Path.GetFileNameWithoutExtension(filename).EndsWith("-2015")) | |
continue; | |
var backup = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + "-2015.csproj"); | |
if (File.Exists(backup) == false) | |
{ | |
File.Copy(filename, backup); | |
} | |
var csproj = XDocument.Load(filename); | |
var project = csproj.Root; | |
if (project.Name.LocalName != "Project") | |
{ | |
project = project.Element("Project"); | |
} | |
if (project == null) | |
continue; | |
var dirty = false; | |
dirty |= DeleteXmlNamespace(project); | |
dirty |= UpdateProjectElement(project); | |
dirty |= DeleteToolsReferences(project); | |
dirty |= DeleteChooseElements(project, filename); | |
dirty |= UpdateFrameworkVersion(project); | |
dirty |= DeleteSourceFileReferences(project); | |
dirty |= DeleteSettings(project, filename); | |
dirty |= DeleteOverrideSettings(project); | |
dirty |= UpdateProjectReferences(project, filename); | |
dirty |= UpdatePackages(project, filename); | |
dirty |= DeletePackagesConfigReference(project); | |
dirty |= DeleteDefaultAssemblyReferences(project); | |
dirty |= DeleteComments(project); | |
dirty |= DeleteEmptyItemGroups(project); | |
dirty |= DisableAssemblyInfoGeneration(project); | |
if (dirty) | |
{ | |
Console.WriteLine($"Updated {filename}"); | |
csproj.Save(filename); | |
//csproj.Dump(); | |
} | |
} | |
} | |
private bool DeleteXmlNamespace(XElement project) | |
{ | |
var xmlns = project.Attributes().SingleOrDefault(a => a.IsNamespaceDeclaration); | |
if (xmlns == null) | |
{ | |
return false; | |
} | |
foreach (var node in project.DescendantsAndSelf()) | |
{ | |
if (node.Name.Namespace != XNamespace.None) | |
{ | |
node.Name = XNamespace.None.GetName(node.Name.LocalName); | |
} | |
if (node.Attributes().Any(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None)) | |
{ | |
node.ReplaceAttributes(node.Attributes() | |
.Select(a => | |
a.IsNamespaceDeclaration | |
? null | |
: a.Name.Namespace != XNamespace.None | |
? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) | |
: a)); | |
} | |
} | |
return true; | |
} | |
private bool UpdateProjectElement(XElement project) | |
{ | |
const string key = "Sdk"; | |
const string value = "Microsoft.NET.Sdk"; | |
var attributes = project.Attributes().ToList(); | |
if (attributes.Count == 1 && attributes[0].Name.LocalName == key && attributes[0].Value == value) | |
{ | |
return false; | |
} | |
project.RemoveAttributes(); | |
project.SetAttributeValue(key, value); | |
return true; | |
} | |
private bool DeleteToolsReferences(XElement project) | |
{ | |
var keys = new[] | |
{ | |
@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props", | |
@"$(MSBuildToolsPath)\Microsoft.CSharp.targets", | |
@"$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" | |
}; | |
var dirty = false; | |
foreach (var key in keys) | |
{ | |
var node = project.XPathSelectElement("//Import[@Project=\"" + key + "\"]"); | |
if (node != null) | |
{ | |
node.Remove(); | |
dirty = true; | |
} | |
} | |
return dirty; | |
} | |
private bool DeleteChooseElements(XElement project, string filename) | |
{ | |
var numChooseElements = project.XPathSelectElements("//Choose")?.Count() ?? 0; | |
if (numChooseElements > 0) | |
{ | |
Console.WriteLine($"{filename} has {numChooseElements} Choose elements. These should be removed manually."); | |
} | |
return false; | |
} | |
private bool UpdateFrameworkVersion(XElement project) | |
{ | |
var dirty = false; | |
foreach (var version in project.XPathSelectElements("//TargetFrameworkVersion")) | |
{ | |
version.Name = "TargetFramework"; | |
version.Value = version.Value.Replace("v", "net").Replace(".", ""); | |
dirty = true; | |
} | |
return dirty; | |
} | |
private bool DeleteSourceFileReferences(XElement project) | |
{ | |
var dirty = false; | |
dirty |= DeleteSourceFileReferences(project, "Compile", "cs"); | |
dirty |= DeleteSourceFileReferences(project, "EmbeddedResource", "resx"); | |
return dirty; | |
} | |
private bool DeleteSourceFileReferences(XElement project, string elementName, string fileExtension) | |
{ | |
var dirty = false; | |
foreach (var node in project.XPathSelectElements("//" + elementName).ToList()) | |
{ | |
var filename = node.Attribute("Include").Value; | |
if (filename.StartsWith("..")) | |
{ | |
continue; | |
} | |
if (filename.EndsWith("." + fileExtension)) | |
{ | |
node.Remove(); | |
dirty = true; | |
} | |
} | |
return dirty; | |
} | |
private bool DeleteSettings(XElement project, string filename) | |
{ | |
var dirty = false; | |
var propertyGroup = project.Elements("PropertyGroup").Where(_ => _.HasAttributes == false).ToList(); | |
var defaultFromFilename = Path.GetFileNameWithoutExtension(filename); | |
dirty |= DeleteDefaultSetting(propertyGroup, "RootNamespace", defaultFromFilename); | |
dirty |= DeleteDefaultSetting(propertyGroup, "AssemblyName", defaultFromFilename); | |
dirty |= DeleteDefaultSetting(propertyGroup, "TreatWarningsAsErrors", "false"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "WarningsAsErrors", ""); | |
dirty |= DeleteDefaultSetting(propertyGroup, "Optimize", "false"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "DocumentationFile", ""); | |
dirty |= DeleteDefaultSetting(propertyGroup, "OutputType", "Library"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "AppDesignerFolder", "Properties"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "FileAlignment", "512"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "OutputPath", @"bin\Debug"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "OutputPath", @"bin\Release"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "DefineConstants", "DEBUG;TRACE"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "ErrorReport", "prompt"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "WarningLevel", "4"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "TestProjectType", "UnitTest"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "IsCodedUITest", "False"); | |
dirty |= DeleteDefaultSetting(propertyGroup, "StartupObject", ""); | |
dirty |= DeleteSetting(propertyGroup, "Configuration"); | |
dirty |= DeleteSetting(propertyGroup, "Platform"); | |
dirty |= DeleteSetting(propertyGroup, "ProjectGuid"); | |
dirty |= DeleteSetting(propertyGroup, "ProjectTypeGuids"); | |
dirty |= DeleteSetting(propertyGroup, "DebugSymbols"); | |
dirty |= DeleteSetting(propertyGroup, "DebugType"); | |
dirty |= DeleteSetting(propertyGroup, "NuGetPackageImportStamp"); | |
dirty |= DeleteSetting(propertyGroup, "TargetFrameworkProfile"); | |
dirty |= DeleteSetting(propertyGroup, "VSToolsPath"); | |
dirty |= DeleteSetting(propertyGroup, "VisualStudioVersion"); | |
return dirty; | |
} | |
private bool DeleteOverrideSettings(XElement project) | |
{ | |
var dirty = false; | |
var propertyGroups = project.Elements("PropertyGroup").Where(_ => _.Attributes().Any(a => a.Name == "Condition")).ToList(); | |
foreach (var propertyGroup in propertyGroups) | |
{ | |
propertyGroup.Remove(); | |
dirty = true; | |
} | |
return dirty; | |
} | |
private bool DeleteDefaultSetting(List<XElement> propertyGroups, string elementName, string defaultValue) | |
{ | |
var dirty = false; | |
foreach (var propertyGroup in propertyGroups) | |
{ | |
dirty |= DeleteDefaultSetting(propertyGroup, elementName, defaultValue); | |
} | |
return true; | |
} | |
private bool DeleteDefaultSetting(XElement propertyGroup, string elementName, string defaultValue) | |
{ | |
var node = propertyGroup.Element(elementName); | |
if (node == null || node.Value.Trim() != defaultValue) | |
{ | |
return false; | |
} | |
node.Remove(); | |
return true; | |
} | |
private bool DeleteSetting(List<XElement> propertyGroups, string elementName) | |
{ | |
var dirty = false; | |
foreach (var propertyGroup in propertyGroups) | |
{ | |
dirty |= DeleteSetting(propertyGroup, elementName); | |
} | |
return true; | |
} | |
private bool DeleteSetting(XElement propertyGroup, string elementName) | |
{ | |
var node = propertyGroup.Element(elementName); | |
if (node == null) | |
{ | |
return false; | |
} | |
node.Remove(); | |
return true; | |
} | |
private bool UpdateProjectReferences(XElement project, string filename) | |
{ | |
var dirty = false; | |
var subprojects = new HashSet<string>(); | |
foreach (var projectReference in project.XPathSelectElements("//ProjectReference")) | |
{ | |
var relativePath = projectReference.Attribute("Include").Value; | |
var refFilename = Path.Combine(Path.GetDirectoryName(filename), relativePath); | |
var csproj = XDocument.Load(refFilename); | |
foreach (var subproject in csproj.XPathSelectElements("//ProjectReference")) | |
{ | |
var subRelativePath = subproject.Attribute("Include").Value; | |
var subRefFilename = Path.Combine(Path.GetDirectoryName(refFilename), subRelativePath); | |
subprojects.Add(subRefFilename); | |
} | |
} | |
foreach (var projectReference in project.XPathSelectElements("//ProjectReference").ToList()) | |
{ | |
var relativePath = projectReference.Attribute("Include").Value; | |
var refFilename = Path.Combine(Path.GetDirectoryName(filename), relativePath); | |
if (subprojects.Contains(refFilename)) | |
{ | |
projectReference.Remove(); | |
dirty = true; | |
continue; | |
} | |
var projectChild = projectReference.Element("Project"); | |
if (projectChild != null) | |
{ | |
projectChild.Remove(); | |
dirty = true; | |
} | |
var nameChild = projectReference.Element("Name"); | |
if (nameChild != null && nameChild.Value == Path.GetFileNameWithoutExtension(refFilename)) | |
{ | |
nameChild.Remove(); | |
dirty = true; | |
} | |
} | |
return dirty; | |
} | |
private bool UpdatePackages(XElement project, string filename) | |
{ | |
var dirty = false; | |
var added = EnsurePackageReferenceElements(project, filename); | |
dirty |= DeletePackageDllReferences(project); | |
var deleted = DeleteChildPackageReferences(project, filename); | |
added.RemoveAll(r => deleted.Any(_ => _.Name == r.Name && _.Version == r.Version)); | |
dirty |= (added.Count > 0); | |
return dirty; | |
} | |
private List<Package> EnsurePackageReferenceElements(XElement project, string filename) | |
{ | |
var xPackages = GetPackagesItemGroup(project); | |
var added = new List<Package>(); | |
var packages = | |
from xPackage in XDocument.Load(Path.Combine(Path.GetDirectoryName(filename), "packages.config")).XPathSelectElements("//package") | |
select new Package | |
{ | |
Name = (string)xPackage.Attribute("id"), | |
Version = (string)xPackage.Attribute("version"), | |
DevelopmentDependency = ((string)xPackage.Attribute("developmentDependency") == "true") | |
}; | |
foreach (var package in packages) | |
{ | |
if (xPackages.XPathSelectElement($"PackageReference[@Include='{package.Name}']") != null) | |
continue; | |
xPackages.Add(new XElement("PackageReference", new XAttribute("Include", package.Name), new XAttribute("Version", package.Version), | |
package.DevelopmentDependency ? new XElement("PrivateAssets", "all") : null) | |
); | |
added.Add(package); | |
} | |
return added; | |
} | |
private bool DeletePackageDllReferences(XElement project) | |
{ | |
var xPackages = GetPackagesItemGroup(project); | |
var dirty = false; | |
foreach (var xHintPath in project.XPathSelectElements("//Reference/HintPath").ToList()) | |
{ | |
var hintPath = @"\" + xHintPath.Value; | |
var packagesPos = hintPath.IndexOf(@"\packages\"); | |
if (packagesPos == -1) | |
{ | |
continue; | |
} | |
var packageStart = packagesPos + 10; | |
var packageEnd = hintPath.IndexOf(@"\", packageStart); | |
if (packageEnd < packageStart) | |
{ | |
continue; | |
} | |
var packageFolder = hintPath.Substring(packageStart, packageEnd - packageStart); | |
var packageFolderParts = packageFolder.Split('.'); | |
var versionLength = 0; | |
for (var i = 0; i < packageFolderParts.Length; i++) | |
{ | |
if (string.IsNullOrEmpty(packageFolderParts[i])) | |
{ | |
continue; | |
} | |
if (char.IsNumber(packageFolderParts[i][0])) | |
{ | |
versionLength = packageFolderParts.Length - i; | |
break; | |
} | |
} | |
if (versionLength == 0 || versionLength == packageFolderParts.Length) | |
{ | |
continue; | |
} | |
var packageName = string.Join(".", packageFolderParts.Take(packageFolderParts.Length - versionLength)); | |
var packageVersion = string.Join(".", packageFolderParts.Skip(packageFolderParts.Length - versionLength)); | |
if (project.XPathSelectElement("//PackageReference[@Include=\"" + packageName + "\" and @Version=\"" + packageVersion + "\"]") == null) | |
{ | |
var packageReference = new XElement("PackageReference", new XAttribute("Include", packageName), new XAttribute("Version", packageVersion)); | |
xPackages.Add(packageReference); | |
} | |
xHintPath.Parent.Remove(); | |
dirty = true; | |
} | |
return dirty; | |
} | |
private XElement GetPackagesItemGroup(XElement project) | |
{ | |
var xPackages = project.XPathSelectElement("//PackageReference")?.Parent; | |
if (xPackages == null) | |
{ | |
xPackages = new XElement("ItemGroup"); | |
project.Add(xPackages); | |
} | |
return xPackages; | |
} | |
private List<Package> DeleteChildPackageReferences(XElement project, string filename) | |
{ | |
var folder = Path.GetDirectoryName(filename); | |
string packagesFolder; | |
do | |
{ | |
packagesFolder = Path.Combine(folder, "packages"); | |
folder = Path.Combine(folder, ".."); | |
} | |
while (Directory.Exists(packagesFolder) == false); | |
var frameworkVersion = GetFrameworkVersion(project); | |
var repository = new LocalPackageRepository(packagesFolder); | |
var subPackages = new HashSet<Package>(); | |
var removedPackages = new List<Package>(); | |
foreach (var packageReference in project.XPathSelectElements("//PackageReference")) | |
{ | |
var package = new Package | |
{ | |
Name = packageReference.Attribute("Include").Value, | |
Version = packageReference.Attribute("Version").Value | |
}; | |
subPackages.AddRange( | |
repository | |
.FindPackage(package.Name, SemanticVersion.Parse(package.Version)) | |
.GetCompatiblePackageDependencies(new FrameworkName(".NETFramework", frameworkVersion)) | |
.Select(_ => repository.ResolveDependency(_, true, true)) | |
.Select(_ => new Package | |
{ | |
Name = _?.Id, | |
Version = _?.Version.ToString() | |
}) | |
); | |
} | |
foreach (var packageReference in project.XPathSelectElements("//PackageReference").ToList()) | |
{ | |
var package = new Package | |
{ | |
Name = packageReference.Attribute("Include").Value, | |
Version = packageReference.Attribute("Version").Value | |
}; | |
if (subPackages.Contains(package)) | |
{ | |
packageReference.Remove(); | |
removedPackages.Add(package); | |
continue; | |
} | |
} | |
return removedPackages; | |
} | |
private Version GetFrameworkVersion(XElement project) | |
{ | |
var netVersion = project.XPathSelectElement("PropertyGroup/TargetFramework").Value; | |
var sb = new StringBuilder(); | |
foreach(var c in netVersion) | |
{ | |
if (char.IsDigit(c)) | |
{ | |
if (sb.Length > 0) | |
{ | |
sb.Append('.'); | |
} | |
sb.Append(c); | |
} | |
} | |
return new Version(sb.ToString()); | |
} | |
private bool DeletePackagesConfigReference(XElement project) | |
{ | |
var pc = project.XPathSelectElement("//None[@Include=\"packages.config\"]"); | |
if (pc == null) | |
{ | |
return false; | |
} | |
pc.Remove(); | |
return true; | |
} | |
private bool DeleteComments(XElement project) | |
{ | |
var comments = project.DescendantNodes().Where(_ => _.NodeType == XmlNodeType.Comment).ToList(); | |
if (comments.Count == 0) | |
{ | |
return false; | |
} | |
foreach (var comment in comments) | |
{ | |
comment.Remove(); | |
} | |
return true; | |
} | |
private bool DeleteDefaultAssemblyReferences(XElement project) | |
{ | |
var defaultAssemblies = new[] | |
{ | |
"System", | |
"System.Core", | |
"System.Data", | |
"System.Drawing", | |
"System.IO.Compression.FileSystem", | |
"System.Numerics", | |
"System.Runtime.Serialization", | |
"System.Xml", | |
"System.Xml.Linq" | |
}; | |
var dirty = false; | |
foreach (var assembly in defaultAssemblies) | |
{ | |
var node = project.XPathSelectElement("//Reference[@Include=\"" + assembly + "\"]"); | |
if (node != null) | |
{ | |
node.Remove(); | |
dirty = true; | |
} | |
} | |
return dirty; | |
} | |
private bool DeleteEmptyItemGroups(XElement project) | |
{ | |
return DeleteEmptyGroups(project, "ItemGroup"); | |
} | |
private bool DeleteEmptyPropertyGroups(XElement project) | |
{ | |
return DeleteEmptyGroups(project, "PropertyGroup"); | |
} | |
private bool DeleteEmptyGroups(XElement project, string elementName) | |
{ | |
var dirty = false; | |
foreach (var node in project.Elements(elementName).ToList()) | |
{ | |
if (node.HasElements == false) | |
{ | |
node.Remove(); | |
dirty = true; | |
} | |
} | |
return dirty; | |
} | |
private bool DisableAssemblyInfoGeneration(XElement project) | |
{ | |
var propertyGroup = project.Element("PropertyGroup"); | |
if (propertyGroup == null) | |
{ | |
propertyGroup = new XElement("PropertyGroup"); | |
project.Add(propertyGroup); | |
} | |
var generateAssemblyInfo = propertyGroup.Element("GenerateAssemblyInfo"); | |
if (generateAssemblyInfo == null) | |
{ | |
generateAssemblyInfo = new XElement("GenerateAssemblyInfo", "false"); | |
propertyGroup.Add(generateAssemblyInfo); | |
return true; | |
} | |
return false; | |
} | |
private class Package | |
{ | |
public string Name { get; set; } | |
public string Version { get; set; } | |
public bool DevelopmentDependency { get; set;} | |
public override bool Equals(object obj) | |
{ | |
var other = obj as Package; | |
if (other == null) | |
{ | |
return false; | |
} | |
return other.Name == Name && other.Version == Version; | |
} | |
public override int GetHashCode() | |
{ | |
return (Name + Version).GetHashCode(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment