diff options
Diffstat (limited to 'src/WixBuildTools.TestSupport/MsbuildRunner.cs')
-rw-r--r-- | src/WixBuildTools.TestSupport/MsbuildRunner.cs | 205 |
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 | } |