Add ability to run 64-bit msbuild.
This commit is contained in:
Родитель
2ab17d9c52
Коммит
223b284595
|
@ -18,7 +18,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.TestSupport
|
||||
{
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
public abstract class ExternalExecutable
|
||||
{
|
||||
private readonly string exePath;
|
||||
|
||||
protected ExternalExecutable(string exePath)
|
||||
{
|
||||
this.exePath = exePath;
|
||||
}
|
||||
|
||||
protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo(this.exePath, args)
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath),
|
||||
};
|
||||
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
// This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written.
|
||||
// If the process is simultaneously writing to both streams, this is impossible to do anyway.
|
||||
var standardOutput = new ConcurrentQueue<string>();
|
||||
var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue<string>();
|
||||
|
||||
process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } };
|
||||
process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } };
|
||||
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
return new ExternalExecutableResult
|
||||
{
|
||||
ExitCode = process.ExitCode,
|
||||
StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
|
||||
StandardOutput = standardOutput.ToArray(),
|
||||
StartInfo = startInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes.
|
||||
internal static string CombineArguments(IEnumerable<string> arguments)
|
||||
{
|
||||
if (arguments == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var arg in arguments)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
if (arg.IndexOf(' ') > -1)
|
||||
{
|
||||
sb.Append("\"");
|
||||
sb.Append(arg);
|
||||
sb.Append("\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.TestSupport
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
||||
public class ExternalExecutableResult
|
||||
{
|
||||
public int ExitCode { get; set; }
|
||||
|
||||
public string[] StandardError { get; set; }
|
||||
|
||||
public string[] StandardOutput { get; set; }
|
||||
|
||||
public ProcessStartInfo StartInfo { get; set; }
|
||||
}
|
||||
}
|
|
@ -4,96 +4,148 @@ namespace WixBuildTools.TestSupport
|
|||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
public static class MsbuildRunner
|
||||
public class MsbuildRunner : ExternalExecutable
|
||||
{
|
||||
private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe";
|
||||
private static readonly string[] VswhereFindArguments = new[] { "-property", "installationPath" };
|
||||
private static readonly string VswhereFindArguments = "-property installationPath";
|
||||
private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe";
|
||||
private static readonly string Msbuild16RelativePath = @"MSBuild\Current\Bin\MSBuild.exe";
|
||||
private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe";
|
||||
private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe";
|
||||
private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe";
|
||||
|
||||
private static readonly object InitLock = new object();
|
||||
|
||||
private static string Msbuild15Path;
|
||||
private static string Msbuild16Path;
|
||||
private static bool Initialized;
|
||||
private static MsbuildRunner Msbuild15Runner;
|
||||
private static MsbuildRunner Msbuild15Runner64;
|
||||
private static MsbuildRunner MsbuildCurrentRunner;
|
||||
private static MsbuildRunner MsbuildCurrentRunner64;
|
||||
|
||||
public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null) => InitAndExecute(String.Empty, projectPath, arguments);
|
||||
public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) =>
|
||||
InitAndExecute(String.Empty, projectPath, arguments, x64);
|
||||
|
||||
public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null) => InitAndExecute("15", projectPath, arguments);
|
||||
public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) =>
|
||||
InitAndExecute("15", projectPath, arguments, x64);
|
||||
|
||||
public static MsbuildRunnerResult ExecuteWithMsbuild16(string projectPath, string[] arguments = null) => InitAndExecute("16", projectPath, arguments);
|
||||
public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) =>
|
||||
InitAndExecute("Current", projectPath, arguments, x64);
|
||||
|
||||
private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments)
|
||||
private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64)
|
||||
{
|
||||
lock (InitLock)
|
||||
{
|
||||
if (Msbuild15Path == null && Msbuild16Path == null)
|
||||
if (!Initialized)
|
||||
{
|
||||
var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath);
|
||||
if (!File.Exists(vswherePath))
|
||||
Initialized = true;
|
||||
var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true);
|
||||
if (vswhereResult.ExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}");
|
||||
throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}");
|
||||
}
|
||||
|
||||
var result = RunProcessCaptureOutput(vswherePath, VswhereFindArguments);
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {result.ExitCode}");
|
||||
}
|
||||
string msbuild15Path = null;
|
||||
string msbuild15Path64 = null;
|
||||
string msbuildCurrentPath = null;
|
||||
string msbuildCurrentPath64 = null;
|
||||
|
||||
Msbuild15Path = String.Empty;
|
||||
Msbuild16Path = String.Empty;
|
||||
|
||||
foreach (var installPath in result.Output)
|
||||
foreach (var installPath in vswhereResult.StandardOutput)
|
||||
{
|
||||
if (String.IsNullOrEmpty(Msbuild16Path))
|
||||
if (msbuildCurrentPath == null)
|
||||
{
|
||||
var path = Path.Combine(installPath, Msbuild16RelativePath);
|
||||
var path = Path.Combine(installPath, MsbuildCurrentRelativePath);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Msbuild16Path = path;
|
||||
msbuildCurrentPath = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(Msbuild15Path))
|
||||
if (msbuildCurrentPath64 == null)
|
||||
{
|
||||
var path = Path.Combine(installPath, MsbuildCurrentRelativePath64);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
msbuildCurrentPath64 = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (msbuild15Path == null)
|
||||
{
|
||||
var path = Path.Combine(installPath, Msbuild15RelativePath);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Msbuild15Path = path;
|
||||
msbuild15Path = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (msbuild15Path64 == null)
|
||||
{
|
||||
var path = Path.Combine(installPath, Msbuild15RelativePath64);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
msbuild15Path64 = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msbuildCurrentPath != null)
|
||||
{
|
||||
MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath);
|
||||
}
|
||||
|
||||
if (msbuildCurrentPath64 != null)
|
||||
{
|
||||
MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64);
|
||||
}
|
||||
|
||||
if (msbuild15Path != null)
|
||||
{
|
||||
Msbuild15Runner = new MsbuildRunner(msbuild15Path);
|
||||
}
|
||||
|
||||
if (msbuild15Path64 != null)
|
||||
{
|
||||
Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var msbuildPath = !String.IsNullOrEmpty(Msbuild15Path) ? Msbuild15Path : Msbuild16Path;
|
||||
|
||||
if (msbuildVersion == "15")
|
||||
MsbuildRunner runner;
|
||||
switch (msbuildVersion)
|
||||
{
|
||||
msbuildPath = Msbuild15Path;
|
||||
}
|
||||
else if (msbuildVersion == "16")
|
||||
{
|
||||
msbuildPath = Msbuild16Path;
|
||||
case "15":
|
||||
{
|
||||
runner = x64 ? Msbuild15Runner64 : Msbuild15Runner;
|
||||
break;
|
||||
}
|
||||
case "Current":
|
||||
{
|
||||
runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64
|
||||
: MsbuildCurrentRunner ?? Msbuild15Runner;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ExecuteCore(msbuildVersion, msbuildPath, projectPath, arguments);
|
||||
if (runner == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}");
|
||||
}
|
||||
|
||||
return runner.ExecuteCore(projectPath, arguments);
|
||||
}
|
||||
|
||||
private static MsbuildRunnerResult ExecuteCore(string msbuildVersion, string msbuildPath, string projectPath, string[] arguments)
|
||||
{
|
||||
if (String.IsNullOrEmpty(msbuildPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to find an installed MSBuild{msbuildVersion}");
|
||||
}
|
||||
private MsbuildRunner(string exePath) : base(exePath) { }
|
||||
|
||||
private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments)
|
||||
{
|
||||
var total = new List<string>
|
||||
{
|
||||
projectPath
|
||||
projectPath,
|
||||
};
|
||||
|
||||
if (arguments != null)
|
||||
|
@ -101,69 +153,16 @@ namespace WixBuildTools.TestSupport
|
|||
total.AddRange(arguments);
|
||||
}
|
||||
|
||||
var args = CombineArguments(total);
|
||||
var mergeErrorIntoOutput = true;
|
||||
var workingFolder = Path.GetDirectoryName(projectPath);
|
||||
return RunProcessCaptureOutput(msbuildPath, total.ToArray(), workingFolder);
|
||||
}
|
||||
var result = this.Run(args, mergeErrorIntoOutput, workingFolder);
|
||||
|
||||
private static MsbuildRunnerResult RunProcessCaptureOutput(string executablePath, string[] arguments = null, string workingFolder = null)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo(executablePath)
|
||||
return new MsbuildRunnerResult
|
||||
{
|
||||
Arguments = CombineArguments(arguments),
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = workingFolder,
|
||||
ExitCode = result.ExitCode,
|
||||
Output = result.StandardOutput,
|
||||
};
|
||||
|
||||
var exitCode = 0;
|
||||
var output = new List<string>();
|
||||
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process.OutputDataReceived += (s, e) => { if (e.Data != null) { output.Add(e.Data); } };
|
||||
process.ErrorDataReceived += (s, e) => { if (e.Data != null) { output.Add(e.Data); } };
|
||||
|
||||
process.BeginErrorReadLine();
|
||||
process.BeginOutputReadLine();
|
||||
|
||||
process.WaitForExit();
|
||||
exitCode = process.ExitCode;
|
||||
}
|
||||
|
||||
return new MsbuildRunnerResult { ExitCode = exitCode, Output = output.ToArray() };
|
||||
}
|
||||
|
||||
private static string CombineArguments(string[] arguments)
|
||||
{
|
||||
if (arguments == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var arg in arguments)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
if (arg.IndexOf(' ') > -1)
|
||||
{
|
||||
sb.Append("\"");
|
||||
sb.Append(arg);
|
||||
sb.Append("\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.TestSupport
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
public class VswhereRunner : ExternalExecutable
|
||||
{
|
||||
private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe";
|
||||
|
||||
private static readonly object InitLock = new object();
|
||||
private static bool Initialized;
|
||||
private static VswhereRunner Instance;
|
||||
|
||||
public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) =>
|
||||
InitAndExecute(args, mergeErrorIntoOutput);
|
||||
|
||||
private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput)
|
||||
{
|
||||
lock (InitLock)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
Initialized = true;
|
||||
var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath);
|
||||
if (!File.Exists(vswherePath))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}");
|
||||
}
|
||||
|
||||
Instance = new VswhereRunner(vswherePath);
|
||||
}
|
||||
}
|
||||
|
||||
return Instance.Run(args, mergeErrorIntoOutput);
|
||||
}
|
||||
|
||||
private VswhereRunner(string exePath) : base(exePath) { }
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05 " PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче