From 4039e78b3e44bd60d4d4d74c8282754dcb7f2974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20=C3=87A=C4=9EDA=C5=9E?= Date: Thu, 3 Sep 2020 14:03:52 +0300 Subject: [PATCH] sort project dependencies using the main changed project list --- .../Cli/Build/DefaultChangedProjectFinder.cs | 79 ++++++++++++++++--- .../Build/DotNetProjectDependencyFiller.cs | 50 ------------ .../Volo/Abp/Cli/Build/DotNetProjectInfo.cs | 13 +++ .../Volo/Abp/Cli/Commands/BuildCommand.cs | 62 ++++++++++++++- 4 files changed, 142 insertions(+), 62 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DefaultChangedProjectFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DefaultChangedProjectFinder.cs index 86b140b9c3..1152784e59 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DefaultChangedProjectFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DefaultChangedProjectFinder.cs @@ -116,7 +116,7 @@ namespace Volo.Abp.Cli.Build List changedProjectList, bool forceBuild) { - if (forceBuild || repositoryBuildStatus.CommitId.IsNullOrEmpty()) + if (forceBuild || repositoryBuildStatus == null || repositoryBuildStatus.CommitId.IsNullOrEmpty()) { AddAllCsProjFiles(repository, changedProjectList); } @@ -143,13 +143,6 @@ namespace Volo.Abp.Cli.Build ); } } - - // Filter ignored directories - foreach (var ignoredDirectory in repository.IgnoredDirectories) - { - changedProjectList = changedProjectList.Where(e => !e.CsProjPath.StartsWith(Path.Combine(repository.RootPath, ignoredDirectory))) - .ToList(); - } } private void AddAllCsProjFiles(GitRepository repository, List changedFiles) @@ -160,6 +153,14 @@ namespace Volo.Abp.Cli.Build SearchOption.AllDirectories ).ToList(); + // Filter ignored directories + foreach (var ignoredDirectory in repository.IgnoredDirectories) + { + allCsProjFiles = allCsProjFiles.Where(e => + !e.StartsWith(Path.Combine(repository.RootPath, ignoredDirectory))) + .ToList(); + } + foreach (var file in allCsProjFiles) { changedFiles.Add( @@ -187,14 +188,25 @@ namespace Volo.Abp.Cli.Build .Select(e => e) .ToList(); - foreach (var file in files) + var affectedCsProjFiles = FindAffectedCsProjFiles(repository.RootPath, files); + + foreach (var file in affectedCsProjFiles) { - var csProjPath = Path.Combine(repository.RootPath, file.Path); + var csProjPath = Path.Combine(repository.RootPath, file); if (status.SucceedProjects.Any(p => p.CsProjPath == csProjPath && p.CommitId == "1")) { continue; } + // Filter ignored directories + foreach (var ignoredDirectory in repository.IgnoredDirectories) + { + if (csProjPath.StartsWith(Path.Combine(repository.RootPath, ignoredDirectory))) + { + continue; + } + } + changedFiles.Add( new DotNetProjectInfo(repository.Name, csProjPath) ); @@ -217,5 +229,52 @@ namespace Volo.Abp.Cli.Build } } } + + private List FindAffectedCsProjFiles(string repositoryPath, List files) + { + var affectedProjectFiles = new List(); + foreach (var file in files) + { + var filePath = Path.Combine(repositoryPath, file.Path); + if (filePath.EndsWith(".csproj")) + { + affectedProjectFiles.Add(filePath); + } + + if (!filePath.EndsWith(".cs")) + { + continue; + } + + var classFile = new FileInfo(filePath); + var csProjPath = FindBelongingProjectPathOfClass(classFile.Directory?.FullName); + if (csProjPath.IsNullOrEmpty() || affectedProjectFiles.Contains(csProjPath)) + { + continue; + } + + affectedProjectFiles.Add(csProjPath); + } + + return affectedProjectFiles; + } + + private string FindBelongingProjectPathOfClass(string directoryPath) + { + var files = Directory.GetFiles(directoryPath, "*.csproj", SearchOption.TopDirectoryOnly); + if (files.Length == 1) + { + return files.First(); + } + + var directoryInfo = new DirectoryInfo(directoryPath); + + if (directoryInfo.Parent == null) + { + return null; + } + + return FindBelongingProjectPathOfClass(directoryInfo.Parent.FullName); + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectDependencyFiller.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectDependencyFiller.cs index 27fea096ea..2f78ae0438 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectDependencyFiller.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectDependencyFiller.cs @@ -36,58 +36,8 @@ namespace Volo.Abp.Cli.Build var referenceProjectInfo = new FileInfo(Path.Combine(file.Directory.FullName, relativePath)); var referenceProject = new DotNetProjectInfo(project.RepositoryName, referenceProjectInfo.FullName); - project.Dependencies.Add(referenceProject); } } - - private void GetProjectsOfRepository(GitRepository repository, List projectPaths) - { - Logger.LogInformation("repo name:" + repository.Name); - Logger.LogInformation("directoryPath:" + repository.RootPath); - - GetProjectsOfADirectory(repository.RootPath, projectPaths, new List()); - - if (!repository.DependingRepositories.Any()) - { - return; - } - - foreach (var dependantRepository in repository.DependingRepositories) - { - GetProjectsOfRepository(dependantRepository, projectPaths); - } - } - - private void GetProjectsOfADirectory( - string directoryPath, - List projectPaths, - List ignoredDirectories) - { - Logger.LogInformation("directoryPath:" + directoryPath); - - var files = Directory.GetFiles(directoryPath, "*.csproj", SearchOption.AllDirectories) - .Select(f => f) - .ToList(); - - // foreach (var ignoredDirectory in ignoredDirectories) - // { - // files = files.Where(e => !e.StartsWith(Path.Combine(directoryPath, ignoredDirectory))) - // .ToList(); - // } - - projectPaths.AddRange(files); - - // foreach (var projectFile in files) - // { - // var projectNode = XElement.Load(projectFile); - // if (projectNode.Attribute("Sdk") == null) - // { - // continue; - // } - // - // projectPaths.Add(projectFile); - // } - } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectInfo.cs index 0bc431d3a0..4506094ebb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectInfo.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Build/DotNetProjectInfo.cs @@ -19,4 +19,17 @@ namespace Volo.Abp.Cli.Build Dependencies = new List(); } } + + public class DotNetProjectInfoEqualityComparer : EqualityComparer + { + public override bool Equals(DotNetProjectInfo x, DotNetProjectInfo y) + { + return (x == null && y == null) || (x != null && y != null && x.CsProjPath == y.CsProjPath); + } + + public override int GetHashCode(DotNetProjectInfo obj) + { + return obj == null ? 0 : obj.CsProjPath.GetHashCode(); + } + } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BuildCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BuildCommand.cs index 0f96959494..8b72775271 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BuildCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/BuildCommand.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -51,7 +52,7 @@ namespace Volo.Abp.Cli.Commands Options.BuildName.Short, Options.BuildName.Long ); - + var forceBuild = commandLineArgs.Options.ContainsKey(Options.ForceBuild.Long); var buildConfig = DotNetProjectBuildConfigReader.Read(workingDirectory ?? Directory.GetCurrentDirectory()); @@ -62,7 +63,10 @@ namespace Volo.Abp.Cli.Commands DotNetProjectDependencyFiller.Fill(changedProjectFiles); - var sortedProjects = changedProjectFiles.SortByDependencies(p => p.Dependencies); + var sortedProjects = SortByDependencies( + changedProjectFiles, + new DotNetProjectInfoEqualityComparer() + ); var buildSucceededProjects = DotNetProjectBuilder.Build( sortedProjects, @@ -84,6 +88,60 @@ namespace Volo.Abp.Cli.Commands return Task.CompletedTask; } + private List SortByDependencies( + List source, + IEqualityComparer comparer = null) + { + /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp + * http://en.wikipedia.org/wiki/Topological_sorting + */ + + var sorted = new List(); + var visited = new Dictionary(comparer); + + foreach (var item in source) + { + SortByDependenciesVisit(source, item, sorted, visited); + } + + return sorted; + } + + private void SortByDependenciesVisit( + List source, + DotNetProjectInfo item, + List sorted, + Dictionary visited) + { + bool inProcess; + var alreadyVisited = visited.TryGetValue(item, out inProcess); + + if (alreadyVisited) + { + if (inProcess) + { + throw new ArgumentException("Cyclic dependency found! Item: " + item); + } + } + else + { + visited[item] = true; + + var dependencies = item.Dependencies; + if (dependencies != null) + { + foreach (var dependency in dependencies) + { + var dependencyItem = source.FirstOrDefault(e => e.CsProjPath == dependency.CsProjPath); + SortByDependenciesVisit(source, dependencyItem, sorted, visited); + } + } + + visited[item] = false; + sorted.Add(item); + } + } + public string GetUsageInfo() { var sb = new StringBuilder();