main
cs 140 lines 4.87 KB
Raw
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 WixToolset.Core.Native
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Text;
10
11 internal class WixNativeExe
12 {
13 private const string WixNativeExeFileName = "wixnative.exe";
14 private static string PathToWixNativeExe;
15
16 private readonly string commandLine;
17 private readonly List<string> stdinLines = new List<string>();
18
19 public WixNativeExe(params object[] args)
20 {
21 this.commandLine = String.Join(" ", QuoteArgumentsAsNecesary(args));
22 }
23
24 public void AddStdinLine(string line)
25 {
26 this.stdinLines.Add(line);
27 }
28
29 public void AddStdinLines(IEnumerable<string> lines)
30 {
31 this.stdinLines.AddRange(lines);
32 }
33
34 public IReadOnlyCollection<string> Run()
35 {
36 EnsurePathToWixNativeExeSet();
37
38 var wixNativeInfo = new ProcessStartInfo(PathToWixNativeExe, this.commandLine)
39 {
40 WorkingDirectory = Environment.CurrentDirectory,
41 RedirectStandardInput = true,
42 RedirectStandardOutput = true,
43 RedirectStandardError = true,
44 StandardOutputEncoding = Encoding.UTF8,
45 CreateNoWindow = true,
46 ErrorDialog = false,
47 UseShellExecute = false
48 };
49
50 var outputLines = new List<string>();
51
52 using (var process = Process.Start(wixNativeInfo))
53 {
54 process.OutputDataReceived += (s, a) => { if (a.Data != null) { outputLines.Add(a.Data); } };
55 process.ErrorDataReceived += (s, a) => { if (a.Data != null) { outputLines.Add(a.Data); } };
56 process.BeginOutputReadLine();
57 process.BeginErrorReadLine();
58
59 // Send the stdin preamble.
60 process.StandardInput.WriteLine(":");
61
62 if (this.stdinLines.Count > 0)
63 {
64 foreach (var line in this.stdinLines)
65 {
66 var bytes = Encoding.UTF8.GetBytes(line + Environment.NewLine);
67 process.StandardInput.BaseStream.Write(bytes, 0, bytes.Length);
68 }
69
70 // Trailing blank line indicates stdin complete.
71 process.StandardInput.WriteLine();
72 }
73
74 // If the process successfully exits documentation says we need to wait again
75 // without a timeout to ensure that all of the redirected output is captured.
76 //
77 process.WaitForExit();
78
79 if (process.ExitCode != 0)
80 {
81 throw WixNativeException.FromOutputLines(process.ExitCode, outputLines);
82 }
83 }
84
85 return outputLines;
86 }
87
88 private static void EnsurePathToWixNativeExeSet()
89 {
90 if (String.IsNullOrEmpty(PathToWixNativeExe))
91 {
92 var result = typeof(WixNativeExe).Assembly.FindFileRelativeToAssembly(WixNativeExeFileName, searchNativeDllDirectories: true);
93
94 if (!result.Found)
95 {
96 throw new PlatformNotSupportedException(
97 $"Could not find platform specific '{WixNativeExeFileName}'",
98 new FileNotFoundException($"Could not find internal piece of WiX Toolset from: {result.PossiblePaths}", WixNativeExeFileName));
99 }
100
101 PathToWixNativeExe = result.Path;
102 }
103 }
104
105 private static IEnumerable<string> QuoteArgumentsAsNecesary(object[] args)
106 {
107 foreach (var arg in args)
108 {
109 if (arg is string str)
110 {
111 if (String.IsNullOrEmpty(str))
112 {
113 }
114 else if (str.Contains(" ") && !str.StartsWith("\""))
115 {
116 // Escape a trailing backslash with another backslash if quoting the path.
117 if (str.EndsWith("\\", StringComparison.Ordinal))
118 {
119 str += "\\";
120 }
121
122 yield return $"\"{str}\"";
123 }
124 else
125 {
126 yield return str;
127 }
128 }
129 else if (arg is int i)
130 {
131 yield return i.ToString();
132 }
133 else
134 {
135 throw new ArgumentException(nameof(args));
136 }
137 }
138 }
139 }
140 }