aboutsummaryrefslogtreecommitdiff
path: root/src/WixBuildTools.TestSupport/MsbuildRunner.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixBuildTools.TestSupport/MsbuildRunner.cs')
-rw-r--r--src/WixBuildTools.TestSupport/MsbuildRunner.cs205
1 files changed, 102 insertions, 103 deletions
diff --git a/src/WixBuildTools.TestSupport/MsbuildRunner.cs b/src/WixBuildTools.TestSupport/MsbuildRunner.cs
index b38387a9..35e53de6 100644
--- a/src/WixBuildTools.TestSupport/MsbuildRunner.cs
+++ b/src/WixBuildTools.TestSupport/MsbuildRunner.cs
@@ -4,96 +4,148 @@ namespace WixBuildTools.TestSupport
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO; 7 using System.IO;
9 using System.Text;
10 8
11 public static class MsbuildRunner 9 public class MsbuildRunner : ExternalExecutable
12 { 10 {
13 private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe"; 11 private static readonly string VswhereFindArguments = "-property installationPath";
14 private static readonly string[] VswhereFindArguments = new[] { "-property", "installationPath" };
15 private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe"; 12 private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe";
16 private static readonly string Msbuild16RelativePath = @"MSBuild\Current\Bin\MSBuild.exe"; 13 private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe";
14 private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe";
15 private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe";
17 16
18 private static readonly object InitLock = new object(); 17 private static readonly object InitLock = new object();
19 18
20 private static string Msbuild15Path; 19 private static bool Initialized;
21 private static string Msbuild16Path; 20 private static MsbuildRunner Msbuild15Runner;
21 private static MsbuildRunner Msbuild15Runner64;
22 private static MsbuildRunner MsbuildCurrentRunner;
23 private static MsbuildRunner MsbuildCurrentRunner64;
22 24
23 public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null) => InitAndExecute(String.Empty, projectPath, arguments); 25 public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) =>
26 InitAndExecute(String.Empty, projectPath, arguments, x64);
24 27
25 public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null) => InitAndExecute("15", projectPath, arguments); 28 public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) =>
29 InitAndExecute("15", projectPath, arguments, x64);
26 30
27 public static MsbuildRunnerResult ExecuteWithMsbuild16(string projectPath, string[] arguments = null) => InitAndExecute("16", projectPath, arguments); 31 public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) =>
32 InitAndExecute("Current", projectPath, arguments, x64);
28 33
29 private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments) 34 private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64)
30 { 35 {
31 lock (InitLock) 36 lock (InitLock)
32 { 37 {
33 if (Msbuild15Path == null && Msbuild16Path == null) 38 if (!Initialized)
34 { 39 {
35 var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath); 40 Initialized = true;
36 if (!File.Exists(vswherePath)) 41 var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true);
42 if (vswhereResult.ExitCode != 0)
37 { 43 {
38 throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}"); 44 throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}");
39 } 45 }
40 46
41 var result = RunProcessCaptureOutput(vswherePath, VswhereFindArguments); 47 string msbuild15Path = null;
42 if (result.ExitCode != 0) 48 string msbuild15Path64 = null;
43 { 49 string msbuildCurrentPath = null;
44 throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {result.ExitCode}"); 50 string msbuildCurrentPath64 = null;
45 }
46
47 Msbuild15Path = String.Empty;
48 Msbuild16Path = String.Empty;
49 51
50 foreach (var installPath in result.Output) 52 foreach (var installPath in vswhereResult.StandardOutput)
51 { 53 {
52 if (String.IsNullOrEmpty(Msbuild16Path)) 54 if (msbuildCurrentPath == null)
55 {
56 var path = Path.Combine(installPath, MsbuildCurrentRelativePath);
57 if (File.Exists(path))
58 {
59 msbuildCurrentPath = path;
60 }
61 }
62
63 if (msbuildCurrentPath64 == null)
53 { 64 {
54 var path = Path.Combine(installPath, Msbuild16RelativePath); 65 var path = Path.Combine(installPath, MsbuildCurrentRelativePath64);
55 if (File.Exists(path)) 66 if (File.Exists(path))
56 { 67 {
57 Msbuild16Path = path; 68 msbuildCurrentPath64 = path;
58 } 69 }
59 } 70 }
60 71
61 if (String.IsNullOrEmpty(Msbuild15Path)) 72 if (msbuild15Path == null)
62 { 73 {
63 var path = Path.Combine(installPath, Msbuild15RelativePath); 74 var path = Path.Combine(installPath, Msbuild15RelativePath);
64 if (File.Exists(path)) 75 if (File.Exists(path))
65 { 76 {
66 Msbuild15Path = path; 77 msbuild15Path = path;
78 }
79 }
80
81 if (msbuild15Path64 == null)
82 {
83 var path = Path.Combine(installPath, Msbuild15RelativePath64);
84 if (File.Exists(path))
85 {
86 msbuild15Path64 = path;
67 } 87 }
68 } 88 }
69 } 89 }
90
91 if (msbuildCurrentPath != null)
92 {
93 MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath);
94 }
95
96 if (msbuildCurrentPath64 != null)
97 {
98 MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64);
99 }
100
101 if (msbuild15Path != null)
102 {
103 Msbuild15Runner = new MsbuildRunner(msbuild15Path);
104 }
105
106 if (msbuild15Path64 != null)
107 {
108 Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64);
109 }
70 } 110 }
71 } 111 }
72 112
73 var msbuildPath = !String.IsNullOrEmpty(Msbuild15Path) ? Msbuild15Path : Msbuild16Path; 113 MsbuildRunner runner;
74 114 switch (msbuildVersion)
75 if (msbuildVersion == "15")
76 { 115 {
77 msbuildPath = Msbuild15Path; 116 case "15":
117 {
118 runner = x64 ? Msbuild15Runner64 : Msbuild15Runner;
119 break;
120 }
121 case "Current":
122 {
123 runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner;
124 break;
125 }
126 default:
127 {
128 runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64
129 : MsbuildCurrentRunner ?? Msbuild15Runner;
130 break;
131 }
78 } 132 }
79 else if (msbuildVersion == "16") 133
134 if (runner == null)
80 { 135 {
81 msbuildPath = Msbuild16Path; 136 throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}");
82 } 137 }
83 138
84 return ExecuteCore(msbuildVersion, msbuildPath, projectPath, arguments); 139 return runner.ExecuteCore(projectPath, arguments);
85 } 140 }
86 141
87 private static MsbuildRunnerResult ExecuteCore(string msbuildVersion, string msbuildPath, string projectPath, string[] arguments) 142 private MsbuildRunner(string exePath) : base(exePath) { }
88 {
89 if (String.IsNullOrEmpty(msbuildPath))
90 {
91 throw new InvalidOperationException($"Failed to find an installed MSBuild{msbuildVersion}");
92 }
93 143
144 private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments)
145 {
94 var total = new List<string> 146 var total = new List<string>
95 { 147 {
96 projectPath 148 projectPath,
97 }; 149 };
98 150
99 if (arguments != null) 151 if (arguments != null)
@@ -101,69 +153,16 @@ namespace WixBuildTools.TestSupport
101 total.AddRange(arguments); 153 total.AddRange(arguments);
102 } 154 }
103 155
156 var args = CombineArguments(total);
157 var mergeErrorIntoOutput = true;
104 var workingFolder = Path.GetDirectoryName(projectPath); 158 var workingFolder = Path.GetDirectoryName(projectPath);
105 return RunProcessCaptureOutput(msbuildPath, total.ToArray(), workingFolder); 159 var result = this.Run(args, mergeErrorIntoOutput, workingFolder);
106 }
107 160
108 private static MsbuildRunnerResult RunProcessCaptureOutput(string executablePath, string[] arguments = null, string workingFolder = null) 161 return new MsbuildRunnerResult
109 {
110 var startInfo = new ProcessStartInfo(executablePath)
111 { 162 {
112 Arguments = CombineArguments(arguments), 163 ExitCode = result.ExitCode,
113 CreateNoWindow = true, 164 Output = result.StandardOutput,
114 RedirectStandardError = true,
115 RedirectStandardOutput = true,
116 UseShellExecute = false,
117 WorkingDirectory = workingFolder,
118 }; 165 };
119
120 var exitCode = 0;
121 var output = new List<string>();
122
123 using (var process = Process.Start(startInfo))
124 {
125 process.OutputDataReceived += (s, e) => { if (e.Data != null) { output.Add(e.Data); } };
126 process.ErrorDataReceived += (s, e) => { if (e.Data != null) { output.Add(e.Data); } };
127
128 process.BeginErrorReadLine();
129 process.BeginOutputReadLine();
130
131 process.WaitForExit();
132 exitCode = process.ExitCode;
133 }
134
135 return new MsbuildRunnerResult { ExitCode = exitCode, Output = output.ToArray() };
136 }
137
138 private static string CombineArguments(string[] arguments)
139 {
140 if (arguments == null)
141 {
142 return null;
143 }
144
145 var sb = new StringBuilder();
146
147 foreach (var arg in arguments)
148 {
149 if (sb.Length > 0)
150 {
151 sb.Append(' ');
152 }
153
154 if (arg.IndexOf(' ') > -1)
155 {
156 sb.Append("\"");
157 sb.Append(arg);
158 sb.Append("\"");
159 }
160 else
161 {
162 sb.Append(arg);
163 }
164 }
165
166 return sb.ToString();
167 } 166 }
168 } 167 }
169} 168}