I'm not sure what scmclean is because I couldn't find any information about it. Nevertheless, try the following:
Option 1 (CliWrap)
Download/install NuGet packages:
Microsoft.Build.Locator
CliWrap
Add the following using directives (ex: Form1.cs)
using System.Diagnostics;
using System.IO;
using Microsoft.Build.Locator;
using CliWrap;
using CliWrap.Buffered;
Choose one of the options below (ie: Option A, Option B, Option C, Option D, or Option E):
Option A - ExecuteBufferedAsync:
private async Task RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
var result = await Cli.Wrap(msBuildFilename)
.WithArguments(arguments)
.WithWorkingDirectory(Path.GetDirectoryName(solutionFilename))
.ExecuteBufferedAsync();
Console.WriteLine(result.StandardOutput);
Console.WriteLine(result.StandardError);
}
Option B - ExecuteAsync:
private async Task RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
var stdOutBuffer = new StringBuilder();
var stdErrBuffer = new StringBuilder();
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
var result = await Cli.Wrap(msBuildFilename)
.WithArguments(arguments)
.WithWorkingDirectory(Path.GetDirectoryName(solutionFilename))
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
.ExecuteAsync();
// Access stdout & stderr buffered in-memory as strings
var stdOut = stdOutBuffer.ToString();
var stdErr = stdErrBuffer.ToString();
Console.WriteLine(stdOut);
Console.WriteLine(stdErr);
}
Option C - ExecuteAsync (Delegate):
Note: This option displays real-time output.
private async Task RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
var result = await Cli.Wrap(msBuildFilename)
.WithArguments(arguments)
.WithWorkingDirectory(Path.GetDirectoryName(solutionFilename))
.WithStandardOutputPipe(PipeTarget.ToDelegate(delegate (string msg)
{
Console.WriteLine(msg);
}))
.WithStandardErrorPipe(PipeTarget.ToDelegate(delegate (string msg)
{
Console.WriteLine(msg);
}))
.ExecuteAsync();
}
Option D - ExecuteAsync (Action/Delegate):
Note: This option displays real-time output.
private async Task RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
Action<string> handleStdOut = delegate (string msg)
{
Console.WriteLine(msg);
};
Action<string> handleStdErr = delegate (string msg)
{
Console.WriteLine(msg);
};
var result = await Cli.Wrap(msBuildFilename)
.WithArguments(arguments)
.WithWorkingDirectory(Path.GetDirectoryName(solutionFilename))
.WithStandardOutputPipe(PipeTarget.ToDelegate(handleStdOut))
.WithStandardErrorPipe(PipeTarget.ToDelegate(handleStdErr))
.ExecuteAsync();
}
Option E - ExecuteAsync (EventStream):
Note: This option displays real-time output.
Download/install NuGet package: System.Reactive.Linq
Add using directives:
using CliWrap.EventStream;
using System.Reactive.Linq;
private async Task RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
Command cmd = Cli.Wrap(msBuildFilename)
.WithArguments(arguments)
.WithWorkingDirectory(Path.GetDirectoryName(solutionFilename));
await cmd.Observe().ForEachAsync(cmdEvent =>
{
switch (cmdEvent)
{
case StartedCommandEvent started:
Console.WriteLine($"Process started; ID: {started.ProcessId}");
break;
case StandardOutputCommandEvent stdOut:
Console.WriteLine($"{stdOut.Text}");
break;
case StandardErrorCommandEvent stdErr:
Console.WriteLine($"{stdErr.Text}");
break;
case ExitedCommandEvent exited:
Console.WriteLine($"Process exited; Code: {exited.ExitCode}");
break;
}
});
}
(Optional) Add the following method overloads:
public enum VSVersionType
{
Latest,
Oldest
}
private async Task RunMSBuild(string solutionFilename, int vsVersionYear, string arguments = null)
{
//get Visual Studio instances
List<VisualStudioInstance> vsInstances = MSBuildLocator.QueryVisualStudioInstances().OrderBy(x => x.Version).ToList();
if (vsInstances != null && vsInstances.Count > 0)
{
//get MSBuild path
var msBuildPath = vsInstances.Where(x => x.Name.EndsWith(vsVersionYear.ToString())).Select(x => x.MSBuildPath).FirstOrDefault();
await RunMSBuild(solutionFilename, msBuildPath, arguments);
}
}
private async Task RunMSBuild(string solutionFilename, VSVersionType vsVersion, string arguments = null)
{
//get Visual Studio instances
List<VisualStudioInstance> vsInstances = MSBuildLocator.QueryVisualStudioInstances().OrderBy(x => x.Version).ToList();
if (vsInstances != null && vsInstances.Count > 0)
{
string msBuildPath = string.Empty;
if (vsVersion == VSVersionType.Latest)
msBuildPath = vsInstances[vsInstances.Count - 1].MSBuildPath;
else if (vsVersion == VSVersionType.Oldest)
msBuildPath = vsInstances[0].MSBuildPath;
await RunMSBuild(solutionFilename, msBuildPath, arguments);
}
}
Usage 1:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, 2017);
Usage 2:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, 2017, "-t:Clean;Compile");
Usage 3:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, VSVersionType.Oldest);
Usage 4:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, VSVersionType.Oldest, "-t:Clean;Compile");
Usage 5:
string msBuildPath = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.ProgramFilesX86)), @"Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin");
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, msBuildPath);
Usage 6:
string msBuildPath = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.ProgramFilesX86)), @"Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin");
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, msBuildPath, "-t:Clean;Compile"););
Option 2 (System.Diagnostics.Process)
Download/install NuGet package: Microsoft.Build.Locator
Add the following using directives (ex: Form1.cs)
using System.Diagnostics;
using System.IO;
using Microsoft.Build.Locator;
public enum VSVersionType
{
Latest,
Oldest
}
private void RunMSBuild(string solutionFilename, int vsVersionYear, string arguments = null)
{
//get Visual Studio instances
List<VisualStudioInstance> vsInstances = MSBuildLocator.QueryVisualStudioInstances().OrderBy(x => x.Version).ToList();
if (vsInstances != null && vsInstances.Count > 0)
{
//get MSBuild path
var msBuildPath = vsInstances.Where(x => x.Name.EndsWith(vsVersionYear.ToString())).Select(x => x.MSBuildPath).FirstOrDefault();
RunMSBuild(solutionFilename, msBuildPath, arguments);
}
}
private void RunMSBuild(string solutionFilename, VSVersionType vsVersion, string arguments = null)
{
//get Visual Studio instances
List<VisualStudioInstance> vsInstances = MSBuildLocator.QueryVisualStudioInstances().OrderBy(x => x.Version).ToList();
if (vsInstances != null && vsInstances.Count > 0)
{
string msBuildPath = string.Empty;
if (vsVersion == VSVersionType.Latest)
msBuildPath = vsInstances[vsInstances.Count - 1].MSBuildPath;
else if (vsVersion == VSVersionType.Oldest)
msBuildPath = vsInstances[0].MSBuildPath;
RunMSBuild(solutionFilename, msBuildPath, arguments);
}
}
private void RunMSBuild(string solutionFilename, string msBuildPath, string arguments = null)
{
//get tools path for newest Visual Studio version
string msBuildFilename = Path.Combine(msBuildPath, "MSBuild.exe");
if (!File.Exists(msBuildFilename))
throw new Exception($"Error: MSBuild.exe not found ({msBuildFilename})");
ProcessStartInfo startInfo = new ProcessStartInfo(msBuildFilename)
{
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
//set value
startInfo.Arguments = arguments;
if (!String.IsNullOrEmpty(arguments))
{
if (!arguments.Contains(Path.GetFileNameWithoutExtension(solutionFilename)))
{
arguments += $" \"{solutionFilename}\"";
}
}
else
{
arguments = $" \"{solutionFilename}\"";
}
Debug.WriteLine($"arguments: {arguments}");
//set value
startInfo.WorkingDirectory = Path.GetDirectoryName(solutionFilename);
using (Process p = new Process() { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
//start
p.Start();
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
//waits until the process is finished before continuing
p.WaitForExit();
p.CancelErrorRead(); //cancel async reading for standard error
p.CancelOutputRead(); //cancel async reading for standard output
}
}
Usage 1:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, 2017);
Usage 2:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, 2017, "-t:Clean;Compile");
Usage 3:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, VSVersionType.Oldest);
Usage 4:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, VSVersionType.Oldest, "-t:Clean;Compile");
Usage 5:
string msBuildPath = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.ProgramFilesX86)), @"Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin");
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, msBuildPath);
Usage 6:
string msBuildPath = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.ProgramFilesX86)), @"Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin");
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, msBuildPath, "-t:Clean;Compile"););
Note: When using the Developer Command Prompt, a batch file ("VsDevCmd.bat") is used to set environment variables. One can see this by:
- Click
Windows Start menu
- Right-click
Developer Command Prompt...
- Select
More
- Select
Open file location
In the folder that opens:
- Right-click
Developer Command Prompt... link.
- Select
Properties
- Select
Shortcut tab
Look at value of Target which shows the batch file that is used to set the environment variables.
Resources:
Additional Resources: