Skip to content

Instantly share code, notes, and snippets.

@andy-uq
Last active May 18, 2017 05:27
Show Gist options
  • Save andy-uq/436d9b16af4c683e6d8e0f0d3650e57e to your computer and use it in GitHub Desktop.
Save andy-uq/436d9b16af4c683e6d8e0f0d3650e57e to your computer and use it in GitHub Desktop.
Convert VS2015 project to 2017
<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