Add ability to run 64-bit msbuild.
Sean Hall committed
May 30, 2020 at 12:16 UTC
223b2845955d4d6604cfd014cf9aa536ca1eb0a2
7 files changed
+251
-106
src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
+1
-1
@@ -18,7 +18,7 @@
18
</ItemGroup>
19
20
<ItemGroup>
21
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" />
21
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
22
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
23
</ItemGroup>
24
src/WixBuildTools.TestSupport/ExternalExecutable.cs
new
+88
@@ -0,0 +1,88 @@
1
+// 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.
2
+
3
+namespace WixBuildTools.TestSupport
4
+{
5
+ using System.Collections.Concurrent;
6
+ using System.Collections.Generic;
7
+ using System.Diagnostics;
8
+ using System.IO;
9
+ using System.Text;
10
+
11
+ public abstract class ExternalExecutable
12
+ {
13
+ private readonly string exePath;
14
+
15
+ protected ExternalExecutable(string exePath)
16
+ {
17
+ this.exePath = exePath;
18
+ }
19
+
20
+ protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
21
+ {
22
+ var startInfo = new ProcessStartInfo(this.exePath, args)
23
+ {
24
+ CreateNoWindow = true,
25
+ RedirectStandardError = true,
26
+ RedirectStandardOutput = true,
27
+ UseShellExecute = false,
28
+ WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath),
29
+ };
30
+
31
+ using (var process = Process.Start(startInfo))
32
+ {
33
+ // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written.
34
+ // If the process is simultaneously writing to both streams, this is impossible to do anyway.
35
+ var standardOutput = new ConcurrentQueue<string>();
36
+ var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue<string>();
37
+
38
+ process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } };
39
+ process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } };
40
+
41
+ process.BeginErrorReadLine();
42
+ process.BeginOutputReadLine();
43
+
44
+ process.WaitForExit();
45
+
46
+ return new ExternalExecutableResult
47
+ {
48
+ ExitCode = process.ExitCode,
49
+ StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
50
+ StandardOutput = standardOutput.ToArray(),
51
+ StartInfo = startInfo,
52
+ };
53
+ }
54
+ }
55
+
56
+ // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes.
57
+ internal static string CombineArguments(IEnumerable<string> arguments)
58
+ {
59
+ if (arguments == null)
60
+ {
61
+ return null;
62
+ }
63
+
64
+ var sb = new StringBuilder();
65
+
66
+ foreach (var arg in arguments)
67
+ {
68
+ if (sb.Length > 0)
69
+ {
70
+ sb.Append(' ');
71
+ }
72
+
73
+ if (arg.IndexOf(' ') > -1)
74
+ {
75
+ sb.Append("\"");
76
+ sb.Append(arg);
77
+ sb.Append("\"");
78
+ }
79
+ else
80
+ {
81
+ sb.Append(arg);
82
+ }
83
+ }
84
+
85
+ return sb.ToString();
86
+ }
87
+ }
88
+}
src/WixBuildTools.TestSupport/ExternalExecutableResult.cs
new
+17
@@ -0,0 +1,17 @@
1
+// 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.
2
+
3
+namespace WixBuildTools.TestSupport
4
+{
5
+ using System.Diagnostics;
6
+
7
+ public class ExternalExecutableResult
8
+ {
9
+ public int ExitCode { get; set; }
10
+
11
+ public string[] StandardError { get; set; }
12
+
13
+ public string[] StandardOutput { get; set; }
14
+
15
+ public ProcessStartInfo StartInfo { get; set; }
16
+ }
17
+}
src/WixBuildTools.TestSupport/MsbuildRunner.cs
+102
-103
@@ -4,96 +4,148 @@ namespace WixBuildTools.TestSupport
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.Diagnostics;
7
using System.IO;
9
- using System.Text;
8
11
- public static class MsbuildRunner
9
+ public class MsbuildRunner : ExternalExecutable
10
{
13
- private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe";
14
- private static readonly string[] VswhereFindArguments = new[] { "-property", "installationPath" };
11
+ private static readonly string VswhereFindArguments = "-property installationPath";
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";
16
17
private static readonly object InitLock = new object();
18
20
- private static string Msbuild15Path;
21
- private static string Msbuild16Path;
19
+ private static bool Initialized;
20
+ private static MsbuildRunner Msbuild15Runner;
21
+ private static MsbuildRunner Msbuild15Runner64;
22
+ private static MsbuildRunner MsbuildCurrentRunner;
23
+ private static MsbuildRunner MsbuildCurrentRunner64;
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);
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);
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);
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)
35
{
36
lock (InitLock)
37
{
33
- if (Msbuild15Path == null && Msbuild16Path == null)
38
+ if (!Initialized)
39
{
35
- var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath);
36
- if (!File.Exists(vswherePath))
40
+ Initialized = true;
41
+ var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true);
42
+ if (vswhereResult.ExitCode != 0)
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)}");
45
}
46
41
- var result = RunProcessCaptureOutput(vswherePath, VswhereFindArguments);
42
- if (result.ExitCode != 0)
43
- {
44
- throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {result.ExitCode}");
45
- }
46
-
47
- Msbuild15Path = String.Empty;
48
- Msbuild16Path = String.Empty;
47
+ string msbuild15Path = null;
48
+ string msbuild15Path64 = null;
49
+ string msbuildCurrentPath = null;
50
+ string msbuildCurrentPath64 = null;
51
50
- foreach (var installPath in result.Output)
52
+ foreach (var installPath in vswhereResult.StandardOutput)
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)
64
{
54
- var path = Path.Combine(installPath, Msbuild16RelativePath);
65
+ var path = Path.Combine(installPath, MsbuildCurrentRelativePath64);
66
if (File.Exists(path))
67
{
57
- Msbuild16Path = path;
68
+ msbuildCurrentPath64 = path;
69
}
70
}
71
61
- if (String.IsNullOrEmpty(Msbuild15Path))
72
+ if (msbuild15Path == null)
73
{
74
var path = Path.Combine(installPath, Msbuild15RelativePath);
75
if (File.Exists(path))
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;
87
}
88
}
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
+ }
110
}
111
}
112
73
- var msbuildPath = !String.IsNullOrEmpty(Msbuild15Path) ? Msbuild15Path : Msbuild16Path;
74
-
75
- if (msbuildVersion == "15")
113
+ MsbuildRunner runner;
114
+ switch (msbuildVersion)
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
+ }
132
}
79
- else if (msbuildVersion == "16")
133
+
134
+ if (runner == null)
135
{
81
- msbuildPath = Msbuild16Path;
136
+ throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}");
137
}
138
84
- return ExecuteCore(msbuildVersion, msbuildPath, projectPath, arguments);
139
+ return runner.ExecuteCore(projectPath, arguments);
140
}
141
87
- private static MsbuildRunnerResult ExecuteCore(string msbuildVersion, string msbuildPath, string projectPath, string[] arguments)
88
- {
89
- if (String.IsNullOrEmpty(msbuildPath))
90
- {
91
- throw new InvalidOperationException($"Failed to find an installed MSBuild{msbuildVersion}");
92
- }
142
+ private MsbuildRunner(string exePath) : base(exePath) { }
143
144
+ private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments)
145
+ {
146
var total = new List<string>
147
{
96
- projectPath
148
+ projectPath,
149
};
150
151
if (arguments != null)
@@ -101,69 +153,16 @@ namespace WixBuildTools.TestSupport
153
total.AddRange(arguments);
154
}
155
156
+ var args = CombineArguments(total);
157
+ var mergeErrorIntoOutput = true;
158
var workingFolder = Path.GetDirectoryName(projectPath);
105
- return RunProcessCaptureOutput(msbuildPath, total.ToArray(), workingFolder);
106
- }
159
+ var result = this.Run(args, mergeErrorIntoOutput, workingFolder);
160
108
- private static MsbuildRunnerResult RunProcessCaptureOutput(string executablePath, string[] arguments = null, string workingFolder = null)
109
- {
110
- var startInfo = new ProcessStartInfo(executablePath)
161
+ return new MsbuildRunnerResult
162
{
112
- Arguments = CombineArguments(arguments),
113
- CreateNoWindow = true,
114
- RedirectStandardError = true,
115
- RedirectStandardOutput = true,
116
- UseShellExecute = false,
117
- WorkingDirectory = workingFolder,
163
+ ExitCode = result.ExitCode,
164
+ Output = result.StandardOutput,
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();
166
}
167
}
168
}
src/WixBuildTools.TestSupport/VswhereRunner.cs
new
+41
@@ -0,0 +1,41 @@
1
+// 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.
2
+
3
+namespace WixBuildTools.TestSupport
4
+{
5
+ using System;
6
+ using System.IO;
7
+
8
+ public class VswhereRunner : ExternalExecutable
9
+ {
10
+ private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe";
11
+
12
+ private static readonly object InitLock = new object();
13
+ private static bool Initialized;
14
+ private static VswhereRunner Instance;
15
+
16
+ public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) =>
17
+ InitAndExecute(args, mergeErrorIntoOutput);
18
+
19
+ private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput)
20
+ {
21
+ lock (InitLock)
22
+ {
23
+ if (!Initialized)
24
+ {
25
+ Initialized = true;
26
+ var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath);
27
+ if (!File.Exists(vswherePath))
28
+ {
29
+ throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}");
30
+ }
31
+
32
+ Instance = new VswhereRunner(vswherePath);
33
+ }
34
+ }
35
+
36
+ return Instance.Run(args, mergeErrorIntoOutput);
37
+ }
38
+
39
+ private VswhereRunner(string exePath) : base(exePath) { }
40
+ }
41
+}
src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
+1
-1
@@ -18,7 +18,7 @@
18
</ItemGroup>
19
20
<ItemGroup>
21
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05 " PrivateAssets="All" />
21
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
22
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
23
</ItemGroup>
24
src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
+1
-1
@@ -19,7 +19,7 @@
19
</ItemGroup>
20
21
<ItemGroup>
22
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" />
22
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
23
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
24
</ItemGroup>
25