C#: Save copy of sln and csproj before applying fixes

(cherry picked from commit 93d7ec8836)
This commit is contained in:
Ignacio Etcheverry 2020-04-22 14:39:42 +02:00 committed by Rémi Verschelde
parent a5bbd13794
commit 08f41f474b
5 changed files with 77 additions and 44 deletions

View File

@ -0,0 +1,27 @@
using System.IO;
namespace GodotTools.Core
{
public static class FileUtils
{
public static void SaveBackupCopy(string filePath)
{
string backupPathBase = filePath + ".old";
string backupPath = backupPathBase;
const int maxAttempts = 5;
int attempt = 1;
while (File.Exists(backupPath) && attempt <= maxAttempts)
{
backupPath = backupPathBase + "." + (attempt);
attempt++;
}
if (attempt > maxAttempts + 1)
return;
File.Copy(filePath, backupPath, overwrite: true);
}
}
}

View File

@ -31,6 +31,7 @@
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FileUtils.cs" />
<Compile Include="ProcessExtensions.cs" /> <Compile Include="ProcessExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StringExtensions.cs" /> <Compile Include="StringExtensions.cs" />

View File

@ -153,7 +153,12 @@ EndProject";
var result = regex.Replace(input,m => dict[m.Value]); var result = regex.Replace(input,m => dict[m.Value]);
if (result != input) if (result != input)
{
// Save a copy of the solution before replacing it
FileUtils.SaveBackupCopy(slnPath);
File.WriteAllText(slnPath, result); File.WriteAllText(slnPath, result);
}
} }
} }
} }

View File

@ -9,8 +9,28 @@ using Microsoft.Build.Construction;
namespace GodotTools.ProjectEditor namespace GodotTools.ProjectEditor
{ {
public sealed class MSBuildProject
{
public ProjectRootElement Root { get; }
public bool HasUnsavedChanges => Root.HasUnsavedChanges;
public void Save() => Root.Save();
public MSBuildProject(ProjectRootElement root)
{
Root = root;
}
}
public static class ProjectUtils public static class ProjectUtils
{ {
public static MSBuildProject Open(string path)
{
var root = ProjectRootElement.Open(path);
return root != null ? new MSBuildProject(root) : null;
}
public static void AddItemToProjectChecked(string projectPath, string itemType, string include) public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
{ {
var dir = Directory.GetParent(projectPath).FullName; var dir = Directory.GetParent(projectPath).FullName;
@ -43,7 +63,6 @@ namespace GodotTools.ProjectEditor
public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include) public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
{ {
var dir = Directory.GetParent(projectPath).FullName;
var root = ProjectRootElement.Open(projectPath); var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null); Debug.Assert(root != null);
@ -59,8 +78,6 @@ namespace GodotTools.ProjectEditor
var root = ProjectRootElement.Open(projectPath); var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null); Debug.Assert(root != null);
bool dirty = false;
var oldFolderNormalized = oldFolder.NormalizePath(); var oldFolderNormalized = oldFolder.NormalizePath();
var newFolderNormalized = newFolder.NormalizePath(); var newFolderNormalized = newFolder.NormalizePath();
string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath(); string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
@ -71,10 +88,9 @@ namespace GodotTools.ProjectEditor
string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath(); string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length); string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\"); item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
dirty = true;
} }
if (dirty) if (root.HasUnsavedChanges)
root.Save(); root.Save();
} }
@ -150,12 +166,9 @@ namespace GodotTools.ProjectEditor
} }
/// Simple function to make sure the Api assembly references are configured correctly /// Simple function to make sure the Api assembly references are configured correctly
public static void FixApiHintPath(string projectPath) public static void FixApiHintPath(MSBuildProject project)
{ {
var root = ProjectRootElement.Open(projectPath); var root = project.Root;
Debug.Assert(root != null);
bool dirty = false;
void AddPropertyIfNotPresent(string name, string condition, string value) void AddPropertyIfNotPresent(string name, string condition, string value)
{ {
@ -170,7 +183,6 @@ namespace GodotTools.ProjectEditor
} }
root.AddProperty(name, value).Condition = " " + condition + " "; root.AddProperty(name, value).Condition = " " + condition + " ";
dirty = true;
} }
AddPropertyIfNotPresent(name: "ApiConfiguration", AddPropertyIfNotPresent(name: "ApiConfiguration",
@ -212,7 +224,6 @@ namespace GodotTools.ProjectEditor
} }
referenceWithHintPath.AddMetadata("HintPath", hintPath); referenceWithHintPath.AddMetadata("HintPath", hintPath);
dirty = true;
return; return;
} }
@ -221,14 +232,12 @@ namespace GodotTools.ProjectEditor
{ {
// Found a Reference item without a HintPath // Found a Reference item without a HintPath
referenceWithoutHintPath.AddMetadata("HintPath", hintPath); referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
dirty = true;
return; return;
} }
} }
// Found no Reference item at all. Add it. // Found no Reference item at all. Add it.
root.AddItem("Reference", referenceName).Condition = " " + condition + " "; root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
dirty = true;
} }
const string coreProjectName = "GodotSharp"; const string coreProjectName = "GodotSharp";
@ -242,17 +251,11 @@ namespace GodotTools.ProjectEditor
SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath); SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath); SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
if (dirty)
root.Save();
} }
public static void MigrateFromOldConfigNames(string projectPath) public static void MigrateFromOldConfigNames(MSBuildProject project)
{ {
var root = ProjectRootElement.Open(projectPath); var root = project.Root;
Debug.Assert(root != null);
bool dirty = false;
bool hasGodotProjectGeneratorVersion = false; bool hasGodotProjectGeneratorVersion = false;
bool foundOldConfiguration = false; bool foundOldConfiguration = false;
@ -267,7 +270,6 @@ namespace GodotTools.ProjectEditor
{ {
configItem.Value = "Debug"; configItem.Value = "Debug";
foundOldConfiguration = true; foundOldConfiguration = true;
dirty = true;
} }
} }
@ -275,7 +277,6 @@ namespace GodotTools.ProjectEditor
{ {
root.PropertyGroups.First(g => g.Condition == string.Empty)? root.PropertyGroups.First(g => g.Condition == string.Empty)?
.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString()); .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
dirty = true;
} }
if (!foundOldConfiguration) if (!foundOldConfiguration)
@ -299,33 +300,21 @@ namespace GodotTools.ProjectEditor
void MigrateConditions(string oldCondition, string newCondition) void MigrateConditions(string oldCondition, string newCondition)
{ {
foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition)) foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
{
propertyGroup.Condition = " " + newCondition + " "; propertyGroup.Condition = " " + newCondition + " ";
dirty = true;
}
foreach (var propertyGroup in root.PropertyGroups) foreach (var propertyGroup in root.PropertyGroups)
{ {
foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition)) foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
{
prop.Condition = " " + newCondition + " "; prop.Condition = " " + newCondition + " ";
dirty = true;
}
} }
foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition)) foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
{
itemGroup.Condition = " " + newCondition + " "; itemGroup.Condition = " " + newCondition + " ";
dirty = true;
}
foreach (var itemGroup in root.ItemGroups) foreach (var itemGroup in root.ItemGroups)
{ {
foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition)) foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
{
item.Condition = " " + newCondition + " "; item.Condition = " " + newCondition + " ";
dirty = true;
}
} }
} }
@ -340,10 +329,6 @@ namespace GodotTools.ProjectEditor
MigrateConfigurationConditions("Release", "ExportRelease"); MigrateConfigurationConditions("Release", "ExportRelease");
MigrateConfigurationConditions("Tools", "Debug"); // Must be last MigrateConfigurationConditions("Tools", "Debug"); // Must be last
} }
if (dirty)
root.Save();
} }
} }
} }

View File

@ -1,4 +1,5 @@
using Godot; using Godot;
using GodotTools.Core;
using GodotTools.Export; using GodotTools.Export;
using GodotTools.Utils; using GodotTools.Utils;
using System; using System;
@ -450,13 +451,27 @@ namespace GodotTools
{ {
// Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath); DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
// Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath);
// Apply the other fixes after configurations are migrated var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
?? throw new Exception("Cannot open C# project");
// NOTE: The order in which changes are made to the project is important
// Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
ProjectUtils.MigrateFromOldConfigNames(msbuildProject);
// Apply the other fixes only after configurations have been migrated
// Make sure the existing project has Api assembly references configured correctly // Make sure the existing project has Api assembly references configured correctly
ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath); ProjectUtils.FixApiHintPath(msbuildProject);
if (msbuildProject.HasUnsavedChanges)
{
// Save a copy of the project before replacing it
FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
msbuildProject.Save();
}
} }
catch (Exception e) catch (Exception e)
{ {