|
|
|
@ -13,7 +13,6 @@ namespace Volo.Abp.Cli.ProjectModification; |
|
|
|
public class SolutionFileModifier : ITransientDependency |
|
|
|
{ |
|
|
|
private readonly ICmdHelper _cmdHelper; |
|
|
|
public static Encoding DefaultEncoding = Encoding.UTF8; |
|
|
|
|
|
|
|
public SolutionFileModifier(ICmdHelper cmdHelper) |
|
|
|
{ |
|
|
|
@ -22,13 +21,15 @@ public class SolutionFileModifier : ITransientDependency |
|
|
|
|
|
|
|
public async Task RemoveProjectFromSolutionFileAsync(string solutionFile, string projectName) |
|
|
|
{ |
|
|
|
if (solutionFile.EndsWith(".sln")) |
|
|
|
{ |
|
|
|
await RemoveProjectFromSlnFileAsync(solutionFile, projectName); |
|
|
|
} |
|
|
|
else |
|
|
|
var list = _cmdHelper.RunCmdAndGetOutput($"dotnet sln {solutionFile} list"); |
|
|
|
|
|
|
|
foreach (var line in list.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None)) |
|
|
|
{ |
|
|
|
await RemoveProjectFromSlnxFileAsync(solutionFile, projectName); |
|
|
|
if (Path.GetFileNameWithoutExtension(line.Trim()).Equals(projectName, StringComparison.InvariantCultureIgnoreCase)) |
|
|
|
{ |
|
|
|
_cmdHelper.RunCmd($"dotnet sln {solutionFile} remove {line.Trim()}"); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -42,168 +43,18 @@ public class SolutionFileModifier : ITransientDependency |
|
|
|
await AddPackageAsync(package, solutionFile); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task RemoveProjectFromSlnxFileAsync(string solutionFile, string projectName) |
|
|
|
{ |
|
|
|
var document = new XmlDocument { PreserveWhitespace = true }; |
|
|
|
document.Load(solutionFile); |
|
|
|
var projectNodes = document.SelectNodes("/Solution/Folder/Project"); |
|
|
|
|
|
|
|
if (projectNodes == null || projectNodes.Count < 1) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var nodesToBeRemoved = new List<XmlNode>(); |
|
|
|
foreach (XmlNode projectNode in projectNodes) |
|
|
|
{ |
|
|
|
var pathAttr = projectNode.Attributes?["Path"]?.Value; |
|
|
|
if (string.IsNullOrWhiteSpace(pathAttr)) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var normalized = pathAttr.Replace('\\', '/'); |
|
|
|
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(normalized); |
|
|
|
|
|
|
|
if (string.Equals(fileNameWithoutExtension, projectName, StringComparison.OrdinalIgnoreCase)) |
|
|
|
{ |
|
|
|
nodesToBeRemoved.Add(projectNode); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var node in nodesToBeRemoved) |
|
|
|
{ |
|
|
|
node.ParentNode!.RemoveChild(node); |
|
|
|
} |
|
|
|
|
|
|
|
await File.WriteAllTextAsync(solutionFile, document.OuterXml); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task RemoveProjectFromSlnFileAsync(string solutionFile, string projectName) |
|
|
|
{ |
|
|
|
using (var fileStream = File.Open(solutionFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) |
|
|
|
{ |
|
|
|
using (var sr = new StreamReader(fileStream, Encoding.Default, true)) |
|
|
|
{ |
|
|
|
var solutionFileContent = await sr.ReadToEndAsync(); |
|
|
|
solutionFileContent.NormalizeLineEndings(); |
|
|
|
|
|
|
|
var lines = solutionFileContent.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None); |
|
|
|
var updatedContent = RemoveProject(lines.ToList(), projectName).JoinAsString(Environment.NewLine); |
|
|
|
|
|
|
|
fileStream.Seek(0, SeekOrigin.Begin); |
|
|
|
fileStream.SetLength(0); |
|
|
|
|
|
|
|
using (var sw = new StreamWriter(fileStream, DefaultEncoding)) |
|
|
|
{ |
|
|
|
await sw.WriteAsync(updatedContent); |
|
|
|
await sw.FlushAsync(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async Task AddPackageAsync(NugetPackageInfo package, string solutionFile) |
|
|
|
{ |
|
|
|
var srcFolderId = await AddNewFolderAndGetIdOrGetExistingIdAsync(solutionFile, "src"); |
|
|
|
|
|
|
|
var solutionFileContent = File.ReadAllText(solutionFile); |
|
|
|
var lines = solutionFileContent.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None).ToList(); |
|
|
|
|
|
|
|
if (lines.Any(l => l.Contains($"\"{package.Name}\""))) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var projectGuid = Guid.NewGuid().ToString(); |
|
|
|
|
|
|
|
var newProjectLine = "Project(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"" + package.Name + "\"," + |
|
|
|
" \"packages\\" + package.Name + "\\" + |
|
|
|
"\\" + package.Name + ".csproj\", \"{" + projectGuid + "}\"" |
|
|
|
+ Environment.NewLine + "EndProject"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Trim().Equals("EndProject"), newProjectLine); |
|
|
|
|
|
|
|
var newPostSolutionLine = |
|
|
|
" {" + projectGuid + "}.Debug|Any CPU.ActiveCfg = Debug|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Debug|Any CPU.Build.0 = Debug|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Release|Any CPU.ActiveCfg = Release|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Release|Any CPU.Build.0 = Release|Any CPU"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("ProjectConfigurationPlatforms"), |
|
|
|
newPostSolutionLine); |
|
|
|
|
|
|
|
var newPreSolutionLine = |
|
|
|
" {" + projectGuid + "} = {" + srcFolderId + "}"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), newPreSolutionLine); |
|
|
|
|
|
|
|
File.WriteAllText(solutionFile, string.Join(Environment.NewLine, lines), DefaultEncoding); |
|
|
|
} |
|
|
|
|
|
|
|
private List<string> RemoveProject(List<string> solutionFileLines, string projectName) |
|
|
|
{ |
|
|
|
var projectKey = FindProjectKey(solutionFileLines, projectName); |
|
|
|
|
|
|
|
if (projectKey == null) |
|
|
|
{ |
|
|
|
return solutionFileLines; |
|
|
|
} |
|
|
|
|
|
|
|
var newSolutionFileLines = new List<string>(); |
|
|
|
var firstOccurence = true; |
|
|
|
|
|
|
|
for (var i = 0; i < solutionFileLines.Count; ++i) |
|
|
|
{ |
|
|
|
if (solutionFileLines[i].Contains(projectKey)) |
|
|
|
{ |
|
|
|
if (firstOccurence) |
|
|
|
{ |
|
|
|
firstOccurence = false; |
|
|
|
++i; //Skip "EndProject" line too.
|
|
|
|
} |
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
newSolutionFileLines.Add(solutionFileLines[i]); |
|
|
|
} |
|
|
|
|
|
|
|
return newSolutionFileLines; |
|
|
|
} |
|
|
|
|
|
|
|
private string FindProjectKey(List<string> solutionFileLines, string projectName) |
|
|
|
{ |
|
|
|
var projectNameWithQuotes = $"\"{projectName}\""; |
|
|
|
foreach (var solutionFileLine in solutionFileLines) |
|
|
|
{ |
|
|
|
if (solutionFileLine.Contains(projectNameWithQuotes)) |
|
|
|
{ |
|
|
|
var curlyBracketStartIndex = solutionFileLine.LastIndexOf("{", StringComparison.OrdinalIgnoreCase); |
|
|
|
var curlyBracketEndIndex = solutionFileLine.LastIndexOf("}", StringComparison.OrdinalIgnoreCase); |
|
|
|
return solutionFileLine.Substring(curlyBracketStartIndex + 1, |
|
|
|
curlyBracketEndIndex - curlyBracketStartIndex - 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
_cmdHelper.RunCmd($"dotnet sln {solutionFile} add packages\\{package.Name}\\{package.Name}.csproj --solution-folder src"); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task AddModuleAsync(ModuleWithMastersInfo module, string solutionFile) |
|
|
|
{ |
|
|
|
var srcModuleFolderId = await AddNewFolderAndGetIdOrGetExistingIdAsync(solutionFile, module.Name, |
|
|
|
await AddNewFolderAndGetIdOrGetExistingIdAsync(solutionFile, "modules")); |
|
|
|
var testModuleFolderId = await AddNewFolderAndGetIdOrGetExistingIdAsync(solutionFile, module.Name + ".Tests", |
|
|
|
await AddNewFolderAndGetIdOrGetExistingIdAsync(solutionFile, "test")); |
|
|
|
|
|
|
|
var file = File.ReadAllText(solutionFile); |
|
|
|
var lines = file.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None).ToList(); |
|
|
|
|
|
|
|
var projectsUnderModule = Directory.GetFiles( |
|
|
|
Path.Combine(Path.GetDirectoryName(solutionFile), "modules", module.Name), |
|
|
|
"*.csproj", |
|
|
|
SearchOption.AllDirectories); |
|
|
|
|
|
|
|
|
|
|
|
var projectsUnderTest = new List<string>(); |
|
|
|
if (Directory.Exists(Path.Combine(Path.GetDirectoryName(solutionFile), "modules", module.Name, "test"))) |
|
|
|
{ |
|
|
|
@ -215,41 +66,14 @@ public class SolutionFileModifier : ITransientDependency |
|
|
|
|
|
|
|
foreach (var projectPath in projectsUnderModule) |
|
|
|
{ |
|
|
|
var parentFolderId = projectsUnderTest.Contains(projectPath) ? testModuleFolderId : srcModuleFolderId; |
|
|
|
var projectId = Path.GetFileName(projectPath).Replace(".csproj", ""); |
|
|
|
var projectParentFolderInModule = projectsUnderTest.Contains(projectPath) ? "test" : "src"; |
|
|
|
|
|
|
|
if (lines.Any(l => l.Contains($"\"{projectId}\""))) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var projectGuid = Guid.NewGuid().ToString(); |
|
|
|
var folder = projectsUnderTest.Contains(projectPath) ? "test" : "src"; |
|
|
|
|
|
|
|
var newProjectLine = "Project(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"" + projectId + "\"," + |
|
|
|
" \"modules\\" + module.Name + "\\" + projectParentFolderInModule + "\\" + |
|
|
|
projectId + "\\" + projectId + ".csproj\", \"{" + projectGuid + "}\"" |
|
|
|
+ Environment.NewLine + "EndProject"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Trim().Equals("EndProject"), newProjectLine); |
|
|
|
|
|
|
|
var newPostSolutionLine = |
|
|
|
" {" + projectGuid + "}.Debug|Any CPU.ActiveCfg = Debug|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Debug|Any CPU.Build.0 = Debug|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Release|Any CPU.ActiveCfg = Release|Any CPU" + Environment.NewLine + |
|
|
|
" {" + projectGuid + "}.Release|Any CPU.Build.0 = Release|Any CPU"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("ProjectConfigurationPlatforms"), |
|
|
|
newPostSolutionLine); |
|
|
|
|
|
|
|
var newPreSolutionLine = |
|
|
|
" {" + projectGuid + "} = {" + parentFolderId + "}"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), newPreSolutionLine); |
|
|
|
var projectId = Path.GetFileName(projectPath).Replace(".csproj", ""); |
|
|
|
var package = @$"modules\{module.Name}\{folder}\{projectId}\{projectId}.csproj"; |
|
|
|
|
|
|
|
_cmdHelper.RunCmd($"dotnet sln {solutionFile} add {package} --solution-folder {folder}"); |
|
|
|
} |
|
|
|
|
|
|
|
File.WriteAllText(solutionFile, string.Join(Environment.NewLine, lines), Encoding.UTF8); |
|
|
|
|
|
|
|
|
|
|
|
if (module.MasterModuleInfos != null) |
|
|
|
{ |
|
|
|
foreach (var masterModule in module.MasterModuleInfos) |
|
|
|
@ -258,43 +82,4 @@ public class SolutionFileModifier : ITransientDependency |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async Task<string> AddNewFolderAndGetIdOrGetExistingIdAsync(string solutionFile, string folderName, |
|
|
|
string parentFolderId = null) |
|
|
|
{ |
|
|
|
var file = File.ReadAllText(solutionFile); |
|
|
|
var lines = file.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None).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"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Trim().Equals("EndProject"), newFolderLine); |
|
|
|
|
|
|
|
if (parentFolderId != null) |
|
|
|
{ |
|
|
|
var newPreSolutionLine = |
|
|
|
" {" + folderId + "} = {" + parentFolderId + "}"; |
|
|
|
|
|
|
|
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), |
|
|
|
newPreSolutionLine); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
folderId = lines[folderLineIndex].Replace("\"", " ").Replace("{", " ").Replace("}", " ").TrimEnd() |
|
|
|
.Split(" ").Last(); |
|
|
|
} |
|
|
|
|
|
|
|
File.WriteAllText(solutionFile, string.Join(Environment.NewLine, lines), Encoding.UTF8); |
|
|
|
|
|
|
|
return folderId; |
|
|
|
} |
|
|
|
} |
|
|
|
|