From d2fb707788ab673ca741e165d0286b7e0839eb88 Mon Sep 17 00:00:00 2001 From: Andrew Boyarshin Date: Wed, 19 Sep 2018 22:46:12 +0700 Subject: [PATCH] Move logic to respective places --- .../Analysis/AnalysisOptions.cs | 2 +- Project2015To2017.Core/Analysis/Analyzer.cs | 18 +- .../Analysis/DiagnosticSet.cs | 35 ++- .../W002MissingProjectFileDiagnostic.cs | 35 +++ .../W020MicrosoftCSharpDiagnostic.cs | 5 +- .../W021SystemNuGetPackagesDiagnostic.cs | 3 +- .../W030LegacyDebugTypesDiagnostic.cs | 3 +- ...SBuildSdkVersionSpecificationDiagnostic.cs | 3 +- .../W032OldLanguageVersionDiagnostic.cs | 3 +- ...bsoletePortableClassLibrariesDiagnostic.cs | 3 +- .../W034ReferenceAliasesDiagnostic.cs | 3 +- .../Vs15DiagnosticSet.cs | 46 ++++ .../CommandLogic.cs | 180 ++------------- .../Project2015To2017.Migrate2017.Tool.csproj | 2 +- Project2015To2017/Facility.cs | 211 ++++++++++++++++++ Project2015To2017/PatternProcessor.cs | 4 + .../ProcessSingleItemCallback.cs | 6 + 17 files changed, 361 insertions(+), 201 deletions(-) create mode 100644 Project2015To2017.Core/Analysis/Diagnostics/W002MissingProjectFileDiagnostic.cs rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W020MicrosoftCSharpDiagnostic.cs (95%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs (98%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W030LegacyDebugTypesDiagnostic.cs (90%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs (92%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W032OldLanguageVersionDiagnostic.cs (92%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs (92%) rename {Project2015To2017.Core/Analysis => Project2015To2017.Migrate2017.Library}/Diagnostics/W034ReferenceAliasesDiagnostic.cs (89%) create mode 100644 Project2015To2017.Migrate2017.Library/Vs15DiagnosticSet.cs create mode 100644 Project2015To2017/Facility.cs create mode 100644 Project2015To2017/PatternProcessor.cs create mode 100644 Project2015To2017/ProcessSingleItemCallback.cs diff --git a/Project2015To2017.Core/Analysis/AnalysisOptions.cs b/Project2015To2017.Core/Analysis/AnalysisOptions.cs index d8a0726..4b99b4a 100644 --- a/Project2015To2017.Core/Analysis/AnalysisOptions.cs +++ b/Project2015To2017.Core/Analysis/AnalysisOptions.cs @@ -12,7 +12,7 @@ public sealed class AnalysisOptions public AnalysisOptions(IEnumerable diagnostics = null) { - this.Diagnostics = (diagnostics ?? DiagnosticSet.AllDefault).ToImmutableHashSet(); + this.Diagnostics = (diagnostics ?? DiagnosticSet.All).ToImmutableHashSet(); } } } diff --git a/Project2015To2017.Core/Analysis/Analyzer.cs b/Project2015To2017.Core/Analysis/Analyzer.cs index fa8e6e1..1127f95 100644 --- a/Project2015To2017.Core/Analysis/Analyzer.cs +++ b/Project2015To2017.Core/Analysis/Analyzer.cs @@ -1,6 +1,7 @@ using Project2015To2017.Definition; using Project2015To2017.Reading; using System; +using Project2015To2017.Analysis.Diagnostics; namespace Project2015To2017.Analysis { @@ -58,19 +59,12 @@ public void Analyze(Solution solution) { if (!projectPath.ProjectFile.Exists) { - this._reporter.Report(new[] + if (this._options.Diagnostics.Contains(DiagnosticSet.W002)) { - new DiagnosticResult - { - Code = "W002", - Message = - $"Referenced project file '{projectPath.Include}' was not found at '{projectPath.ProjectFile.FullName}'.", - Location = new DiagnosticLocation - { - Source = solution.FilePath - } - } - }, this._reporter.DefaultOptions); + this._reporter.Report( + W002MissingProjectFileDiagnostic.CreateResult(projectPath, solution), + this._reporter.DefaultOptions); + } continue; } diff --git a/Project2015To2017.Core/Analysis/DiagnosticSet.cs b/Project2015To2017.Core/Analysis/DiagnosticSet.cs index e36be97..738ad06 100644 --- a/Project2015To2017.Core/Analysis/DiagnosticSet.cs +++ b/Project2015To2017.Core/Analysis/DiagnosticSet.cs @@ -1,36 +1,35 @@ -using System.Collections.Generic; using Project2015To2017.Analysis.Diagnostics; +using System.Collections.Generic; namespace Project2015To2017.Analysis { public sealed class DiagnosticSet : HashSet { public static readonly IDiagnostic W001 = new W001IllegalProjectTypeDiagnostic(); - // W002 is not a real diagnostic + public static readonly IDiagnostic W002 = new W002MissingProjectFileDiagnostic(); public static readonly IDiagnostic W010 = new W010ConfigurationsMismatchDiagnostic(); public static readonly IDiagnostic W011 = new W011UnsupportedConditionalDiagnostic(); - public static readonly IDiagnostic W020 = new W020MicrosoftCSharpDiagnostic(); - public static readonly IDiagnostic W021 = new W021SystemNuGetPackagesDiagnostic(); - public static readonly IDiagnostic W030 = new W030LegacyDebugTypesDiagnostic(); - public static readonly IDiagnostic W031 = new W031MSBuildSdkVersionSpecificationDiagnostic(); - public static readonly IDiagnostic W032 = new W032OldLanguageVersionDiagnostic(); - public static readonly IDiagnostic W033 = new W033ObsoletePortableClassLibrariesDiagnostic(); - public static readonly IDiagnostic W034 = new W034ReferenceAliasesDiagnostic(); - public static readonly DiagnosticSet AllDefault = new DiagnosticSet + public static readonly DiagnosticSet NoneDefault = new DiagnosticSet(); + + public static readonly DiagnosticSet System = new DiagnosticSet { W001, + W002, + }; + + public static readonly DiagnosticSet GenericProjectIssues = new DiagnosticSet + { W010, W011, - W020, - W021, - W030, - W031, - W032, - W033, - W034, }; - public static readonly DiagnosticSet NoneDefault = new DiagnosticSet(); + public static readonly DiagnosticSet All = new DiagnosticSet(); + + static DiagnosticSet() + { + All.UnionWith(System); + All.UnionWith(GenericProjectIssues); + } } } \ No newline at end of file diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W002MissingProjectFileDiagnostic.cs b/Project2015To2017.Core/Analysis/Diagnostics/W002MissingProjectFileDiagnostic.cs new file mode 100644 index 0000000..5586c39 --- /dev/null +++ b/Project2015To2017.Core/Analysis/Diagnostics/W002MissingProjectFileDiagnostic.cs @@ -0,0 +1,35 @@ +using Project2015To2017.Definition; +using System; +using System.Collections.Generic; + +namespace Project2015To2017.Analysis.Diagnostics +{ + public sealed class W002MissingProjectFileDiagnostic : DiagnosticBase + { + public override bool SkipForLegacyProject => true; + public override bool SkipForModernProject => true; + public override IReadOnlyList Analyze(Project project) => + throw new InvalidOperationException("W002 is not an executable diagnostic"); + + public static IReadOnlyList CreateResult(ProjectReference @ref, Solution solution = null) + { + return new[] + { + new DiagnosticResult + { + Code = "W002", + Message = + $"Referenced project file '{@ref.Include}' was not found at '{@ref.ProjectFile.FullName}'.", + Location = new DiagnosticLocation + { + Source = solution?.FilePath + } + } + }; + } + + public W002MissingProjectFileDiagnostic() : base(2) + { + } + } +} diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W020MicrosoftCSharpDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W020MicrosoftCSharpDiagnostic.cs similarity index 95% rename from Project2015To2017.Core/Analysis/Diagnostics/W020MicrosoftCSharpDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W020MicrosoftCSharpDiagnostic.cs index d1b7409..cd180e2 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W020MicrosoftCSharpDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W020MicrosoftCSharpDiagnostic.cs @@ -1,9 +1,10 @@ -using Project2015To2017.Definition; using System; using System.Collections.Generic; using System.Linq; +using Project2015To2017.Analysis; +using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W020MicrosoftCSharpDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs similarity index 98% rename from Project2015To2017.Core/Analysis/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs index 1add134..e22a314 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W021SystemNuGetPackagesDiagnostic.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W021SystemNuGetPackagesDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W030LegacyDebugTypesDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W030LegacyDebugTypesDiagnostic.cs similarity index 90% rename from Project2015To2017.Core/Analysis/Diagnostics/W030LegacyDebugTypesDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W030LegacyDebugTypesDiagnostic.cs index 4b8e122..16fb382 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W030LegacyDebugTypesDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W030LegacyDebugTypesDiagnostic.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W030LegacyDebugTypesDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs similarity index 92% rename from Project2015To2017.Core/Analysis/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs index f2f730a..ca919f0 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W031MSBuildSdkVersionSpecificationDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W032OldLanguageVersionDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W032OldLanguageVersionDiagnostic.cs similarity index 92% rename from Project2015To2017.Core/Analysis/Diagnostics/W032OldLanguageVersionDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W032OldLanguageVersionDiagnostic.cs index 411a853..78bed32 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W032OldLanguageVersionDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W032OldLanguageVersionDiagnostic.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W032OldLanguageVersionDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs similarity index 92% rename from Project2015To2017.Core/Analysis/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs index ec88e13..76acf87 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W033ObsoletePortableClassLibrariesDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Core/Analysis/Diagnostics/W034ReferenceAliasesDiagnostic.cs b/Project2015To2017.Migrate2017.Library/Diagnostics/W034ReferenceAliasesDiagnostic.cs similarity index 89% rename from Project2015To2017.Core/Analysis/Diagnostics/W034ReferenceAliasesDiagnostic.cs rename to Project2015To2017.Migrate2017.Library/Diagnostics/W034ReferenceAliasesDiagnostic.cs index 1f0acc3..5bedacc 100644 --- a/Project2015To2017.Core/Analysis/Diagnostics/W034ReferenceAliasesDiagnostic.cs +++ b/Project2015To2017.Migrate2017.Library/Diagnostics/W034ReferenceAliasesDiagnostic.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Linq; +using Project2015To2017.Analysis; using Project2015To2017.Definition; -namespace Project2015To2017.Analysis.Diagnostics +namespace Project2015To2017.Migrate2017.Diagnostics { public sealed class W034ReferenceAliasesDiagnostic : DiagnosticBase { diff --git a/Project2015To2017.Migrate2017.Library/Vs15DiagnosticSet.cs b/Project2015To2017.Migrate2017.Library/Vs15DiagnosticSet.cs new file mode 100644 index 0000000..246fa03 --- /dev/null +++ b/Project2015To2017.Migrate2017.Library/Vs15DiagnosticSet.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Project2015To2017.Analysis; +using Project2015To2017.Analysis.Diagnostics; +using Project2015To2017.Migrate2017.Diagnostics; +using static Project2015To2017.Analysis.DiagnosticSet; + +namespace Project2015To2017.Migrate2017 +{ + public static class Vs15DiagnosticSet + { + public static readonly IDiagnostic W020 = new W020MicrosoftCSharpDiagnostic(); + public static readonly IDiagnostic W021 = new W021SystemNuGetPackagesDiagnostic(); + + public static readonly IDiagnostic W030 = new W030LegacyDebugTypesDiagnostic(); + public static readonly IDiagnostic W031 = new W031MSBuildSdkVersionSpecificationDiagnostic(); + public static readonly IDiagnostic W032 = new W032OldLanguageVersionDiagnostic(); + public static readonly IDiagnostic W033 = new W033ObsoletePortableClassLibrariesDiagnostic(); + public static readonly IDiagnostic W034 = new W034ReferenceAliasesDiagnostic(); + + public static readonly DiagnosticSet ModernIssues = new DiagnosticSet + { + W020, + W021, + }; + + public static readonly DiagnosticSet ModernizationTips = new DiagnosticSet + { + W030, + W031, + W032, + W033, + W034, + }; + + public static readonly DiagnosticSet All = new DiagnosticSet(); + + static Vs15DiagnosticSet() + { + All.UnionWith(DiagnosticSet.All); + All.UnionWith(ModernIssues); + All.UnionWith(ModernizationTips); + } + } +} diff --git a/Project2015To2017.Migrate2017.Tool/CommandLogic.cs b/Project2015To2017.Migrate2017.Tool/CommandLogic.cs index dad18d5..5264f6f 100644 --- a/Project2015To2017.Migrate2017.Tool/CommandLogic.cs +++ b/Project2015To2017.Migrate2017.Tool/CommandLogic.cs @@ -1,146 +1,41 @@ using DotNet.Globbing; -using Project2015To2017.Analysis; -using Project2015To2017.Definition; -using Project2015To2017.Reading; -using Project2015To2017.Transforms; -using Project2015To2017.Writing; using Serilog; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; -using System.Linq; namespace Project2015To2017.Migrate2017.Tool { public class CommandLogic { - private readonly Microsoft.Extensions.Logging.ILogger genericLogger; - private ImmutableArray<(string path, string extension)> files; - private readonly ImmutableArray extensions; - - public CommandLogic() - { - this.genericLogger = new Serilog.Extensions.Logging.SerilogLoggerProvider().CreateLogger(nameof(Serilog)); - extensions = ProjectConverter.ProjectFileMappings.Keys.Concat(new[] {".sln"}).ToImmutableArray(); - } - - public void DoProcessableFileSearch(bool force = false) + private readonly PatternProcessor globProcessor = (converter, pattern, callback, self) => { - if (files != null && files.Length > 0 && !force) + Log.Verbose("Falling back to globbing"); + self.DoProcessableFileSearch(); + var glob = Glob.Parse(pattern); + Log.Verbose("Parsed glob {Glob}", glob); + foreach (var (path, extension) in self.Files) { - Log.Verbose("Glob file list reevaluation skipped"); - return; + if (!glob.IsMatch(path)) continue; + var file = new FileInfo(path); + callback(file, extension); } - Log.Verbose("Glob file list reevaluation started"); - files = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.AllDirectories) - .Select(x => (path: x, extension: Path.GetExtension(x)?.ToLowerInvariant())) - .Where(x => !string.IsNullOrEmpty(x.extension)) - .Where(x => extensions.Contains(x.extension)) - .ToImmutableArray(); - Log.Verbose("Glob file list reevaluation finished: {Count} items", files.Length); - } - - public (IReadOnlyCollection projects, IReadOnlyCollection solutions) ParseProjects( - IEnumerable items, - ITransformationSet transformationSet, - ConversionOptions conversionOptions) - { - var converter = new ProjectConverter(genericLogger, transformationSet, conversionOptions); - var convertedProjects = new List(); - var convertedSolutions = new List(); - - foreach (var pattern in items) - { - if (File.Exists(pattern)) - { - var file = new FileInfo(pattern); - var extension = file.Extension.ToLowerInvariant(); - ProcessSingleItem(file, extension); - continue; - } - - if (Directory.Exists(pattern)) - { - var dir = new DirectoryInfo(pattern); - var cwdFiles = dir.GetFiles() - .Where(x => extensions.Contains(x.Extension.ToLowerInvariant())) - .ToImmutableArray(); - if (cwdFiles.Length == 1) - { - var file = cwdFiles[0]; - var extension = file.Extension.ToLowerInvariant(); - ProcessSingleItem(file, extension); - } - else - { - Log.Warning( - "Directory {Directory} contains {Count} matching files, specify which project or solution file to use.", - dir, cwdFiles.Length); - } + return true; + }; - continue; - } + private readonly Facility facility; - Log.Verbose("Falling back to globbing"); - DoProcessableFileSearch(); - var glob = Glob.Parse(pattern); - Log.Verbose("Parsed glob {Glob}", glob); - foreach (var (path, extension) in files) - { - if (!glob.IsMatch(path)) continue; - var file = new FileInfo(path); - ProcessSingleItem(file, extension); - } - } - - return (convertedProjects, convertedSolutions); - - void ProcessSingleItem(FileInfo file, string extension) - { - Log.Verbose("Processing {Item}", file); - switch (extension) - { - case ".sln": - { - var solution = SolutionReader.Instance.Read(file, genericLogger); - convertedSolutions.Add(solution); - convertedProjects.AddRange(converter.ProcessSolutionFile(solution)); - break; - } - default: - { - convertedProjects.Add(converter.ProcessProjectFile(file, null)); - break; - } - } - } + public CommandLogic() + { + var genericLogger = new Serilog.Extensions.Logging.SerilogLoggerProvider().CreateLogger(nameof(Serilog)); + facility = new Facility(genericLogger, globProcessor); } public void ExecuteEvaluate( IReadOnlyCollection items, ConversionOptions conversionOptions) { - var (projects, solutions) = ParseProjects(items, Vs15TransformationSet.Instance, conversionOptions); - - if (projects.Count == 0) - { - return; - } - - var diagnosticSet = DiagnosticSet.NoneDefault; - diagnosticSet.Add(DiagnosticSet.W001); - diagnosticSet.Add(DiagnosticSet.W010); - diagnosticSet.Add(DiagnosticSet.W011); - DoAnalysis(projects, new AnalysisOptions(diagnosticSet)); - - var projectPaths = solutions.SelectMany(x => x.UnsupportedProjectPaths).ToImmutableArray(); - if (projectPaths.Length <= 0) return; - Log.Information("List of unsupported solution projects:"); - foreach (var projectPath in projectPaths) - { - Log.Warning("Project {ProjectPath} not supported", projectPath); - } + facility.ExecuteEvaluate(items, conversionOptions); } public void ExecuteMigrate( @@ -148,51 +43,14 @@ public void ExecuteMigrate( bool noBackup, ConversionOptions conversionOptions) { - var (projects, _) = ParseProjects(items, Vs15TransformationSet.Instance, conversionOptions); - - if (projects.Count == 0) - { - return; - } - - var doBackup = !noBackup; - - var writer = new ProjectWriter(genericLogger, x => x.Delete(), _ => { }); - foreach (var project in projects) - { - writer.Write(project, doBackup); - } - - conversionOptions.ProjectCache?.Purge(); - - (projects, _) = ParseProjects(items, BasicReadTransformationSet.Instance, conversionOptions); - - DoAnalysis(projects); + facility.ExecuteMigrate(items, noBackup, conversionOptions); } public void ExecuteAnalyze( IReadOnlyCollection items, ConversionOptions conversionOptions) { - var (projects, _) = ParseProjects(items, BasicReadTransformationSet.Instance, conversionOptions); - - if (projects.Count == 0) - { - return; - } - - DoAnalysis(projects); - } - - private void DoAnalysis(IEnumerable convertedProjects, AnalysisOptions options = null) - { - Log.Verbose("Starting analysis..."); - var analyzer = new Analyzer(new LoggerReporter(genericLogger), options); - - foreach (var project in convertedProjects) - { - analyzer.Analyze(project); - } + facility.ExecuteAnalyze(items, conversionOptions); } } } \ No newline at end of file diff --git a/Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj b/Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj index 178c4db..39fea5b 100644 --- a/Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj +++ b/Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj @@ -25,7 +25,7 @@ - + diff --git a/Project2015To2017/Facility.cs b/Project2015To2017/Facility.cs new file mode 100644 index 0000000..438df4f --- /dev/null +++ b/Project2015To2017/Facility.cs @@ -0,0 +1,211 @@ +using Microsoft.Extensions.Logging; +using Project2015To2017.Analysis; +using Project2015To2017.Definition; +using Project2015To2017.Migrate2017; +using Project2015To2017.Reading; +using Project2015To2017.Transforms; +using Project2015To2017.Writing; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; + +namespace Project2015To2017 +{ + public class Facility + { + // ReSharper disable MemberCanBePrivate.Global + /// + /// Supported files to be considered for migration/analysis + /// + public ImmutableArray<(string path, string extension)> Files; + /// + /// Supported project/solution file extensions + /// + public ImmutableArray Extensions; + /// + /// The ordered list of processors applied to each supported file until one returns true + /// + public readonly List Processors; + // ReSharper restore MemberCanBePrivate.Global + + private readonly ILogger logger; + + private readonly PatternProcessor fileProcessor = (converter, pattern, callback, _) => + { + if (!File.Exists(pattern)) return false; + + var file = new FileInfo(pattern); + var extension = file.Extension.ToLowerInvariant(); + callback(file, extension); + return true; + }; + + private readonly PatternProcessor directoryProcessor = (converter, pattern, callback, self) => + { + if (!Directory.Exists(pattern)) return false; + + var dir = new DirectoryInfo(pattern); + var cwdFiles = dir.GetFiles() + .Where(x => self.Extensions.Contains(x.Extension.ToLowerInvariant())) + .ToImmutableArray(); + if (cwdFiles.Length == 1) + { + var file = cwdFiles[0]; + var extension = file.Extension.ToLowerInvariant(); + callback(file, extension); + } + else + { + self.logger.LogWarning( + "Directory {Directory} contains {Count} matching files, specify which project or solution file to use.", + dir, cwdFiles.Length); + } + return true; + }; + + public Facility(ILogger logger, params PatternProcessor[] additionalProcessors) + { + this.logger = logger; + Extensions = ProjectConverter.ProjectFileMappings.Keys.Concat(new[] { ".sln" }).ToImmutableArray(); + Processors = new List(2 + additionalProcessors.Length) + { + fileProcessor, directoryProcessor + }; + Processors.AddRange(additionalProcessors); + } + + public void DoProcessableFileSearch(bool force = false) + { + if (Files != null && Files.Length > 0 && !force) + { + logger.LogTrace("Glob file list reevaluation skipped"); + return; + } + + logger.LogTrace("Glob file list reevaluation started"); + Files = Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.AllDirectories) + .Select(x => (path: x, extension: Path.GetExtension(x)?.ToLowerInvariant())) + .Where(x => !string.IsNullOrEmpty(x.extension)) + .Where(x => Extensions.Contains(x.extension)) + .ToImmutableArray(); + logger.LogTrace("Glob file list reevaluation finished: {Count} items", Files.Length); + } + + private void DoAnalysis(IEnumerable convertedProjects, AnalysisOptions options = null) + { + logger.LogTrace("Starting analysis..."); + var analyzer = new Analyzer(new LoggerReporter(logger), options); + + foreach (var project in convertedProjects) + { + analyzer.Analyze(project); + } + } + + public (IReadOnlyCollection projects, IReadOnlyCollection solutions) ParseProjects( + IEnumerable items, + ITransformationSet transformationSet, + ConversionOptions conversionOptions) + { + var converter = new ProjectConverter(logger, transformationSet, conversionOptions); + var convertedProjects = new List(); + var convertedSolutions = new List(); + + foreach (var pattern in items) + { + foreach (var patternProcessor in Processors) + { + if (patternProcessor?.Invoke(converter, pattern, ProcessSingleItem, this) ?? false) + break; + } + } + + return (convertedProjects, convertedSolutions); + + void ProcessSingleItem(FileInfo file, string extension) + { + logger.LogTrace("Processing {Item}", file); + switch (extension) + { + case ".sln": + { + var solution = SolutionReader.Instance.Read(file, logger); + convertedSolutions.Add(solution); + convertedProjects.AddRange(converter.ProcessSolutionFile(solution)); + break; + } + default: + { + convertedProjects.Add(converter.ProcessProjectFile(file, null)); + break; + } + } + } + } + + public void ExecuteEvaluate( + IReadOnlyCollection items, + ConversionOptions conversionOptions) + { + var (projects, solutions) = ParseProjects(items, Vs15TransformationSet.Instance, conversionOptions); + + if (projects.Count == 0) + { + return; + } + + DoAnalysis(projects, new AnalysisOptions(DiagnosticSet.All)); + + var projectPaths = solutions.SelectMany(x => x.UnsupportedProjectPaths).ToImmutableArray(); + if (projectPaths.Length <= 0) return; + + logger.LogInformation("List of unsupported solution projects:"); + foreach (var projectPath in projectPaths) + { + logger.LogWarning("Project {ProjectPath} not supported", projectPath); + } + } + + public void ExecuteMigrate( + IReadOnlyCollection items, + bool noBackup, + ConversionOptions conversionOptions) + { + var (projects, _) = ParseProjects(items, Vs15TransformationSet.Instance, conversionOptions); + + if (projects.Count == 0) + { + return; + } + + var doBackup = !noBackup; + + var writer = new ProjectWriter(logger, x => x.Delete(), _ => { }); + foreach (var project in projects) + { + writer.Write(project, doBackup); + } + + conversionOptions.ProjectCache?.Purge(); + + (projects, _) = ParseProjects(items, BasicReadTransformationSet.Instance, conversionOptions); + + DoAnalysis(projects); + } + + public void ExecuteAnalyze( + IReadOnlyCollection items, + ConversionOptions conversionOptions) + { + var (projects, _) = ParseProjects(items, BasicReadTransformationSet.Instance, conversionOptions); + + if (projects.Count == 0) + { + return; + } + + DoAnalysis(projects); + } + } +} diff --git a/Project2015To2017/PatternProcessor.cs b/Project2015To2017/PatternProcessor.cs new file mode 100644 index 0000000..947bb71 --- /dev/null +++ b/Project2015To2017/PatternProcessor.cs @@ -0,0 +1,4 @@ +namespace Project2015To2017 +{ + public delegate bool PatternProcessor(ProjectConverter converter, string pattern, ProcessSingleItemCallback callback, Facility self); +} \ No newline at end of file diff --git a/Project2015To2017/ProcessSingleItemCallback.cs b/Project2015To2017/ProcessSingleItemCallback.cs new file mode 100644 index 0000000..919c1d6 --- /dev/null +++ b/Project2015To2017/ProcessSingleItemCallback.cs @@ -0,0 +1,6 @@ +using System.IO; + +namespace Project2015To2017 +{ + public delegate void ProcessSingleItemCallback(FileInfo file, string extension); +} \ No newline at end of file