1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-07 08:34:06 +02:00

Move debug resource hot swap into a separate class

This commit is contained in:
chylex 2019-08-21 10:31:30 +02:00
parent 92716ea3e0
commit a6963a18d4
5 changed files with 130 additions and 120 deletions

View File

@ -14,7 +14,6 @@
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Resources;
using TweetDuck.Updates;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Events;
@ -371,14 +370,7 @@ public void ReinjectCustomCSS(string css){
}
public void ReloadToTweetDeck(){
#if DEBUG
ScriptLoader.HotSwap();
#else
if (ModifierKeys.HasFlag(Keys.Shift)){
ScriptLoader.ClearCache();
}
#endif
Program.Resources.OnReloadTriggered();
ignoreUpdateCheckError = false;
browser.ReloadToTweetDeck();
AnalyticsFile.BrowserReloads.Trigger();

View File

@ -59,7 +59,12 @@ static Program(){
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
Config = new ConfigManager();
#if DEBUG
Resources = new ScriptLoaderDebug();
#else
Resources = new ScriptLoader();
#endif
Lib.Initialize(new App.Builder{
ErrorHandler = Reporter,

View File

@ -7,36 +7,33 @@
using TweetDuck.Core.Other;
using TweetLib.Core.Application;
#if DEBUG
using System.Diagnostics;
using System.Reflection;
using TweetDuck.Core;
using TweetLib.Core.Features.Plugins;
#endif
namespace TweetDuck.Resources{
sealed class ScriptLoader : IAppResourceHandler{
private static readonly Dictionary<string, string> CachedData = new Dictionary<string, string>(16);
public static void ClearCache(){
CachedData.Clear();
}
class ScriptLoader : IAppResourceHandler{
private readonly Dictionary<string, string> cache = new Dictionary<string, string>(16);
private Control sync;
public void Initialize(Control sync){
this.sync = sync;
}
public string Load(string path){
return LoadInternal(path, silent: false);
protected void ClearCache(){
cache.Clear();
}
public string LoadSilent(string path){
return LoadInternal(path, silent: true);
public virtual void OnReloadTriggered(){
if (Control.ModifierKeys.HasFlag(Keys.Shift)){
ClearCache();
}
}
private string LoadInternal(string name, bool silent){
public string Load(string path) => LoadInternal(path, silent: false);
public string LoadSilent(string path) => LoadInternal(path, silent: true);
protected virtual string LocateFile(string path){
return Path.Combine(Program.ScriptPath, path);
}
private string LoadInternal(string path, bool silent){
if (sync == null){
throw new InvalidOperationException("Cannot use ScriptLoader before initialization.");
}
@ -44,23 +41,15 @@ private string LoadInternal(string name, bool silent){
return null; // better than crashing I guess...?
}
if (CachedData.TryGetValue(name, out string resourceData)){
if (cache.TryGetValue(path, out string resourceData)){
return resourceData;
}
string path = Program.ScriptPath;
#if DEBUG
if (Directory.Exists(HotSwapTargetDir)){
path = Path.Combine(HotSwapTargetDir, "scripts");
Debug.WriteLine($"Hot swap active, redirecting {name}");
}
#endif
string location = LocateFile(path);
string resource;
try{
string contents = File.ReadAllText(Path.Combine(path, name), Encoding.UTF8);
string contents = File.ReadAllText(location, Encoding.UTF8);
int separator;
// first line can be either:
@ -68,7 +57,7 @@ private string LoadInternal(string name, bool silent){
// #<version>\n
if (contents[0] != '#'){
ShowLoadError(silent ? null : sync, $"File {name} appears to be corrupted, please try reinstalling the app.");
ShowLoadError(silent ? null : sync, $"File {path} appears to be corrupted, please try reinstalling the app.");
separator = 0;
}
else{
@ -76,98 +65,21 @@ private string LoadInternal(string name, bool silent){
string fileVersion = contents.Substring(1, separator-1).TrimEnd();
if (fileVersion != Program.VersionTag){
ShowLoadError(silent ? null : sync, $"File {name} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app.");
ShowLoadError(silent ? null : sync, $"File {path} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app.");
}
}
resource = contents.Substring(separator).TrimStart();
}catch(Exception ex){
ShowLoadError(silent ? null : sync, $"Could not load {name}. The program will continue running with limited functionality.\n\n{ex.Message}");
ShowLoadError(silent ? null : sync, $"Could not load {path}. The program will continue running with limited functionality.\n\n{ex.Message}");
resource = null;
}
return CachedData[name] = resource;
return cache[path] = resource;
}
private static void ShowLoadError(Control sync, string message){
sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
}
// TODO move hot swap to another implementation
#if DEBUG
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "bld", "post_build.exe");
static ScriptLoader(){
if (File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Activating resource hot swap...");
ResetHotSwap();
Application.ApplicationExit += (sender, args) => ResetHotSwap();
}
}
public static void HotSwap(){
if (!File.Exists(HotSwapRebuildScript)){
Debug.WriteLine($"Failed resource hot swap, missing rebuild script: {HotSwapRebuildScript}");
return;
}
ResetHotSwap();
Directory.CreateDirectory(HotSwapTargetDir);
Stopwatch sw = Stopwatch.StartNew();
using(Process process = Process.Start(new ProcessStartInfo{
FileName = HotSwapRebuildScript,
Arguments = $"\"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
WindowStyle = ProcessWindowStyle.Hidden
})){
// ReSharper disable once PossibleNullReferenceException
if (!process.WaitForExit(8000)){
Debug.WriteLine("Failed resource hot swap, script did not finish in time");
return;
}
else if (process.ExitCode != 0){
Debug.WriteLine($"Failed resource hot swap, script exited with code {process.ExitCode}");
return;
}
}
sw.Stop();
Debug.WriteLine($"Finished rebuild script in {sw.ElapsedMilliseconds} ms");
ClearCache();
// Force update plugin manager setup scripts
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
Type typePluginManager = typeof(PluginManager);
Type typeFormBrowser = typeof(FormBrowser);
// ReSharper disable PossibleNullReferenceException
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
typePluginManager.GetField("pluginFolder", flagsInstance).SetValue(instPluginManager, newPluginRoot);
Debug.WriteLine("Reloading hot swapped plugins...");
((PluginManager)instPluginManager).Reload();
// ReSharper restore PossibleNullReferenceException
}
private static void ResetHotSwap(){
try{
Directory.Delete(HotSwapTargetDir, true);
}catch(DirectoryNotFoundException){}
}
private static string FixPathSlash(string path){
return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + '\\';
}
#endif
}
}

View File

@ -0,0 +1,100 @@
#if DEBUG
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using TweetDuck.Core;
using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Resources{
sealed class ScriptLoaderDebug : ScriptLoader{
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "bld", "post_build.exe");
private static string FixPathSlash(string path){
return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + '\\';
}
public ScriptLoaderDebug(){
if (File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Activating resource hot swap...");
ResetHotSwap();
Application.ApplicationExit += (sender, args) => ResetHotSwap();
}
}
public override void OnReloadTriggered(){
HotSwap();
}
protected override string LocateFile(string path){
if (Directory.Exists(HotSwapTargetDir)){
Debug.WriteLine($"Hot swap active, redirecting {path}");
return Path.Combine(HotSwapTargetDir, "scripts", path);
}
return base.LocateFile(path);
}
private void HotSwap(){
if (!File.Exists(HotSwapRebuildScript)){
Debug.WriteLine($"Failed resource hot swap, missing rebuild script: {HotSwapRebuildScript}");
return;
}
ResetHotSwap();
Directory.CreateDirectory(HotSwapTargetDir);
Stopwatch sw = Stopwatch.StartNew();
using(Process process = Process.Start(new ProcessStartInfo{
FileName = HotSwapRebuildScript,
Arguments = $"\"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
WindowStyle = ProcessWindowStyle.Hidden
})){
// ReSharper disable once PossibleNullReferenceException
if (!process.WaitForExit(8000)){
Debug.WriteLine("Failed resource hot swap, script did not finish in time");
return;
}
else if (process.ExitCode != 0){
Debug.WriteLine($"Failed resource hot swap, script exited with code {process.ExitCode}");
return;
}
}
sw.Stop();
Debug.WriteLine($"Finished rebuild script in {sw.ElapsedMilliseconds} ms");
ClearCache();
// Force update plugin manager setup scripts
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
Type typePluginManager = typeof(PluginManager);
Type typeFormBrowser = typeof(FormBrowser);
// ReSharper disable PossibleNullReferenceException
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
typePluginManager.GetField("pluginFolder", flagsInstance).SetValue(instPluginManager, newPluginRoot);
Debug.WriteLine("Reloading hot swapped plugins...");
((PluginManager)instPluginManager).Reload();
// ReSharper restore PossibleNullReferenceException
}
private void ResetHotSwap(){
try{
Directory.Delete(HotSwapTargetDir, true);
}catch(DirectoryNotFoundException){}
}
}
}
#endif

View File

@ -256,6 +256,7 @@
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Reporter.cs" />
<Compile Include="Resources\ScriptLoaderDebug.cs" />
<Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>