Skip to content

Instantly share code, notes, and snippets.

@chilversc
Created October 25, 2016 15:26
Show Gist options
  • Save chilversc/8aa317ca2f2754e64c5779be32a13ee0 to your computer and use it in GitHub Desktop.
Save chilversc/8aa317ca2f2754e64c5779be32a13ee0 to your computer and use it in GitHub Desktop.
Sort msbuild project items
<Query Kind="Program" />
static XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
static XName ItemGroup = ns + "ItemGroup";
static XName Reference = ns + "Reference";
static XName ProjectReference = ns + "ProjectReference";
static XName Condition = "Condition";
static XName Include = "Include";
static XName OutputType = ns + "OutputType";
static XName Folder = ns + "Folder";
static XName RefactorLog = ns + "RefactorLog";
static XName ArtifactReference = ns + "ArtifactReference";
static XName PostDeploy = ns + "PostDeploy";
static XName SqlCmdVariable = ns + "SqlCmdVariable";
void Main(string[] args)
{
if (args.Length == 0) {
Console.WriteLine ("Syntax: lprun SortProject.linq Core\\Core.csproj (...)");
} else {
foreach (var file in args) {
if (File.Exists (file)) {
SortProject (file);
} else {
Console.Error.WriteLine ("File not found: {0}", file);
}
}
}
}
private void SortProject (string file)
{
XDocument doc = XDocument.Load (file);
var root = doc.Root;
var groups = root
.Elements (ItemGroup)
.Where (group => !group.HasAttributes)
.ToList ();
foreach (var g in groups) {
g.Remove ();
}
var outputType = root.Descendants (OutputType).Select (x => x.Value).FirstOrDefault ();
var isDatabase = "Database".Equals(outputType, StringComparison.OrdinalIgnoreCase);
var items = GroupItems (groups.SelectMany (g => g.Elements ()));
if (isDatabase) {
root.Add (GroupItems (items.Elements (ArtifactReference)));
root.Add (GroupItems (items.Elements (SqlCmdVariable), ItemComparer.FullName));
root.Add (GroupItems (items.Elements (Folder), ItemComparer.FullName));
} else {
root.Add (GroupItems (items.Elements (Reference)));
root.Add (GroupItems (items.Elements (ProjectReference)));
}
root.Add (GroupItems (items.Elements ()));
doc.Save (file);
}
private IEnumerable<XElement> Find (XElement container, params XName[] names)
{
return names.SelectMany (n => container.Elements (n));
}
private XElement GroupItems (IEnumerable<XElement> elements)
{
return GroupItems (elements, ItemComparer.Default);
}
private XElement GroupItems (IEnumerable<XElement> elements, IComparer<XElement> comparer)
{
var container = new XElement (ItemGroup);
MoveElements (container, elements, comparer);
return container;
}
private void MoveElements (XElement container, IEnumerable<XElement> elements, IComparer<XElement> comparer)
{
foreach (var element in elements.OrderBy (e => e, comparer).ToList ()) {
element.Remove ();
container.Add (element);
}
}
class ItemComparer : IComparer<XElement>
{
private readonly Comparison<string> comparison;
private ItemComparer (Comparison<string> comparison)
{
this.comparison = comparison;
}
public int Compare (XElement e1, XElement e2)
{
var a1 = e1.Attribute (Include);
var a2 = e2.Attribute (Include);
if (a1 == null && a2 == null) return 0;
if (a1 == null) return 1;
if (a2 == null) return -1;
var v1 = a1.Value;
var v2 = a2.Value;
return comparison (v1, v2);
}
public static readonly ItemComparer Default = new ItemComparer (
delegate (string v1, string v2) {
try {
var d1 = Path.GetDirectoryName (v1);
var d2 = Path.GetDirectoryName (v2);
var result = string.Compare (d1, d2, StringComparison.OrdinalIgnoreCase);
if (result != 0) return result;
var f1 = Path.GetFileName (v1);
var f2 = Path.GetFileName (v2);
result = string.Compare (f1, f2, StringComparison.OrdinalIgnoreCase);
return result;
} catch {
return string.Compare (v1, v2, StringComparison.OrdinalIgnoreCase);
}
});
public static readonly ItemComparer FullName = new ItemComparer (
delegate (string v1, string v2) {
return string.Compare (v1, v2, StringComparison.OrdinalIgnoreCase);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment