Я пытаюсь запустить msbuild /t:scmclean
из моего консольного приложения C#
, но не могу. Это полностью работает, если я запускаю его из командной строки Visual Studio 2017, но терпит неудачу при выполнении из моего консольного приложения C#
.
Моя целевая папка, в которой я запускаю эту команду: D:/Git/abc/Build/Tools
, эта команда внутренне вызывает файл csproj.
Что я пробовал:
var result = await Cli.Wrap("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\MSBuild\\15.0\\Bin\\MSBuild.exe")
.WithArguments("/t:scmclean")
.WithWorkingDirectory("D:\\git\\abc\\Build\\Tools")
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutCO))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrCO))
.ExecuteBufferedAsync();
var stdOut1 = stdOutCO.ToString();
var stdErr1 = stdErrCO.ToString();
Console.WriteLine("Build Info:");
Console.WriteLine(stdOut1);
Console.WriteLine(stdErr1);
Это дает отказ в доступе к ошибке, однако, когда я проверяю пользователя с помощью команды
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
он отображается как супер пользователь. Еще одна вещь, которую я пробовал, запускается после установки местоположения в мой текущий каталог:
Process.Start("C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\MSBuild\\15.0\\Bin\\MSBuild.exe", "/t:scmclean");
это также кажется неудачным. Также мне нужно знать, как записать вывод процесса в консоль.
MSBUILD: ошибка MSB1003: укажите проект или файл решения. текущий рабочий каталог не содержит файла проекта или решения.
Я не уверен, что такое scmclean
, потому что я не мог найти никакой информации об этом. Тем не менее, попробуйте следующее:
Загрузите/установите пакеты NuGet:
Microsoft.Build.Locator
CliWrap
Добавьте следующие директивы using (например, Form1.cs)
using System.Diagnostics;
using System.IO;
using Microsoft.Build.Locator;
using CliWrap;
using CliWrap.Buffered;
Выберите один из приведенных ниже вариантов (например, вариант A, вариант B, вариант C, вариант D или вариант E):
Вариант 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);
}
Вариант Б — 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);
}
Вариант C — ExecuteAsync (делегат):
Примечание. Этот параметр отображает выходные данные в реальном времени.
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();
}
Вариант D — ExecuteAsync (действие/делегат):
Примечание. Этот параметр отображает выходные данные в реальном времени.
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();
}
Вариант E — ExecuteAsync (EventStream):
Примечание. Этот параметр отображает выходные данные в реальном времени.
Загрузите/установите пакет NuGet: System.Reactive.Linq
Добавьте использование директив:
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;
}
});
}
(Необязательно) Добавьте следующие перегрузки методов:
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);
}
}
Использование 1:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, 2017);
Использование 2:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, 2017, "-t:Clean;Compile");
Использование 3:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
await RunMSBuild(solutionFilename, VSVersionType.Oldest);
Использование 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");
Использование 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);
Использование 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"););
Загрузите/установите пакет NuGet: Microsoft.Build.Locator
Добавьте следующие директивы using (например, 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
}
}
Использование 1:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, 2017);
Использование 2:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, 2017, "-t:Clean;Compile");
Использование 3:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, VSVersionType.Oldest);
Использование 4:
string solutionFilename = Path.Combine(Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "Visual Studio 2017", "Projects", "MyAmazingApp", "MyAmazingApp.sln"));
RunMSBuild(solutionFilename, VSVersionType.Oldest, "-t:Clean;Compile");
Использование 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);
Использование 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"););
Примечание. При использовании Developer Command Prompt
для установки переменных среды используется пакетный файл («VsDevCmd.bat»). Это можно увидеть по:
Windows Start menu
Developer Command Prompt...
More
Open file location
В открывшейся папке:
Developer Command Prompt...
.Properties
Shortcut
Посмотрите на значение Target
, которое показывает пакетный файл, который используется для установки переменных среды.
Ресурсы:
Дополнительные ресурсы:
@JonathanDodds: я не был знаком с Microsoft.Build.Locator. После просмотра предоставленного URL-адреса я обновил код.
То, что я пытаюсь сделать, это запустить t:scmclean внутри папки, D:/Git/abc/build/tools с пропуском msbuid.exe , который я должен получить из системного пути, установленного для msbuil.exe из консольное приложение С#. Может быть, я не могу объяснить, но все, что мне нужно сделать, это перейти в каталог и запустить t:scmclean , который отлично работает из командной строки, но не через мой код С#.
@madhurima Переключатель /t
(или /target
) указывает набор целей для запуска в указанном файле проекта. Если файл проекта не указан (как в командной строке), MSBuild ищет в текущем рабочем каталоге файл с расширением, которое заканчивается на proj
(например, в проектах C# используется .csproj
). Гарантированно ли D:/Git/abc/Build/Tools
содержит файл *.*proj
? Вы получаете сообщение об ошибке, потому что файл не существует?
@madhurima: я не знаю, что такое t:scmclean
— я не смог найти никакой информации об этом. Однако я обновил сообщение, включив в него использование с пакетом NuGet CliWrap
. Весь код в посте проверен.
@ user09938 /t:scmclean
работает с пользовательской письменной целью с именем scmclean
(я полагаю, это «scm clean»).
Я настоятельно рекомендую использовать пакет Microsoft.Build.Locator, даже если вы планируете создать новый процесс и не встраивать механизм сборки.