diff --git a/tools/NetCoreCsProjSync/NetCoreCsProjSync/NetCoreCsProjGenerator.cs b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NetCoreCsProjGenerator.cs index db3580d57a13..b74fe59676ad 100644 --- a/tools/NetCoreCsProjSync/NetCoreCsProjSync/NetCoreCsProjGenerator.cs +++ b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NetCoreCsProjGenerator.cs @@ -32,16 +32,38 @@ internal static class NetCoreCsProjGenerator private const string NetCoreFilter = @"*" + NetCoreCsProjExtension; private const string CommandsFilter = @"Commands.*"; - private const string TestFolderDenoter = @".Test"; - private const string TestFolderDenoter2 = @".UnitTest"; + private static readonly string[] TestFolderDenoters = {@".Test", @".UnitTest", @".Tests"}; + + private static bool IsTestFolder(string path) => TestFolderDenoters.Any(path.EndsWith); + + private static bool HasCsProj(string path, bool ignoreExisting = false) => + Directory.EnumerateFiles(path, CsProjFilter, SearchOption.TopDirectoryOnly).Any() && + (ignoreExisting || !Directory.EnumerateFiles(path, NetCoreFilter, SearchOption.TopDirectoryOnly).Any()); // https://stackoverflow.com/a/25245678/294804 public static IEnumerable GetProjectFolderPaths(string rmPath, bool ignoreExisting = false) => Directory.EnumerateDirectories(rmPath).SelectMany(md => Directory.EnumerateDirectories(md, CommandsFilter).Where(pd => - !pd.EndsWith(TestFolderDenoter) && !pd.EndsWith(TestFolderDenoter2) && - Directory.EnumerateFiles(pd, CsProjFilter, SearchOption.TopDirectoryOnly).Any() && - (ignoreExisting || !Directory.EnumerateFiles(pd, NetCoreFilter, SearchOption.TopDirectoryOnly).Any()))); + !IsTestFolder(pd) && HasCsProj(pd, ignoreExisting))); + + private static readonly List<(string[] FolderNames, Func> GetTestDirs)?> TestFolderMapper = + new List<(string[] FolderNames, Func> GetTestDirs)?> + { + (new []{"Common", "Storage"}, + dir => Directory.EnumerateDirectories(dir).Where(IsTestFolder)), + (new []{"ResourceManager", "StackAdmin"}, + dir => Directory.EnumerateDirectories(dir).SelectMany(md => Directory.EnumerateDirectories(md).Where(IsTestFolder))) + }; + + //https://stackoverflow.com/a/5229311/294804 + public static IEnumerable GetTestProjectFolderPaths(string srcPath, bool ignoreExisting = false) => + Directory.EnumerateDirectories(srcPath) + .SelectMany(dir => TestFolderMapper.FirstOrDefault(m => m?.FolderNames?.Contains(new DirectoryInfo(dir).Name) ?? false)?.GetTestDirs(dir) ?? Enumerable.Empty()) + .Where(pd => HasCsProj(pd, ignoreExisting)); + + public static IEnumerable GetTestDesktopFilePaths(IEnumerable testProjectPaths) => + testProjectPaths.Select(pd => Directory.EnumerateFiles(pd).Where(f => TestFolderDenoters.Any(tfd => f.EndsWith(tfd + CsProjExtension))) + .First(f => !f.Contains(NetCoreCsProjExtension))); public static IEnumerable GetDesktopFilePaths(IEnumerable projectPaths) => projectPaths.Select(pd => Directory.EnumerateFiles(pd, CsProjFilter, SearchOption.TopDirectoryOnly).First(f => !f.Contains(NetCoreCsProjExtension))); @@ -119,7 +141,11 @@ private static string CreateVersionFromHintPath(string hintPath) var slashParts = hintPath.Split('\\'); if (slashParts[0] != "..") return null; - var parts = slashParts[4].Split('.'); + var slashPartIndex = slashParts.Select((sp, i) => ((int Index, string SlashPart)?)(Index: i, SlashPart: sp)) + .FirstOrDefault(t => t?.SlashPart?.ToLower()?.Contains("packages") ?? false)?.Index; + if (slashPartIndex == null) return null; + + var parts = slashParts[slashPartIndex.Value + 1].Split('.'); //https://stackoverflow.com/a/18251942/294804 var firstDigitIndex = parts.Select((p, i) => (Index: i, Part: p)).First(a => a.Part.All(Char.IsDigit)).Index; //https://stackoverflow.com/a/14435083/294804 @@ -135,11 +161,11 @@ public static string GetVersionString(OldReference oldReference) return version ?? (hasVersion ? oldReference.Include.Split(',')[1].Replace(versionToken, String.Empty) : null); } - private static NewPackageReference ConvertOldToNewPackageReference(OldReference oldReference) + public static NewPackageReference ConvertOldToNewPackageReference(OldReference oldReference, IEnumerable skipList) { var version = GetVersionString(oldReference); var include = oldReference.Include.Split(',').First(); - return version == null || include == "System.Management.Automation" ? null : new NewPackageReference { Include = include, Version = version }; + return version == null || skipList.Contains(include) ? null : new NewPackageReference { Include = include, Version = version }; } private static string ModifyOutputPath(string outputPath) @@ -165,7 +191,8 @@ private static string ModifyOutputPath(string outputPath) {"Commands.Common.Strategies.csproj", "Common.Strategies.Netcore.csproj"}, {"Commands.ResourceManager.Common.csproj", "Common.ResourceManager.Netcore.csproj"}, - {"Commands.Resources.Rest.csproj", "Commands.Resources.Rest.Netcore.csproj"} + {"Commands.Resources.Rest.csproj", "Commands.Resources.Rest.Netcore.csproj"}, + {"Commands.ScenarioTests.ResourceManager.Common.csproj","Common.ResourceManager.ScenarioTests.Netcore.csproj"} }; private static string ModifyProjectReferencePath(string includePath) @@ -180,7 +207,7 @@ private static string ModifyProjectReferencePath(string includePath) public static NewProjectDefinition ConvertOldToNewNetCore(OldProjectDefinition oldDefinition) { var oldReferences = oldDefinition.ItemGroups.Where(ig => ig.References?.Any() ?? false).SelectMany(ig => ig.References); - var newPackageReferences = oldReferences.Select(ConvertOldToNewPackageReference).Where(r => r != null).ToList(); + var newPackageReferences = oldReferences.Select(or => ConvertOldToNewPackageReference(or, new []{ "System.Management.Automation" })).Where(r => r != null).ToList(); var packageReferencesItemGroup = !newPackageReferences.Any() ? null : new NewItemGroup { PackageReferences = newPackageReferences @@ -305,5 +332,136 @@ public static NewProjectDefinition ConvertOldToNewNetCore(OldProjectDefinition o } }; } + + private static readonly List TestReferenceSkipList = new List + { + "Hyak.Common", + "Microsoft.Azure.Common", + "Microsoft.Azure.Common.NetFramework", + "Microsoft.Azure.Gallery", + "Microsoft.Azure.Management.Authorization", + "Microsoft.Azure.Management.ResourceManager", + "Microsoft.Azure.ResourceManager", + "Microsoft.Azure.Test.Framework", + "Microsoft.Azure.Test.HttpRecorder", + "Microsoft.IdentityModel.Clients.ActiveDirectory", + "Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms", + "Microsoft.Rest.ClientRuntime", + "Microsoft.Rest.ClientRuntime.Azure", + "Microsoft.Rest.ClientRuntime.Azure.Authentication", + "Microsoft.Rest.ClientRuntime.Azure.TestFramework", + "Microsoft.Threading.Tasks", + "Microsoft.Threading.Tasks.Extensions", + "Microsoft.Threading.Tasks.Extensions.Desktop", + "Microsoft.WindowsAzure.Management", + "Microsoft.Bcl.Build", + "Microsoft.Data.Edm", + "Microsoft.Data.OData", + "Microsoft.Data.Services.Client", + "Newtonsoft.Json", + "System.Net.Http.Extensions", + "System.Net.Http.Primitives", + "System.Spatial", + "xunit.abstractions", + "xunit.assert", + "xunit.core", + "xunit.execution.desktop" + }; + + public static NewProjectDefinition ConvertOldTestToNewTestNetCore(OldProjectDefinition oldDefinition) + { + var oldReferences = oldDefinition.ItemGroups.Where(ig => ig.References?.Any() ?? false).SelectMany(ig => ig.References); + var newPackageReferences = oldReferences.Select(or => ConvertOldToNewPackageReference(or, TestReferenceSkipList)).Where(r => r != null).ToList(); + var packageReferencesItemGroup = !newPackageReferences.Any() ? null : new NewItemGroup + { + PackageReferences = newPackageReferences + }; + + var newAssemblyName = oldDefinition.PropertyGroups.First(pg => pg.AssemblyName != null).AssemblyName; + var testDenoter = TestFolderDenoters.First(tfd => oldDefinition.FilePath.Contains(tfd)); + var newDllReferenceInclude = newAssemblyName.Replace(testDenoter, String.Empty); + var projectName = newDllReferenceInclude.Split('.').Last(); + var isRmModule = oldDefinition.FilePath.Contains("ResourceManager"); + + var newProjectReferences = oldDefinition.ItemGroups.Where(ig => ig.ProjectReferences?.Any() ?? false).SelectMany(ig => ig.ProjectReferences) + .Where(pr => !(isRmModule && pr.Include.Contains(projectName))) + .Select(pr => new NewProjectReference { Include = ModifyProjectReferencePath(pr.Include) }).ToList(); + var projectReferencesItemGroup = !newProjectReferences.Any() ? null : new NewItemGroup + { + ProjectReferences = newProjectReferences + }; + + var dllReferenceItemGroup = !isRmModule ? null : new NewItemGroup + { + References = new List + { + new NewReference + { + Include = newDllReferenceInclude, + HintPath = $"..\\..\\..\\Package\\$(Configuration)\\ResourceManager\\AzureResourceManager\\AzureRM.{projectName}.Netcore\\{newDllReferenceInclude}.dll" + } + } + }; + + var projectFolder = Path.GetDirectoryName(oldDefinition.FilePath); + var hasSessionRecords = Directory.Exists(Path.Combine(projectFolder, "SessionRecords")); + var hasScenarioTests = Directory.Exists(Path.Combine(projectFolder, "ScenarioTests")); + var noneItemGroup = !(hasSessionRecords || hasScenarioTests) ? null : new NewItemGroup + { + NoneItems = new List + { + !hasSessionRecords ? null : new NewNone + { + Update = @"SessionRecords\**\*.json", + CopyToOutputDirectory = "PreserveNewest" + }, + !hasScenarioTests ? null : new NewNone + { + Update = @"ScenarioTests\*.ps1", + CopyToOutputDirectory = "PreserveNewest" + } + } + }; + + return new NewProjectDefinition + { + Sdk = "Microsoft.NET.Sdk", + Import = new NewImport + { + Project = @"..\..\..\..\tools\Common.Netcore.Dependencies.Test.targets" + }, + PropertyGroups = new List + { + new NewPropertyGroup + { + TargetFramework = "netcoreapp2.0", + AssemblyName = newAssemblyName, + RootNamespace = oldDefinition.PropertyGroups.First(pg => pg.RootNamespace != null).RootNamespace, + GenerateAssemblyInfo = false + }, + new NewPropertyGroup + { + Condition = "'$(Configuration)|$(Platform)'=='Debug|AnyCPU'", + DelaySign = false, + DefineConstants = "TRACE;DEBUG;NETSTANDARD" + }, + new NewPropertyGroup + { + Condition = "'$(Configuration)|$(Platform)'=='Release|AnyCPU'", + SignAssembly = true, + DelaySign = true, + AssemblyOriginatorKeyFile = "MSSharedLibKey.snk", + DefineConstants = "TRACE;RELEASE;NETSTANDARD;SIGN" + } + }, + ItemGroups = new List + { + packageReferencesItemGroup, + projectReferencesItemGroup, + dllReferenceItemGroup, + noneItemGroup + } + }; + } } } diff --git a/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewItemGroup.cs b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewItemGroup.cs index e8acc0516332..3434f4a9d99f 100644 --- a/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewItemGroup.cs +++ b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewItemGroup.cs @@ -41,5 +41,8 @@ public class NewItemGroup [XmlElement("Content")] public List ContentItems { get; set; } + + [XmlElement("Reference")] + public List References { get; set; } } } diff --git a/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewReference.cs b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewReference.cs new file mode 100644 index 000000000000..4464df0eb983 --- /dev/null +++ b/tools/NetCoreCsProjSync/NetCoreCsProjSync/NewModel/NewReference.cs @@ -0,0 +1,29 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Xml.Serialization; + +namespace NetCoreCsProjSync.NewModel +{ + [Serializable] + public class NewReference + { + [XmlAttribute("Include")] + public string Include { get; set; } + + [XmlElement("HintPath")] + public string HintPath { get; set; } + } +} diff --git a/tools/NetCoreCsProjSync/NetCoreCsProjSync/Program.cs b/tools/NetCoreCsProjSync/NetCoreCsProjSync/Program.cs index fb1820104f1e..2fa27bbcac80 100644 --- a/tools/NetCoreCsProjSync/NetCoreCsProjSync/Program.cs +++ b/tools/NetCoreCsProjSync/NetCoreCsProjSync/Program.cs @@ -27,25 +27,59 @@ public static class Program { private const string Validate = "-v"; private const string Create = "-c"; + private const string TestProj = "-t"; + private const string CreateTest = "-s"; private static readonly Dictionary> ModeMap = new Dictionary> { { Validate, ValidateCsProjFiles }, - { Create, CreateCsProjFiles } + { Create, CreateCsProjFiles }, + { TestProj, TestCsProjFiles }, + { CreateTest, CreateTestCsProjFiles } }; public static void Main(string[] args) { - var rmPath = args.FirstOrDefault(a => !ModeMap.ContainsKey(a)) ?? @"..\..\..\src\ResourceManager"; + var rmPath = args.FirstOrDefault(a => !ModeMap.ContainsKey(a.ToLower())) ?? @"..\..\..\src\ResourceManager"; if (!Directory.Exists(rmPath)) { throw new ArgumentException($"Directory [{rmPath}] does not exist"); } //https://stackoverflow.com/a/17563994/294804 - var mode = args.Any(a => a.IndexOf(Create, StringComparison.InvariantCultureIgnoreCase) >= 0) ? Create : Validate; + var mode = ModeMap.Keys.FirstOrDefault(k => args.Any(a => a.IndexOf(k, StringComparison.InvariantCultureIgnoreCase) >= 0)) ?? Validate; ModeMap[mode](rmPath); } + private static void CreateTestCsProjFiles(string srcPath) + { + var projectFolders = GetTestProjectFolderPaths(srcPath); + var desktopFilePaths = GetTestDesktopFilePaths(projectFolders).Where(fp => !fp.EndsWith("Commands.Common.Tests.csproj")); + var desktopDefinitions = GetDesktopDefinitions(desktopFilePaths); + + var serializer = new XmlSerializer(typeof(NewProjectDefinition)); + foreach (var desktopDefinition in desktopDefinitions) + { + var path = ConvertDesktopToNetCorePath(desktopDefinition.FilePath); + Console.WriteLine($"Creating {path}"); + + var netCoreDefinition = ConvertOldTestToNewTestNetCore(desktopDefinition); + WriteProjectFile(serializer, netCoreDefinition, path); + } + } + + private static void TestCsProjFiles(string srcPath) + { + var projectFolders = GetTestProjectFolderPaths(srcPath); + var desktopFilePaths = GetTestDesktopFilePaths(projectFolders).Where(fp => !fp.EndsWith("Commands.Common.Tests.csproj")); + var desktopDefinitions = GetDesktopDefinitions(desktopFilePaths); + + desktopDefinitions.SelectMany(d => d.ItemGroups.SelectMany(ig => ig.References.Select(or => ConvertOldToNewPackageReference(or, Enumerable.Empty())))) + .Where(p => p != null) + .GroupBy(p => $"{p.Include}: {p.Version}", (k, r) => (Reference: k, Count: r.Count())).Select(g => $"{g.Reference}: {g.Count}") + .OrderBy(g => g) + .ToList().ForEach(Console.WriteLine); + } + private static void ValidateCsProjFiles(string rmPath) { var projectFolders = GetProjectFolderPaths(rmPath, true); @@ -132,29 +166,34 @@ private static void CreateCsProjFiles(string rmPath) Console.WriteLine($"Creating {path}"); var netCoreDefinition = ConvertOldToNewNetCore(desktopDefinition); - //https://stackoverflow.com/a/760290/294804 - //https://stackoverflow.com/a/3732234/294804 - var blankNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); - var xmlSettings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true }; - using (var stringWriter = new StringWriter()) - using (var xmlWriter = XmlWriter.Create(stringWriter, xmlSettings)) + WriteProjectFile(serializer, netCoreDefinition, path); + } + } + + private static void WriteProjectFile(XmlSerializer serializer, NewProjectDefinition netCoreDefinition, string path) + { + //https://stackoverflow.com/a/760290/294804 + //https://stackoverflow.com/a/3732234/294804 + var blankNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); + var xmlSettings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true }; + using (var stringWriter = new StringWriter()) + using (var xmlWriter = XmlWriter.Create(stringWriter, xmlSettings)) + { + serializer.Serialize(xmlWriter, netCoreDefinition, blankNamespaces); + var lines = stringWriter.ToString().Split(Environment.NewLine).ToList(); + var newLineIndecies = lines.Select((l, i) => (Index: i, Line: l)).Where(a => + a.Line.StartsWith("") || a.Line.StartsWith(" ")) + .Select(a => a.Index).ToList(); + + for (var i = 0; i < newLineIndecies.Count; ++i) { - serializer.Serialize(xmlWriter, netCoreDefinition, blankNamespaces); - var lines = stringWriter.ToString().Split(Environment.NewLine).ToList(); - var newLineIndecies = lines.Select((l, i) => (Index: i, Line: l)).Where(a => - a.Line.StartsWith("") || a.Line.StartsWith(" ")) - .Select(a => a.Index).ToList(); - - for (var i = 0; i < newLineIndecies.Count; ++i) - { - lines.Insert(newLineIndecies[i] + i + 1, String.Empty); - } - File.WriteAllLines(path, lines.Take(lines.Count - 1)); - using (var streamWriter = File.AppendText(path)) - { - streamWriter.Write(lines.Last()); - } + lines.Insert(newLineIndecies[i] + i + 1, String.Empty); + } + File.WriteAllLines(path, lines.Take(lines.Count - 1)); + using (var streamWriter = File.AppendText(path)) + { + streamWriter.Write(lines.Last()); } } }