From e88358d78976b75c7b14811c3317bdee6cf3b17e Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 6 Sep 2021 14:33:39 +0300 Subject: [PATCH] Studio: Improve module installing --- .../Solution/DotnetSlnFileModifierService.cs | 116 ++++++++++++++++++ .../Solution/IDotnetSlnFileModifierService.cs | 9 ++ .../Volo/Abp/Studio/Modules/ModuleConsts.cs | 16 +++ .../ModuleInstallingPipelineBuilderBase.cs | 5 + .../Steps/AddToSolutionFileStep.cs | 38 ++++++ .../Steps/PackageReferencingStep.cs | 32 ++++- 6 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/DotnetSlnFileModifierService.cs create mode 100644 studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/IDotnetSlnFileModifierService.cs create mode 100644 studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/Modules/ModuleConsts.cs create mode 100644 studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/AddToSolutionFileStep.cs diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/DotnetSlnFileModifierService.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/DotnetSlnFileModifierService.cs new file mode 100644 index 0000000000..9ef9f2bfcd --- /dev/null +++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/DotnetSlnFileModifierService.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Studio.Helpers; + +namespace Volo.Abp.Studio.Solution +{ + public class DotnetSlnFileModifierService : IDotnetSlnFileModifierService + { + public async Task AddProjectAsync(string slnFilePath, string projectPath, string slnTargetFolder = "src") + { + var projectName = Path.GetFileName(projectPath).RemovePostFix(".csproj"); + var folderId = await GetOrAddFolderIdAsync(slnFilePath, slnTargetFolder); + var slnFileLines = (await File.ReadAllTextAsync(slnFilePath)) + .Split(Environment.NewLine).ToList(); + + if (slnFileLines.Any(l => l.Contains($"\"{projectName}\""))) + { + throw new AbpStudioException(AbpStudioErrorCodes.ProjectWithSameNameAlreadyExistInTheSolutionFile) + .WithData("Solution Path", slnFilePath) + .WithData("Project Name", projectName); + } + + var projectId = Guid.NewGuid().ToString(); + var relativeProjectPath = PathHelper.GetRelativePath(slnFilePath, projectPath).Replace("/", "\\"); + var newProjectLine = "Project(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"" + projectName + "\"," + + " \"" + relativeProjectPath + "\", \"{" + projectId + "}\"" + + Environment.NewLine + "EndProject"; + + slnFileLines.InsertAfter(l => l.Trim().Equals("EndProject"), newProjectLine); + + var newPostSolutionLine = + " {" + projectId + "}.Debug|Any CPU.ActiveCfg = Debug|Any CPU" + Environment.NewLine + + " {" + projectId + "}.Debug|Any CPU.Build.0 = Debug|Any CPU" + Environment.NewLine + + " {" + projectId + "}.Release|Any CPU.ActiveCfg = Release|Any CPU" + Environment.NewLine + + " {" + projectId + "}.Release|Any CPU.Build.0 = Release|Any CPU"; + + if (!slnFileLines.Any(l => l.Contains("GlobalSection") && l.Contains("ProjectConfigurationPlatforms"))) + { + slnFileLines.InsertBefore(l => l.Trim().Equals("EndGlobal"), " GlobalSection(ProjectConfigurationPlatforms) = postSolution"); + slnFileLines.InsertBefore(l => l.Trim().Equals("EndGlobal"), " EndGlobalSection"); + } + + slnFileLines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("ProjectConfigurationPlatforms"), + newPostSolutionLine); + + var newPreSolutionLine = + " {" + projectId + "} = {" + folderId + "}"; + + + if (!slnFileLines.Any(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"))) + { + slnFileLines.InsertBefore(l => l.Trim().Equals("EndGlobal"), " GlobalSection(NestedProjects) = preSolution"); + slnFileLines.InsertBefore(l => l.Trim().Equals("EndGlobal"), " EndGlobalSection"); + } + + slnFileLines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), newPreSolutionLine); + + await File.WriteAllTextAsync(slnFilePath, string.Join(Environment.NewLine, slnFileLines)); + } + + private async Task GetOrAddFolderIdAsync(string solutionFile, string folderName, string parentFolderId = null) + { + if (folderName.Contains("/") && parentFolderId == null) + { + var parents = folderName.Split("/").SkipLast(1).JoinAsString("/"); + + parentFolderId = await GetOrAddFolderIdAsync(solutionFile, parents); + } + + var file = await File.ReadAllTextAsync(solutionFile); + var lines = file.Split(Environment.NewLine).ToList(); + string folderId; + + var folderLineIndex = lines.FindIndex(l => + l.Contains("2150E333-8FDC-42A3-9474-1A3956D46DE8") && l.Contains("\"" + folderName + "\"")); + + if (folderLineIndex < 0) + { + folderId = Guid.NewGuid().ToString(); + var newFolderLine = "Project(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"" + folderName + "\", \"" + + folderName + "\", \"{" + folderId + "}\"" + + Environment.NewLine + "EndProject"; + + if (lines.Any(l => l.Trim().Equals("EndProject"))) + { + lines.InsertAfter(l => l.Trim().Equals("EndProject"), newFolderLine); + } + else + { + lines.InsertAfter(l => l.StartsWith("Microsoft Visual Studio Solution File"), newFolderLine); + } + + if (parentFolderId != null && lines.Any(l=>l.Contains("GlobalSection") && l.Contains("NestedProjects"))) + { + var newPreSolutionLine = + " {" + folderId + "} = {" + parentFolderId + "}"; + + lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), + newPreSolutionLine); + } + + await File.WriteAllTextAsync(solutionFile, string.Join(Environment.NewLine, lines)); + } + else + { + folderId = lines[folderLineIndex].Replace("\"", " ").Replace("{", " ").Replace("}", " ").TrimEnd() + .Split(" ").Last(); + } + + return folderId; + } + } +} diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/IDotnetSlnFileModifierService.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/IDotnetSlnFileModifierService.cs new file mode 100644 index 0000000000..5bb26bf9d9 --- /dev/null +++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Solution/IDotnetSlnFileModifierService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Studio.Solution +{ + public interface IDotnetSlnFileModifierService + { + Task AddProjectAsync(string slnFile, string projectPath, string slnTargetFolder = "src"); + } +} diff --git a/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/Modules/ModuleConsts.cs b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/Modules/ModuleConsts.cs new file mode 100644 index 0000000000..378d66ab65 --- /dev/null +++ b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/Modules/ModuleConsts.cs @@ -0,0 +1,16 @@ +namespace Volo.Abp.Studio.Modules +{ + public static class ModuleConsts + { + public const string FileExtension = ".abpmdl.json"; + public const string Packages = "packages"; + + public static class Layers //TODO: Moving to PackageTypes + { + public const string Domain = "lib.domain"; + public const string DomainShared = "lib.domain.shared"; + public const string Application = "lib.application"; + public const string ApplicationContracts = "lib.application.contracts"; + } + } +} diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs index ed04e03f6a..6443e39511 100644 --- a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs +++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs @@ -11,6 +11,11 @@ namespace Volo.Abp.Studio.ModuleInstalling if (context.WithSourceCode) { pipeline.Add(new SourceCodeDownloadStep()); + + if (context.AddToSolutionFile) + { + pipeline.Add(new AddToSolutionFileStep()); + } } pipeline.Add(new PackageReferencingStep()); diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/AddToSolutionFileStep.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/AddToSolutionFileStep.cs new file mode 100644 index 0000000000..3705c0a94f --- /dev/null +++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/AddToSolutionFileStep.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Studio.Modules; +using Volo.Abp.Studio.Solution; + +namespace Volo.Abp.Studio.ModuleInstalling.Steps +{ + public class AddToSolutionFileStep : ModuleInstallingPipelineStep + { + public override async Task ExecuteAsync(ModuleInstallingContext context) + { + var slnFile = context.TargetModule.RemovePostFix(ModuleConsts.FileExtension) + ".sln"; + var moduleSrcFolder = Path.Combine(context.GetTargetSourceCodeFolder(), "src"); + var moduleTestFolder = Path.Combine(context.GetTargetSourceCodeFolder(), "test"); + + await AddProjectsUnderDirectoryToSolutionFile(context, slnFile, moduleSrcFolder, $"modules/{context.ModuleName}/src"); + await AddProjectsUnderDirectoryToSolutionFile(context, slnFile, moduleTestFolder, $"test/{context.ModuleName}"); + } + + private static async Task AddProjectsUnderDirectoryToSolutionFile( + ModuleInstallingContext context, + string slnFile, + string directory, + string slnTargetFolder) + { + var _dotnetSlnFileModifier = context.ServiceProvider.GetRequiredService(); + + var projects = Directory.GetFiles(directory, "*.csproj", SearchOption.AllDirectories); + + foreach (var project in projects) + { + await _dotnetSlnFileModifier.AddProjectAsync(slnFile, project, slnTargetFolder); + } + } + } +} diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs index 94f97ff9d2..e74987c5e1 100644 --- a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs +++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs @@ -14,7 +14,6 @@ namespace Volo.Abp.Studio.ModuleInstalling.Steps { public override async Task ExecuteAsync(ModuleInstallingContext context) { - var _csprojFileManager = context.ServiceProvider.GetRequiredService(); var _abpModuleFileManager = context.ServiceProvider.GetRequiredService(); foreach (var referencePackage in context.ReferenceModulePackages) @@ -23,10 +22,7 @@ namespace Volo.Abp.Studio.ModuleInstalling.Steps foreach (var targetPackage in targetPackages) { - await _csprojFileManager.AddPackageReferenceAsync( - targetPackage.Path.RemovePostFix(PackageConsts.FileExtension) + ".csproj", - referencePackage.Name, - context.Version); + await AddReferenceAsync(context, targetPackage, referencePackage); var targetAbpModulePath = FindAbpModuleFile(targetPackage.Path); @@ -35,6 +31,32 @@ namespace Volo.Abp.Studio.ModuleInstalling.Steps } } + private async Task AddReferenceAsync( + ModuleInstallingContext context, + PackageInfo targetPackage, + PackageInfoWithAnalyze referencePackage) + { + var _csprojFileManager = context.ServiceProvider.GetRequiredService(); + var csprojFilePath = targetPackage.Path.RemovePostFix(PackageConsts.FileExtension) + ".csproj"; + + if (context.WithSourceCode) + { + var referenceProjectPath = Directory.GetFiles(context.GetTargetSourceCodeFolder(), + $"{referencePackage.Name}.csproj").FirstOrDefault(); + + await _csprojFileManager.AddProjectReferenceAsync( + csprojFilePath, + referenceProjectPath); + } + else + { + await _csprojFileManager.AddPackageReferenceAsync( + csprojFilePath, + referencePackage.Name, + context.Version); + } + } + private string FindAbpModuleFile(string targetPackagePath) { return Directory.GetFiles(Path.GetDirectoryName(targetPackagePath), "*Module.cs",