aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-05-30 12:16:27 +1000
committerSean Hall <r.sean.hall@gmail.com>2020-05-30 23:06:26 +1000
commit223b2845955d4d6604cfd014cf9aa536ca1eb0a2 (patch)
treed54dfd97dfd0fa2b892b5eec087431e167de18a1 /src
parent2ab17d9c527629fa70ee4dec81bd0050e1b27f31 (diff)
downloadwix-223b2845955d4d6604cfd014cf9aa536ca1eb0a2.tar.gz
wix-223b2845955d4d6604cfd014cf9aa536ca1eb0a2.tar.bz2
wix-223b2845955d4d6604cfd014cf9aa536ca1eb0a2.zip
Add ability to run 64-bit msbuild.
Diffstat (limited to 'src')
-rw-r--r--src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj2
-rw-r--r--src/WixBuildTools.TestSupport/ExternalExecutable.cs88
-rw-r--r--src/WixBuildTools.TestSupport/ExternalExecutableResult.cs17
-rw-r--r--src/WixBuildTools.TestSupport/MsbuildRunner.cs205
-rw-r--r--src/WixBuildTools.TestSupport/VswhereRunner.cs41
-rw-r--r--src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj2
-rw-r--r--src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj2
7 files changed, 251 insertions, 106 deletions
diff --git a/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj b/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
index 80d6b0d6..ccbb92c2 100644
--- a/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
+++ b/src/WixBuildTools.MsgGen/WixBuildTools.MsgGen.csproj
@@ -18,7 +18,7 @@
18 </ItemGroup> 18 </ItemGroup>
19 19
20 <ItemGroup> 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" /> 22 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
23 </ItemGroup> 23 </ItemGroup>
24 24
diff --git a/src/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/WixBuildTools.TestSupport/ExternalExecutable.cs
new file mode 100644
index 00000000..eb07aa13
--- /dev/null
+++ b/src/WixBuildTools.TestSupport/ExternalExecutable.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs b/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs
new file mode 100644
index 00000000..19b5183b
--- /dev/null
+++ b/src/WixBuildTools.TestSupport/ExternalExecutableResult.cs
@@ -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
3namespace 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}
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}
diff --git a/src/WixBuildTools.TestSupport/VswhereRunner.cs b/src/WixBuildTools.TestSupport/VswhereRunner.cs
new file mode 100644
index 00000000..0197e125
--- /dev/null
+++ b/src/WixBuildTools.TestSupport/VswhereRunner.cs
@@ -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
3namespace 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}
diff --git a/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj b/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
index 31bdf033..e6cdddef 100644
--- a/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
+++ b/src/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj
@@ -18,7 +18,7 @@
18 </ItemGroup> 18 </ItemGroup>
19 19
20 <ItemGroup> 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" /> 22 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
23 </ItemGroup> 23 </ItemGroup>
24 24
diff --git a/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj b/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
index ef24420e..bf9d957f 100644
--- a/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
+++ b/src/WixBuildTools.XsdGen/WixBuildTools.XsdGen.csproj
@@ -19,7 +19,7 @@
19 </ItemGroup> 19 </ItemGroup>
20 20
21 <ItemGroup> 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" /> 23 <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="All" />
24 </ItemGroup> 24 </ItemGroup>
25 25