diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 5c8353b422ac..31a51ddc7acf 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -15,14 +15,14 @@ public class GodotBuildLogger : ILogger
public void Initialize(IEventSource eventSource)
{
if (null == Parameters)
- throw new LoggerException("Log directory was not set.");
+ throw new LoggerException("Log directory parameter not specified.");
var parameters = Parameters.Split(new[] {';'});
string logDir = parameters[0];
if (string.IsNullOrEmpty(logDir))
- throw new LoggerException("Log directory was not set.");
+ throw new LoggerException("Log directory parameter is empty.");
if (parameters.Length > 1)
throw new LoggerException("Too many parameters passed.");
@@ -51,22 +51,31 @@ public void Initialize(IEventSource eventSource)
{
throw new LoggerException("Failed to create log file: " + ex.Message);
}
- else
- {
- // Unexpected failure
- throw;
- }
+
+ // Unexpected failure
+ throw;
}
eventSource.ProjectStarted += eventSource_ProjectStarted;
- eventSource.TaskStarted += eventSource_TaskStarted;
+ eventSource.ProjectFinished += eventSource_ProjectFinished;
eventSource.MessageRaised += eventSource_MessageRaised;
eventSource.WarningRaised += eventSource_WarningRaised;
eventSource.ErrorRaised += eventSource_ErrorRaised;
- eventSource.ProjectFinished += eventSource_ProjectFinished;
}
- void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
+ private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
+ {
+ WriteLine(e.Message);
+ indent++;
+ }
+
+ private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
+ {
+ indent--;
+ WriteLine(e.Message);
+ }
+
+ private void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
@@ -81,7 +90,7 @@ void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
issuesStreamWriter.WriteLine(errorLine);
}
- void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
+ private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
@@ -108,40 +117,6 @@ private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
}
}
- private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
- {
- // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
- // To keep this log clean, this logger will ignore these events.
- }
-
- private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
- {
- WriteLine(e.Message);
- indent++;
- }
-
- private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
- {
- indent--;
- WriteLine(e.Message);
- }
-
- ///
- /// Write a line to the log, adding the SenderName
- ///
- private void WriteLineWithSender(string line, BuildEventArgs e)
- {
- if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
- {
- // Well, if the sender name is MSBuild, let's leave it out for prettiness
- WriteLine(line);
- }
- else
- {
- WriteLine(e.SenderName + ": " + line);
- }
- }
-
///
/// Write a line to the log, adding the SenderName and Message
/// (these parameters are on all MSBuild event argument objects)
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
deleted file mode 100644
index 709cae70b7df..000000000000
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-using Godot;
-using System;
-using System.IO;
-using Godot.Collections;
-using GodotTools.Internals;
-using static GodotTools.Internals.Globals;
-using File = GodotTools.Utils.File;
-using Path = System.IO.Path;
-
-namespace GodotTools
-{
- public class BottomPanel : VBoxContainer
- {
- private EditorInterface editorInterface;
-
- private TabContainer panelTabs;
-
- private VBoxContainer panelBuildsTab;
-
- private ItemList buildTabsList;
- private TabContainer buildTabs;
-
- private ToolButton warningsBtn;
- private ToolButton errorsBtn;
- private Button viewLogBtn;
-
- private void _UpdateBuildTab(int index, int? currentTab)
- {
- var tab = (BuildTab)buildTabs.GetChild(index);
-
- string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
- itemName += " [" + tab.BuildInfo.Configuration + "]";
-
- buildTabsList.AddItem(itemName, tab.IconTexture);
-
- string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
- itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
- itemTooltip += "\nStatus: ";
-
- if (tab.BuildExited)
- itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
- else
- itemTooltip += "Running";
-
- if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
- itemTooltip += $"\nErrors: {tab.ErrorCount}";
-
- itemTooltip += $"\nWarnings: {tab.WarningCount}";
-
- buildTabsList.SetItemTooltip(index, itemTooltip);
-
- // If this tab was already selected before the changes or if no tab was selected
- if (currentTab == null || currentTab == index)
- {
- buildTabsList.Select(index);
- _BuildTabsItemSelected(index);
- }
- }
-
- private void _UpdateBuildTabsList()
- {
- buildTabsList.Clear();
-
- int? currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- currentTab = null;
-
- for (int i = 0; i < buildTabs.GetChildCount(); i++)
- _UpdateBuildTab(i, currentTab);
- }
-
- public BuildTab GetBuildTabFor(BuildInfo buildInfo)
- {
- foreach (var buildTab in new Array(buildTabs.GetChildren()))
- {
- if (buildTab.BuildInfo.Equals(buildInfo))
- return buildTab;
- }
-
- var newBuildTab = new BuildTab(buildInfo);
- AddBuildTab(newBuildTab);
-
- return newBuildTab;
- }
-
- private void _BuildTabsItemSelected(int idx)
- {
- if (idx < 0 || idx >= buildTabs.GetTabCount())
- throw new IndexOutOfRangeException();
-
- buildTabs.CurrentTab = idx;
- if (!buildTabs.Visible)
- buildTabs.Visible = true;
-
- warningsBtn.Visible = true;
- errorsBtn.Visible = true;
- viewLogBtn.Visible = true;
- }
-
- private void _BuildTabsNothingSelected()
- {
- if (buildTabs.GetTabCount() != 0)
- {
- // just in case
- buildTabs.Visible = false;
-
- // This callback is called when clicking on the empty space of the list.
- // ItemList won't deselect the items automatically, so we must do it ourselves.
- buildTabsList.UnselectAll();
- }
-
- warningsBtn.Visible = false;
- errorsBtn.Visible = false;
- viewLogBtn.Visible = false;
- }
-
- private void _WarningsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.WarningsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- private void _ErrorsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.ErrorsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- public void BuildProjectPressed()
- {
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
-
- string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
- string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
-
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
-
- if (File.Exists(editorScriptsMetadataPath))
- {
- try
- {
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
- }
- catch (IOException e)
- {
- GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}");
- return;
- }
- }
-
- bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
-
- if (!buildSuccess)
- return;
-
- // Notify running game for hot-reload
- Internal.ScriptEditorDebuggerReloadScripts();
-
- // Hot-reload in the editor
- GodotSharpEditor.Instance.GetNode("HotReloadAssemblyWatcher").RestartTimer();
-
- if (Internal.IsAssembliesReloadingNeeded())
- Internal.ReloadAssemblies(softReload: false);
- }
-
- private void _ViewLogPressed()
- {
- if (!buildTabsList.IsAnythingSelected())
- return;
-
- var selectedItems = buildTabsList.GetSelectedItems();
-
- if (selectedItems.Length != 1)
- throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}");
-
- int selectedItem = selectedItems[0];
-
- var buildTab = (BuildTab)buildTabs.GetTabControl(selectedItem);
-
- OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, BuildManager.MsBuildLogFileName));
- }
-
- public override void _Notification(int what)
- {
- base._Notification(what);
-
- if (what == EditorSettings.NotificationEditorSettingsChanged)
- {
- var editorBaseControl = editorInterface.GetBaseControl();
- panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
- }
- }
-
- public void AddBuildTab(BuildTab buildTab)
- {
- buildTabs.AddChild(buildTab);
- RaiseBuildTab(buildTab);
- }
-
- public void RaiseBuildTab(BuildTab buildTab)
- {
- if (buildTab.GetParent() != buildTabs)
- throw new InvalidOperationException("Build tab is not in the tabs list");
-
- buildTabs.MoveChild(buildTab, 0);
- _UpdateBuildTabsList();
- }
-
- public void ShowBuildTab()
- {
- for (int i = 0; i < panelTabs.GetTabCount(); i++)
- {
- if (panelTabs.GetTabControl(i) == panelBuildsTab)
- {
- panelTabs.CurrentTab = i;
- GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this);
- return;
- }
- }
-
- GD.PushError("Builds tab not found");
- }
-
- public override void _Ready()
- {
- base._Ready();
-
- editorInterface = GodotSharpEditor.Instance.GetEditorInterface();
-
- var editorBaseControl = editorInterface.GetBaseControl();
-
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
- SetAnchorsAndMarginsPreset(LayoutPreset.Wide);
-
- panelTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- RectMinSize = new Vector2(0, 228) * EditorScale,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
- AddChild(panelTabs);
-
- {
- // Builds tab
- panelBuildsTab = new VBoxContainer
- {
- Name = "Builds".TTR(),
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddChild(panelBuildsTab);
-
- var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
- panelBuildsTab.AddChild(toolBarHBox);
-
- var buildProjectBtn = new Button
- {
- Text = "Build Project".TTR(),
- FocusMode = FocusModeEnum.None
- };
- buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
- toolBarHBox.AddChild(buildProjectBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- warningsBtn = new ToolButton
- {
- Text = "Warnings".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
- toolBarHBox.AddChild(warningsBtn);
-
- errorsBtn = new ToolButton
- {
- Text = "Errors".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
- toolBarHBox.AddChild(errorsBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- viewLogBtn = new Button
- {
- Text = "View log".TTR(),
- FocusMode = FocusModeEnum.None,
- Visible = false
- };
- viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
- toolBarHBox.AddChild(viewLogBtn);
-
- var hsc = new HSplitContainer
- {
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelBuildsTab.AddChild(hsc);
-
- buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
- buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
- buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
- hsc.AddChild(buildTabsList);
-
- buildTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- TabsVisible = false
- };
- hsc.AddChild(buildTabs);
- }
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
similarity index 71%
rename from modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
rename to modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index ab090c46e73c..51055dc9b9bb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,7 +4,7 @@
using GodotTools.Internals;
using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
[Serializable]
public sealed class BuildInfo : Reference // TODO Remove Reference once we have proper serialization
@@ -20,7 +20,9 @@ public sealed class BuildInfo : Reference // TODO Remove Reference once we have
public override bool Equals(object obj)
{
if (obj is BuildInfo other)
- return other.Solution == Solution && other.Configuration == Configuration;
+ return other.Solution == Solution && other.Targets == Targets &&
+ other.Configuration == Configuration && other.Restore == Restore &&
+ other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
return false;
}
@@ -31,7 +33,11 @@ public override int GetHashCode()
{
int hash = 17;
hash = hash * 29 + Solution.GetHashCode();
+ hash = hash * 29 + Targets.GetHashCode();
hash = hash * 29 + Configuration.GetHashCode();
+ hash = hash * 29 + Restore.GetHashCode();
+ hash = hash * 29 + CustomProperties.GetHashCode();
+ hash = hash * 29 + LogsDirPath.GetHashCode();
return hash;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
similarity index 64%
rename from modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
rename to modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 81af3ab0eb8d..9b512d90f527 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,20 +1,19 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Build;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
-using GodotTools.Utils;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
+using OS = GodotTools.Utils.OS;
+using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
public static class BuildManager
{
- private static readonly List BuildsInProgress = new List();
+ private static BuildInfo _buildInProgress;
public const string PropNameMSBuildMono = "MSBuild (Mono)";
public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
@@ -24,6 +23,14 @@ public static class BuildManager
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
public const string MsBuildLogFileName = "msbuild_log.txt";
+ public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
+
+ public static event BuildLaunchFailedEventHandler BuildLaunchFailed;
+ public static event Action BuildStarted;
+ public static event Action BuildFinished;
+ public static event Action StdOutputReceived;
+ public static event Action StdErrorReceived;
+
private static void RemoveOldIssuesFile(BuildInfo buildInfo)
{
var issuesFile = GetIssuesFilePath(buildInfo);
@@ -36,12 +43,13 @@ private static void RemoveOldIssuesFile(BuildInfo buildInfo)
private static void ShowBuildErrorDialog(string message)
{
- GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error");
- GodotSharpEditor.Instance.BottomPanel.ShowBuildTab();
+ var plugin = GodotSharpEditor.Instance;
+ plugin.ShowErrorDialog(message, "Build error");
+ plugin.MakeBottomPanelItemVisible(plugin.MSBuildPanel);
}
- public static void RestartBuild(BuildTab buildTab) => throw new NotImplementedException();
- public static void StopBuild(BuildTab buildTab) => throw new NotImplementedException();
+ public static void RestartBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
+ public static void StopBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
private static string GetLogFilePath(BuildInfo buildInfo)
{
@@ -61,15 +69,14 @@ private static void PrintVerbose(string text)
public static bool Build(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
- buildTab.OnBuildStart();
+ BuildStarted?.Invoke(buildInfo);
// Required in order to update the build tasks list
Internal.GodotMainIteration();
@@ -80,44 +87,44 @@ public static bool Build(BuildInfo buildInfo)
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = BuildSystem.Build(buildInfo);
+ int exitCode = BuildSystem.Build(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
public static async Task BuildAsync(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
+ BuildStarted?.Invoke(buildInfo);
try
{
@@ -125,43 +132,57 @@ public static async Task BuildAsync(BuildInfo buildInfo)
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = await BuildSystem.BuildAsync(buildInfo);
+ int exitCode = await BuildSystem.BuildAsync(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
+ public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return BuildProjectBlocking(buildInfo);
+ }
+
+ private static bool BuildProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug");
+ string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
@@ -173,15 +194,6 @@ public static bool BuildProjectBlocking(string config, [CanBeNull] string platfo
{
pr.Step("Building project solution", 0);
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, new[] {"Build"}, config, restore: true);
-
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
-
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
-
if (!Build(buildInfo))
{
ShowBuildErrorDialog("Failed to build project solution");
@@ -197,18 +209,41 @@ public static bool EditorBuildCallback()
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
+ GenerateEditorScriptMetadata();
+
+ if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
+ return true; // Requested play from an external editor/IDE which already built the project
+
+ return BuildProjectBlocking("Debug");
+ }
+
+ // NOTE: This will be replaced with C# source generators in 4.0
+ public static void GenerateEditorScriptMetadata()
+ {
string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
- if (File.Exists(editorScriptsMetadataPath))
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+ if (!File.Exists(editorScriptsMetadataPath))
+ return;
- if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
- return true; // Requested play from an external editor/IDE which already built the project
+ try
+ {
+ File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+ }
+ catch (IOException e)
+ {
+ throw new IOException("Failed to copy scripts metadata file.", innerException: e);
+ }
+ }
- return BuildProjectBlocking("Debug");
+ // NOTE: This will be replaced with C# source generators in 4.0
+ public static string GenerateExportedGameScriptMetadata(bool isDebug)
+ {
+ string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
+ CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
+ return scriptsMetadataPath;
}
public static void Initialize()
@@ -254,8 +289,6 @@ public static void Initialize()
["hint"] = Godot.PropertyHint.Enum,
["hint_string"] = hintString
});
-
- EditorDef("mono/builds/print_build_output", false);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
similarity index 52%
rename from modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
rename to modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index b35bd3244b0f..bb7173eedb64 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -5,16 +5,10 @@
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
- public class BuildTab : VBoxContainer
+ public class BuildOutputView : VBoxContainer, ISerializationListener
{
- public enum BuildResults
- {
- Error,
- Success
- }
-
[Serializable]
private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization
{
@@ -29,10 +23,15 @@ private class BuildIssue : Reference // TODO Remove Reference once we have prope
private readonly Array issues = new Array(); // TODO Use List once we have proper serialization
private ItemList issuesList;
+ private TextEdit buildLog;
+ private PopupMenu issuesListContextMenu;
- public bool BuildExited { get; private set; } = false;
+ [Signal]
+ public delegate void BuildStateChanged();
- public BuildResults? BuildResult { get; private set; } = null;
+ public bool HasBuildExited { get; private set; } = false;
+
+ public BuildResult? BuildResult { get; private set; } = null;
public int ErrorCount { get; private set; } = 0;
@@ -41,23 +40,31 @@ private class BuildIssue : Reference // TODO Remove Reference once we have prope
public bool ErrorsVisible { get; set; } = true;
public bool WarningsVisible { get; set; } = true;
- public Texture IconTexture
+ public Texture BuildStateIcon
{
get
{
- if (!BuildExited)
+ if (!HasBuildExited)
return GetIcon("Stop", "EditorIcons");
- if (BuildResult == BuildResults.Error)
- return GetIcon("StatusError", "EditorIcons");
+ if (BuildResult == Build.BuildResult.Error)
+ return GetIcon("Error", "EditorIcons");
+
+ if (WarningCount > 1)
+ return GetIcon("Warning", "EditorIcons");
- return GetIcon("StatusSuccess", "EditorIcons");
+ return null;
}
}
- public BuildInfo BuildInfo { get; private set; }
+ private BuildInfo BuildInfo { get; set; }
- private void _LoadIssuesFromFile(string csvFile)
+ public bool LogVisible
+ {
+ set => buildLog.Visible = value;
+ }
+
+ private void LoadIssuesFromFile(string csvFile)
{
using (var file = new Godot.File())
{
@@ -107,7 +114,7 @@ private void _LoadIssuesFromFile(string csvFile)
}
}
- private void _IssueActivated(int idx)
+ private void IssueActivated(int idx)
{
if (idx < 0 || idx >= issuesList.GetItemCount())
throw new IndexOutOfRangeException("Item list index out of range");
@@ -190,49 +197,79 @@ public void UpdateIssuesList()
}
}
- public void OnBuildStart()
+ private void BuildLaunchFailed(BuildInfo buildInfo, string cause)
+ {
+ HasBuildExited = true;
+ BuildResult = Build.BuildResult.Error;
+
+ issuesList.Clear();
+
+ var issue = new BuildIssue {Message = cause, Warning = false};
+
+ ErrorCount += 1;
+ issues.Add(issue);
+
+ UpdateIssuesList();
+
+ EmitSignal(nameof(BuildStateChanged));
+ }
+
+ private void BuildStarted(BuildInfo buildInfo)
{
- BuildExited = false;
+ BuildInfo = buildInfo;
+ HasBuildExited = false;
issues.Clear();
WarningCount = 0;
ErrorCount = 0;
+ buildLog.Text = string.Empty;
+
UpdateIssuesList();
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ EmitSignal(nameof(BuildStateChanged));
}
- public void OnBuildExit(BuildResults result)
+ private void BuildFinished(BuildResult result)
{
- BuildExited = true;
+ HasBuildExited = true;
BuildResult = result;
- _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+ LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+
UpdateIssuesList();
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ EmitSignal(nameof(BuildStateChanged));
}
- public void OnBuildExecFailed(string cause)
+ private void StdOutputReceived(string text)
{
- BuildExited = true;
- BuildResult = BuildResults.Error;
-
- issuesList.Clear();
+ buildLog.Text += text + "\n";
+ ScrollToLastNonEmptyLogLine();
+ }
- var issue = new BuildIssue { Message = cause, Warning = false };
+ private void StdErrorReceived(string text)
+ {
+ buildLog.Text += text + "\n";
+ ScrollToLastNonEmptyLogLine();
+ }
- ErrorCount += 1;
- issues.Add(issue);
+ private void ScrollToLastNonEmptyLogLine()
+ {
+ int line;
+ for (line = buildLog.GetLineCount(); line > 0; line--)
+ {
+ string lineText = buildLog.GetLine(line);
- UpdateIssuesList();
+ if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim()))
+ break;
+ }
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ buildLog.CursorSetLine(line);
}
public void RestartBuild()
{
- if (!BuildExited)
+ if (!HasBuildExited)
throw new InvalidOperationException("Build already started");
BuildManager.RestartBuild(this);
@@ -240,28 +277,118 @@ public void RestartBuild()
public void StopBuild()
{
- if (!BuildExited)
+ if (!HasBuildExited)
throw new InvalidOperationException("Build is not in progress");
BuildManager.StopBuild(this);
}
+ private enum IssuesContextMenuOption
+ {
+ Copy
+ }
+
+ private void IssuesListContextOptionPressed(IssuesContextMenuOption id)
+ {
+ switch (id)
+ {
+ case IssuesContextMenuOption.Copy:
+ {
+ // We don't allow multi-selection but just in case that changes later...
+ string text = null;
+
+ foreach (int issueIndex in issuesList.GetSelectedItems())
+ {
+ if (text != null)
+ text += "\n";
+ text += issuesList.GetItemText(issueIndex);
+ }
+
+ if (text != null)
+ OS.Clipboard = text;
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid issue context menu option");
+ }
+ }
+
+ private void IssuesListRmbSelected(int index, Vector2 atPosition)
+ {
+ _ = index; // Unused
+
+ issuesListContextMenu.Clear();
+ issuesListContextMenu.SetSize(new Vector2(1, 1));
+
+ if (issuesList.IsAnythingSelected())
+ {
+ // Add menu entries for the selected item
+ issuesListContextMenu.AddIconItem(GetIcon("ActionCopy", "EditorIcons"),
+ label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy);
+ }
+
+ if (issuesListContextMenu.GetItemCount() > 0)
+ {
+ issuesListContextMenu.SetPosition(issuesList.RectGlobalPosition + atPosition);
+ issuesListContextMenu.Popup_();
+ }
+ }
+
public override void _Ready()
{
base._Ready();
- issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
- issuesList.Connect("item_activated", this, nameof(_IssueActivated));
- AddChild(issuesList);
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var hsc = new HSplitContainer
+ {
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill
+ };
+ AddChild(hsc);
+
+ issuesList = new ItemList
+ {
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log
+ };
+ issuesList.Connect("item_activated", this, nameof(IssueActivated));
+ issuesList.AllowRmbSelect = true;
+ issuesList.Connect("item_rmb_selected", this, nameof(IssuesListRmbSelected));
+ hsc.AddChild(issuesList);
+
+ issuesListContextMenu = new PopupMenu();
+ issuesListContextMenu.Connect("id_pressed", this, nameof(IssuesListContextOptionPressed));
+ issuesList.AddChild(issuesListContextMenu);
+
+ buildLog = new TextEdit
+ {
+ Readonly = true,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
+ };
+ hsc.AddChild(buildLog);
+
+ AddBuildEventListeners();
}
- private BuildTab()
+ private void AddBuildEventListeners()
{
+ BuildManager.BuildLaunchFailed += BuildLaunchFailed;
+ BuildManager.BuildStarted += BuildStarted;
+ BuildManager.BuildFinished += BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived += line => CallDeferred(nameof(StdOutputReceived), line);
+ BuildManager.StdErrorReceived += line => CallDeferred(nameof(StdErrorReceived), line);
}
- public BuildTab(BuildInfo buildInfo)
+ public void OnBeforeSerialize()
{
- BuildInfo = buildInfo;
+ }
+
+ public void OnAfterDeserialize()
+ {
+ AddBuildEventListeners(); // Re-add them
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
new file mode 100644
index 000000000000..59055b60c3d5
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
@@ -0,0 +1,8 @@
+namespace GodotTools.Build
+{
+ public enum BuildResult
+ {
+ Error,
+ Success
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index d9862ae36180..bac7a2e6db20 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -44,10 +44,7 @@ private static bool UsingMonoMsBuildOnWindows
}
}
- private static bool PrintBuildOutput =>
- (bool)EditorSettings.GetSetting("mono/builds/print_build_output");
-
- private static Process LaunchBuild(BuildInfo buildInfo)
+ private static Process LaunchBuild(BuildInfo buildInfo, Action stdOutHandler, Action stdErrHandler)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
@@ -58,13 +55,13 @@ private static Process LaunchBuild(BuildInfo buildInfo)
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
- bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
-
- if (!redirectOutput || Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
- startInfo.RedirectStandardOutput = redirectOutput;
- startInfo.RedirectStandardError = redirectOutput;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
if (UsingMonoMsBuildOnWindows)
@@ -82,20 +79,22 @@ private static Process LaunchBuild(BuildInfo buildInfo)
var process = new Process {StartInfo = startInfo};
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+
process.Start();
- if (redirectOutput)
- {
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- }
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
return process;
}
- public static int Build(BuildInfo buildInfo)
+ public static int Build(BuildInfo buildInfo, Action stdOutHandler, Action stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,9 +102,9 @@ public static int Build(BuildInfo buildInfo)
}
}
- public static async Task BuildAsync(BuildInfo buildInfo)
+ public static async Task BuildAsync(BuildInfo buildInfo, Action stdOutHandler, Action stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
await process.WaitForExitAsync();
@@ -152,10 +151,5 @@ private static void RemovePlatformVariable(StringDictionary environmentVariables
foreach (string env in platformEnvironmentVariables)
environmentVariables.Remove(env);
}
-
- private static bool IsDebugMsBuildRequested()
- {
- return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
- }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
new file mode 100644
index 000000000000..515412777cf1
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -0,0 +1,165 @@
+using System;
+using Godot;
+using GodotTools.Internals;
+using JetBrains.Annotations;
+using static GodotTools.Internals.Globals;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public class MSBuildPanel : VBoxContainer
+ {
+ public BuildOutputView BuildOutputView { get; private set; }
+
+ private Button errorsBtn;
+ private Button warningsBtn;
+ private Button viewLogBtn;
+
+ private void WarningsToggled(bool pressed)
+ {
+ BuildOutputView.WarningsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ private void ErrorsToggled(bool pressed)
+ {
+ BuildOutputView.ErrorsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ [UsedImplicitly]
+ public void BuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ BuildManager.GenerateEditorScriptMetadata();
+
+ if (!BuildManager.BuildProjectBlocking("Debug"))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.ScriptEditorDebuggerReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void RebuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ BuildManager.GenerateEditorScriptMetadata();
+
+ if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"}))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.ScriptEditorDebuggerReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void CleanSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Clean"});
+ }
+
+ private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
+
+ private void BuildMenuOptionPressed(BuildMenuOptions id)
+ {
+ switch (id)
+ {
+ case BuildMenuOptions.BuildSolution:
+ BuildSolution();
+ break;
+ case BuildMenuOptions.RebuildSolution:
+ RebuildSolution();
+ break;
+ case BuildMenuOptions.CleanSolution:
+ CleanSolution();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
+ }
+ }
+
+ private enum BuildMenuOptions
+ {
+ BuildSolution,
+ RebuildSolution,
+ CleanSolution
+ }
+
+ public override void _Ready()
+ {
+ base._Ready();
+
+ RectMinSize = new Vector2(0, 228) * EditorScale;
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
+ AddChild(toolBarHBox);
+
+ var buildMenuBtn = new MenuButton {Text = "Build", Icon = GetIcon("Play", "EditorIcons")};
+ toolBarHBox.AddChild(buildMenuBtn);
+
+ var buildMenu = buildMenuBtn.GetPopup();
+ buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution);
+ buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution);
+ buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution);
+ buildMenu.Connect("id_pressed", this, nameof(BuildMenuOptionPressed));
+
+ errorsBtn = new Button
+ {
+ HintTooltip = "Show Errors".TTR(),
+ Icon = GetIcon("StatusError", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ errorsBtn.Connect("toggled", this, nameof(ErrorsToggled));
+ toolBarHBox.AddChild(errorsBtn);
+
+ warningsBtn = new Button
+ {
+ HintTooltip = "Show Warnings".TTR(),
+ Icon = GetIcon("NodeWarning", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ warningsBtn.Connect("toggled", this, nameof(WarningsToggled));
+ toolBarHBox.AddChild(warningsBtn);
+
+ viewLogBtn = new Button
+ {
+ Text = "Show Output".TTR(),
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ viewLogBtn.Connect("toggled", this, nameof(ViewLogToggled));
+ toolBarHBox.AddChild(viewLogBtn);
+
+ BuildOutputView = new BuildOutputView();
+ AddChild(BuildOutputView);
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index 7629223d8ce9..d2d6f46ece01 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -31,7 +31,7 @@ public static (string, BuildTool) FindMsBuild()
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Visual Studio.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
goto case BuildTool.MsBuildVs;
}
case BuildTool.MsBuildVs:
@@ -89,7 +89,7 @@ public static (string, BuildTool) FindMsBuild()
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Mono.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
goto case BuildTool.MsBuildMono;
}
case BuildTool.MsBuildMono:
@@ -161,7 +161,7 @@ private static string FindMsBuildToolsPathOnWindows()
// Try to find 15.0 with vswhere
- var envNames = Internal.GodotIs32Bits() ? new[] { "ProgramFiles", "ProgramW6432" } : new[] { "ProgramFiles(x86)", "ProgramFiles" };
+ var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"};
string vsWherePath = null;
foreach (var envName in envNames)
@@ -177,7 +177,7 @@ private static string FindMsBuildToolsPathOnWindows()
vsWherePath = null;
}
- var vsWhereArgs = new[] { "-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild" };
+ var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
var outputArray = new Godot.Collections.Array();
int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index fc64b1a426e1..ec773cf44c20 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
using JetBrains.Annotations;
@@ -143,6 +144,8 @@ public override void _ExportBegin(string[] features, bool isDebug, string path,
private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
+ _ = flags; // Unused
+
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return;
@@ -154,12 +157,10 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, int
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
-
+ string scriptsMetadataPath = BuildManager.GenerateExportedGameScriptMetadata(isDebug);
AddFile(scriptsMetadataPath, scriptsMetadataPath);
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
+ if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
throw new Exception("Failed to build project");
// Add dependency assemblies
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index fdc0161a3b1a..2668a1790b60 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -4,9 +4,9 @@
using GodotTools.Utils;
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using GodotTools.Build;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -19,7 +19,6 @@
namespace GodotTools
{
- [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class GodotSharpEditor : EditorPlugin, ISerializationListener
{
private EditorSettings editorSettings;
@@ -37,7 +36,7 @@ public class GodotSharpEditor : EditorPlugin, ISerializationListener
private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization
- public BottomPanel BottomPanel { get; private set; }
+ public MSBuildPanel MSBuildPanel { get; private set; }
public bool SkipBuildBeforePlaying { get; set; } = false;
@@ -160,7 +159,7 @@ private void _MenuOptionPressed(MenuOptions id)
}
}
- private void _BuildSolutionPressed()
+ private void BuildSolutionPressed()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
{
@@ -168,7 +167,7 @@ private void _BuildSolutionPressed()
return; // Failed to create solution
}
- Instance.BottomPanel.BuildProjectPressed();
+ Instance.MSBuildPanel.BuildSolution();
}
private void _FileSystemDockFileMoved(string file, string newFile)
@@ -199,28 +198,28 @@ private void _FileSystemDockFolderRemoved(string oldFolder)
ProjectSettings.GlobalizePath(oldFolder));
}
- public override void _Notification(int what)
+ public override void _Ready()
{
- base._Notification(what);
+ base._Ready();
- if (what == NotificationReady)
+ MSBuildPanel.BuildOutputView.Connect(
+ nameof(BuildOutputView.BuildStateChanged), this, nameof(BuildStateChanged));
+
+ bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
+ if (showInfoDialog)
{
- bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- if (showInfoDialog)
- {
- aboutDialog.PopupExclusive = true;
- _ShowAboutDialog();
- // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
- aboutDialog.PopupExclusive = false;
- }
+ aboutDialog.PopupExclusive = true;
+ _ShowAboutDialog();
+ // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
+ aboutDialog.PopupExclusive = false;
+ }
- var fileSystemDock = GetEditorInterface().GetFileSystemDock();
+ var fileSystemDock = GetEditorInterface().GetFileSystemDock();
- fileSystemDock.Connect("files_moved", this, nameof(_FileSystemDockFileMoved));
- fileSystemDock.Connect("file_removed", this, nameof(_FileSystemDockFileRemoved));
- fileSystemDock.Connect("folder_moved", this, nameof(_FileSystemDockFolderMoved));
- fileSystemDock.Connect("folder_removed", this, nameof(_FileSystemDockFolderRemoved));
- }
+ fileSystemDock.Connect("files_moved", this, nameof(_FileSystemDockFileMoved));
+ fileSystemDock.Connect("file_removed", this, nameof(_FileSystemDockFileRemoved));
+ fileSystemDock.Connect("folder_moved", this, nameof(_FileSystemDockFolderMoved));
+ fileSystemDock.Connect("folder_removed", this, nameof(_FileSystemDockFolderRemoved));
}
private enum MenuOptions
@@ -437,6 +436,12 @@ private void ApplyNecessaryChangesToSolution()
}
}
+ private void BuildStateChanged()
+ {
+ if (bottomPanelBtn != null)
+ bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
+ }
+
public override void EnablePlugin()
{
base.EnablePlugin();
@@ -453,9 +458,8 @@ public override void EnablePlugin()
errorDialog = new AcceptDialog();
editorBaseControl.AddChild(errorDialog);
- BottomPanel = new BottomPanel();
-
- bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR());
+ MSBuildPanel = new MSBuildPanel();
+ bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
@@ -463,7 +467,7 @@ public override void EnablePlugin()
menuPopup.Hide();
menuPopup.SetAsToplevel(true);
- AddToolSubmenuItem("Mono", menuPopup);
+ AddToolSubmenuItem("C#", menuPopup);
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
{
@@ -516,7 +520,7 @@ public override void EnablePlugin()
HintTooltip = "Build solution",
FocusMode = Control.FocusModeEnum.None
};
- toolBarButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
+ toolBarButton.Connect("pressed", this, nameof(BuildSolutionPressed));
AddControlToContainer(CustomControlContainer.Toolbar, toolBarButton);
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
@@ -610,6 +614,7 @@ public void OnAfterDeserialize()
public static GodotSharpEditor Instance { get; private set; }
+ [UsedImplicitly]
private GodotSharpEditor()
{
}