Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/VisualStudio/ProjectBase/Wix/XProjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ protected override void Reload()

public string CleanURL(string url)
{
return url.TrimEnd('\\');
return url.TrimEnd('\\');
}
public void RemoveURL(HierarchyNode node)
{
Expand Down Expand Up @@ -349,7 +349,7 @@ protected override QueryStatusResult QueryStatusCommandFromOleCommandTarget(Guid
/// <param name="cmd">The command to query status for.</param>
/// <param name="result">An out parameter specifying the QueryStatusResult of the command.</param>
/// <returns>It returns true if succeeded, false otherwise.</returns>
internal bool QueryStatusOnProjectNode(Guid guidCmdGroup, uint cmd, ref QueryStatusResult result)
protected bool QueryStatusOnProjectNode(Guid guidCmdGroup, uint cmd, ref QueryStatusResult result)
{
if (guidCmdGroup == VsMenus.guidStandardCommandSet2K)
{
Expand Down Expand Up @@ -531,7 +531,7 @@ public override int Save(string fileToBeSaved, int remember, uint formatIndex)
catch (Exception)
{
// If the save of the user file fails, we should not prevent the project from saving.
}
}
}

return result;
Expand Down Expand Up @@ -674,7 +674,7 @@ private void Filechangemanager_FileChangedOnDisk(object sender, FileChangedOnDis
{
//Logger.Information("FileChangedOnDisk " + e.FileName);
this.OnFileChanged(e.FileName);

}
protected void ObserveItem(string url)
{
Expand Down
148 changes: 148 additions & 0 deletions src/VisualStudio/ProjectPackage/Commands/CommandBuild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using Community.VisualStudio.Toolkit;

using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;

using System;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace XSharp.Project
{
internal abstract class CommandBuild<T> : BaseCommand<T> where T : class, new()
{
CommandID cmd;
protected abstract string CommandName { get; }
protected CommandProgression DoCmd()
{
ThreadHelper.JoinableTaskFactory.Run(async () =>
{
await DoCmdAsync();
});
return CommandProgression.Stop;
}
protected override async Task InitializeCompletedAsync()
{
cmd = await VS.Commands.FindCommandAsync(CommandName);
if (cmd != null)
await VS.Commands.InterceptAsync(cmd, () => DoCmd());

await base.InitializeCompletedAsync();
}
protected string projectPath;
protected async Task<bool> VerifySdkProjectAsync(string command)
{
await VS.Commands.ExecuteAsync(KnownCommands.File_SaveAll);
var project = await VS.Solutions.GetActiveProjectAsync();
if (project == null)
{
await VS.MessageBox.ShowErrorAsync(command, "No active project selected.");
return false;
}
projectPath = project.FullPath;
var prj = XSharpProjectNode.FindProject(projectPath);
if (prj == null || !prj.IsSdkProject)
{
await VS.MessageBox.ShowErrorAsync(command, $"The {command} command is only available for SDK-style projects.");
return false;
}
return true;
}

protected async Task<Process> CreateProcessAsync(string arguments, string output)
{
var psi = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = arguments,
WorkingDirectory = Path.GetDirectoryName(projectPath),
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
var process = new Process { StartInfo = psi };
process.OutputDataReceived += Process_OutputDataReceived;
process.ErrorDataReceived += Process_ErrorDataReceived; ;

await EnsureOutputPaneAsync();
await outputPane.ActivateAsync();
await outputPane.WriteLineAsync(output);
await outputPane.WriteLineAsync($"Command: dotnet {arguments}");
await outputPane.WriteLineAsync("");
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();

await Task.Run(() => process.WaitForExit());

return process;
}

private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs ea)
{
ThreadHelper.JoinableTaskFactory.Run(async () =>
{
if (!string.IsNullOrEmpty(ea.Data))
{
await outputPane.WriteLineAsync($"ERROR: {ea.Data}");
}
});
}

private void Process_OutputDataReceived(object sender, DataReceivedEventArgs ea)
{
ThreadHelper.JoinableTaskFactory.Run(async () =>
{
try
{
if (!string.IsNullOrEmpty(ea.Data))
{
await outputPane.WriteLineAsync(ea.Data);
}
}
catch { }
});

}

protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
await DoCmdAsync();
}
protected abstract Task DoCmdAsync();
protected static OutputWindowPane outputPane = null;
protected async Task EnsureOutputPaneAsync()
{

if (outputPane == null)
{
var guid = VSConstants.GUID_BuildOutputWindowPane;
outputPane = await VS.Windows.GetOutputWindowPaneAsync(guid);
}
}
protected override void BeforeQueryStatus(EventArgs e)
{
base.BeforeQueryStatus(e);
ThreadHelper.JoinableTaskFactory.Run(CheckAvailabilityAsync);
}

protected async Task CheckAvailabilityAsync()
{
Command.Visible = await Commands.ProjectIsXSharpProjectAsync();
if (Command.Visible)
{
var project = await VS.Solutions.GetActiveProjectAsync();
var path = project.FullPath;
var prj = XSharpProjectNode.FindProject(path);
// Only show for SDK-style projects as they support dotnet pack/publish
Command.Visible = prj != null && prj.IsSdkProject;
}
}


}
}
83 changes: 83 additions & 0 deletions src/VisualStudio/ProjectPackage/Commands/CommandPack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Community.VisualStudio.Toolkit;

using System;
using System.IO;

using Task = System.Threading.Tasks.Task;


namespace XSharp.Project
{
[Command(PackageIds.idPackProject)]
internal sealed class CommandPack : CommandBuild<CommandPack>
{
protected override string CommandName => "Build.PackSelection";

protected override async Task DoCmdAsync()
{
if (!await VerifySdkProjectAsync("Pack"))
{
return;
}
// Show confirmation dialog
var result = await VS.MessageBox.ShowAsync(
"Create NuGet Package",
"This will create a NuGet package from the project using 'dotnet pack'.\n\n" +
"The package will be created in the project's bin folder.\n\n" +
"Continue?",
Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_QUERY,
Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL);

if (result == Microsoft.VisualStudio.VSConstants.MessageBoxResult.IDOK)
{
await PackProjectAsync(projectPath);
}
}

private async Task PackProjectAsync(string projectPath)
{
try
{
await VS.StatusBar.ShowMessageAsync("Creating NuGet package...");

// Build dotnet pack command
var arguments = $"pack \"{projectPath}\" -c Release";

var process = await CreateProcessAsync(arguments,
$"Creating NuGet package for: {Path.GetFileName(projectPath)}");


string packagePath = null;

if (process.ExitCode == 0)
{
await outputPane.WriteLineAsync("");
await outputPane.WriteLineAsync("Pack succeeded.");
await VS.StatusBar.ShowMessageAsync("NuGet package created successfully.");

var message = "NuGet package created successfully.";
if (!string.IsNullOrEmpty(packagePath))
{
message += $"\n\nPackage location:\n{packagePath}";
}

await VS.MessageBox.ShowAsync("Pack",
message,
Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_INFO,
Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OK);
}
else
{
await outputPane.WriteLineAsync("");
await outputPane.WriteLineAsync($"Pack failed with exit code {process.ExitCode}.");
await VS.StatusBar.ShowMessageAsync("Pack failed.");
await VS.MessageBox.ShowErrorAsync("Pack", "Pack failed. See Output window for details.");
}
}
catch (Exception ex)
{
await VS.MessageBox.ShowErrorAsync("Pack Error", $"Failed to create NuGet package:\n{ex.Message}");
}
}
}
}
125 changes: 125 additions & 0 deletions src/VisualStudio/ProjectPackage/Commands/CommandPublish.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Community.VisualStudio.Toolkit;

using System;
using System.IO;

using Task = System.Threading.Tasks.Task;

namespace XSharp.Project
{
[Command(PackageIds.idPublishProject)]
internal sealed class CommandPublish : CommandBuild<CommandPublish>
{
protected override string CommandName => "Build.PublishSelection";

protected override async Task DoCmdAsync()
{
if (!await VerifySdkProjectAsync("Publish"))
{
return;
}

// Show publish dialog to get options
var dialog = new PublishDialog(projectPath);
var result = dialog.ShowDialog();

if (result == true)
{
await PublishProjectAsync(projectPath, dialog.PublishOptions);
}
}

private async Task PublishProjectAsync(string projectPath, PublishOptions options)
{
try
{
await VS.StatusBar.ShowMessageAsync("Publishing project...");

var arguments = BuildPublishArguments(projectPath, options);
var process = await CreateProcessAsync(arguments,
$"Publishing project: {Path.GetFileName(projectPath)}");

if (process.ExitCode == 0)
{
await outputPane.WriteLineAsync("");
await outputPane.WriteLineAsync("Publish succeeded.");
await VS.StatusBar.ShowMessageAsync("Publish succeeded.");
await VS.MessageBox.ShowAsync("Publish",
$"Project published successfully to:\n{options.OutputPath}",
Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_INFO,
Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OK);
}
else
{
await outputPane.WriteLineAsync("");
await outputPane.WriteLineAsync($"Publish failed with exit code {process.ExitCode}.");
await VS.StatusBar.ShowMessageAsync("Publish failed.");
await VS.MessageBox.ShowErrorAsync("Publish", "Publish failed. See Output window for details.");
}
}
catch (Exception ex)
{
await VS.MessageBox.ShowErrorAsync("Publish Error", $"Failed to publish project:\n{ex.Message}");
}
}

private string BuildPublishArguments(string projectPath, PublishOptions options)
{
var args = $"publish \"{projectPath}\"";

if (!string.IsNullOrEmpty(options.Configuration))
{
args += $" -c {options.Configuration}";
}

if (!string.IsNullOrEmpty(options.TargetFramework))
{
args += $" -f {options.TargetFramework}";
}

if (!string.IsNullOrEmpty(options.Runtime))
{
args += $" -r {options.Runtime}";
}

if (!string.IsNullOrEmpty(options.OutputPath))
{
args += $" -o \"{options.OutputPath}\"";
}

if (options.SelfContained.HasValue)
{
args += options.SelfContained.Value ? " --self-contained" : " --no-self-contained";
}

if (options.SingleFile)
{
args += " -p:PublishSingleFile=true";
}

if (options.ReadyToRun)
{
args += " -p:PublishReadyToRun=true";
}

if (options.Trimmed)
{
args += " -p:PublishTrimmed=true";
}

return args;
}
}

public class PublishOptions
{
public string Configuration { get; set; } = "Release";
public string TargetFramework { get; set; }
public string Runtime { get; set; }
public string OutputPath { get; set; }
public bool? SelfContained { get; set; }
public bool SingleFile { get; set; }
public bool ReadyToRun { get; set; }
public bool Trimmed { get; set; }
}
}
Loading
Loading