diff options
Diffstat (limited to 'src/WixToolset.Core/Bind/Databases')
16 files changed, 0 insertions, 3537 deletions
diff --git a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs b/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs deleted file mode 100644 index 5e2650e9..00000000 --- a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs +++ /dev/null | |||
@@ -1,314 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.IO; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Rows; | ||
11 | |||
12 | /// <summary> | ||
13 | /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. | ||
14 | /// </summary> | ||
15 | public class AssignMediaCommand : ICommand | ||
16 | { | ||
17 | public AssignMediaCommand() | ||
18 | { | ||
19 | this.CabinetNameTemplate = "Cab{0}.cab"; | ||
20 | } | ||
21 | |||
22 | public Output Output { private get; set; } | ||
23 | |||
24 | public bool FilesCompressed { private get; set; } | ||
25 | |||
26 | public string CabinetNameTemplate { private get; set; } | ||
27 | |||
28 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
29 | |||
30 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
31 | |||
32 | /// <summary> | ||
33 | /// Gets cabinets with their file rows. | ||
34 | /// </summary> | ||
35 | public Dictionary<MediaRow, IEnumerable<FileFacade>> FileFacadesByCabinetMedia { get; private set; } | ||
36 | |||
37 | /// <summary> | ||
38 | /// Get media rows. | ||
39 | /// </summary> | ||
40 | public RowDictionary<MediaRow> MediaRows { get; private set; } | ||
41 | |||
42 | /// <summary> | ||
43 | /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no. | ||
44 | /// This contains all the files when Package element is marked with compression=no | ||
45 | /// </summary> | ||
46 | public IEnumerable<FileFacade> UncompressedFileFacades { get; private set; } | ||
47 | |||
48 | public void Execute() | ||
49 | { | ||
50 | Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia = new Dictionary<MediaRow, List<FileFacade>>(); | ||
51 | |||
52 | RowDictionary<MediaRow> mediaRows = new RowDictionary<MediaRow>(); | ||
53 | |||
54 | List<FileFacade> uncompressedFiles = new List<FileFacade>(); | ||
55 | |||
56 | MediaRow mergeModuleMediaRow = null; | ||
57 | Table mediaTable = this.Output.Tables["Media"]; | ||
58 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
59 | |||
60 | // If both tables are authored, it is an error. | ||
61 | if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) | ||
62 | { | ||
63 | throw new WixException(WixErrors.MediaTableCollision(null)); | ||
64 | } | ||
65 | |||
66 | // When building merge module, all the files go to "#MergeModule.CABinet". | ||
67 | if (OutputType.Module == this.Output.Type) | ||
68 | { | ||
69 | Table mergeModuleMediaTable = new Table(null, this.TableDefinitions["Media"]); | ||
70 | mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null); | ||
71 | mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; | ||
72 | |||
73 | filesByCabinetMedia.Add(mergeModuleMediaRow, new List<FileFacade>()); | ||
74 | } | ||
75 | |||
76 | if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) | ||
77 | { | ||
78 | this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); | ||
83 | } | ||
84 | |||
85 | this.FileFacadesByCabinetMedia = new Dictionary<MediaRow, IEnumerable<FileFacade>>(); | ||
86 | |||
87 | foreach (var mediaRowWithFiles in filesByCabinetMedia) | ||
88 | { | ||
89 | this.FileFacadesByCabinetMedia.Add(mediaRowWithFiles.Key, mediaRowWithFiles.Value); | ||
90 | } | ||
91 | |||
92 | this.MediaRows = mediaRows; | ||
93 | |||
94 | this.UncompressedFileFacades = uncompressedFiles; | ||
95 | } | ||
96 | |||
97 | /// <summary> | ||
98 | /// Assign files to cabinets based on MediaTemplate authoring. | ||
99 | /// </summary> | ||
100 | /// <param name="fileFacades">FileRowCollection</param> | ||
101 | private void AutoAssignFiles(Table mediaTable, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) | ||
102 | { | ||
103 | const int MaxCabIndex = 999; | ||
104 | |||
105 | ulong currentPreCabSize = 0; | ||
106 | ulong maxPreCabSizeInBytes; | ||
107 | int maxPreCabSizeInMB = 0; | ||
108 | int currentCabIndex = 0; | ||
109 | |||
110 | MediaRow currentMediaRow = null; | ||
111 | |||
112 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
113 | |||
114 | // Auto assign files to cabinets based on maximum uncompressed media size | ||
115 | mediaTable.Rows.Clear(); | ||
116 | WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; | ||
117 | |||
118 | if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) | ||
119 | { | ||
120 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; | ||
121 | } | ||
122 | |||
123 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
124 | |||
125 | try | ||
126 | { | ||
127 | // Override authored mums value if environment variable is authored. | ||
128 | if (!String.IsNullOrEmpty(mumsString)) | ||
129 | { | ||
130 | maxPreCabSizeInMB = Int32.Parse(mumsString); | ||
131 | } | ||
132 | else | ||
133 | { | ||
134 | maxPreCabSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; | ||
135 | } | ||
136 | |||
137 | maxPreCabSizeInBytes = (ulong)maxPreCabSizeInMB * 1024 * 1024; | ||
138 | } | ||
139 | catch (FormatException) | ||
140 | { | ||
141 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); | ||
142 | } | ||
143 | catch (OverflowException) | ||
144 | { | ||
145 | throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCabSizeInMB)); | ||
146 | } | ||
147 | |||
148 | foreach (FileFacade facade in this.FileFacades) | ||
149 | { | ||
150 | // When building a product, if the current file is not to be compressed or if | ||
151 | // the package set not to be compressed, don't cab it. | ||
152 | if (OutputType.Product == this.Output.Type && | ||
153 | (YesNoType.No == facade.File.Compressed || | ||
154 | (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) | ||
155 | { | ||
156 | uncompressedFiles.Add(facade); | ||
157 | continue; | ||
158 | } | ||
159 | |||
160 | if (currentCabIndex == MaxCabIndex) | ||
161 | { | ||
162 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. | ||
163 | List<FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; | ||
164 | facade.WixFile.DiskId = currentCabIndex; | ||
165 | cabinetFiles.Add(facade); | ||
166 | continue; | ||
167 | } | ||
168 | |||
169 | // Update current cab size. | ||
170 | currentPreCabSize += (ulong)facade.File.FileSize; | ||
171 | |||
172 | if (currentPreCabSize > maxPreCabSizeInBytes) | ||
173 | { | ||
174 | // Overflow due to current file | ||
175 | currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); | ||
176 | mediaRows.Add(currentMediaRow); | ||
177 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | ||
178 | |||
179 | List<FileFacade> cabinetFileRows = filesByCabinetMedia[currentMediaRow]; | ||
180 | facade.WixFile.DiskId = currentCabIndex; | ||
181 | cabinetFileRows.Add(facade); | ||
182 | // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize | ||
183 | currentPreCabSize = (ulong)facade.File.FileSize; | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | // File fits in the current cab. | ||
188 | if (currentMediaRow == null) | ||
189 | { | ||
190 | // Create new cab and MediaRow | ||
191 | currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); | ||
192 | mediaRows.Add(currentMediaRow); | ||
193 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | ||
194 | } | ||
195 | |||
196 | // Associate current file with current cab. | ||
197 | List<FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; | ||
198 | facade.WixFile.DiskId = currentCabIndex; | ||
199 | cabinetFiles.Add(facade); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | // If there are uncompressed files and no MediaRow, create a default one. | ||
204 | if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) | ||
205 | { | ||
206 | MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
207 | defaultMediaRow.DiskId = 1; | ||
208 | mediaRows.Add(defaultMediaRow); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /// <summary> | ||
213 | /// Assign files to cabinets based on Media authoring. | ||
214 | /// </summary> | ||
215 | /// <param name="mediaTable"></param> | ||
216 | /// <param name="mergeModuleMediaRow"></param> | ||
217 | /// <param name="fileFacades"></param> | ||
218 | private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) | ||
219 | { | ||
220 | if (OutputType.Module != this.Output.Type) | ||
221 | { | ||
222 | if (null != mediaTable) | ||
223 | { | ||
224 | Dictionary<string, MediaRow> cabinetMediaRows = new Dictionary<string, MediaRow>(StringComparer.InvariantCultureIgnoreCase); | ||
225 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
226 | { | ||
227 | // If the Media row has a cabinet, make sure it is unique across all Media rows. | ||
228 | if (!String.IsNullOrEmpty(mediaRow.Cabinet)) | ||
229 | { | ||
230 | MediaRow existingRow; | ||
231 | if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) | ||
232 | { | ||
233 | Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); | ||
234 | Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); | ||
235 | } | ||
236 | else | ||
237 | { | ||
238 | cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | mediaRows.Add(mediaRow); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | foreach (MediaRow mediaRow in mediaRows.Values) | ||
247 | { | ||
248 | if (null != mediaRow.Cabinet) | ||
249 | { | ||
250 | filesByCabinetMedia.Add(mediaRow, new List<FileFacade>()); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | foreach (FileFacade facade in fileFacades) | ||
256 | { | ||
257 | if (OutputType.Module == this.Output.Type) | ||
258 | { | ||
259 | filesByCabinetMedia[mergeModuleMediaRow].Add(facade); | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | MediaRow mediaRow; | ||
264 | if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow)) | ||
265 | { | ||
266 | Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId)); | ||
267 | continue; | ||
268 | } | ||
269 | |||
270 | // When building a product, if the current file is not to be compressed or if | ||
271 | // the package set not to be compressed, don't cab it. | ||
272 | if (OutputType.Product == this.Output.Type && | ||
273 | (YesNoType.No == facade.File.Compressed || | ||
274 | (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) | ||
275 | { | ||
276 | uncompressedFiles.Add(facade); | ||
277 | } | ||
278 | else // file is marked compressed. | ||
279 | { | ||
280 | List<FileFacade> cabinetFiles; | ||
281 | if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles)) | ||
282 | { | ||
283 | cabinetFiles.Add(facade); | ||
284 | } | ||
285 | else | ||
286 | { | ||
287 | Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId)); | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | /// <summary> | ||
295 | /// Adds a row to the media table with cab name template filled in. | ||
296 | /// </summary> | ||
297 | /// <param name="mediaTable"></param> | ||
298 | /// <param name="cabIndex"></param> | ||
299 | /// <returns></returns> | ||
300 | private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) | ||
301 | { | ||
302 | MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); | ||
303 | currentMediaRow.DiskId = cabIndex; | ||
304 | currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); | ||
305 | |||
306 | Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); | ||
307 | WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); | ||
308 | row.DiskId = cabIndex; | ||
309 | row.CompressionLevel = mediaTemplateRow.CompressionLevel; | ||
310 | |||
311 | return currentMediaRow; | ||
312 | } | ||
313 | } | ||
314 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs b/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs deleted file mode 100644 index 95bd4cf0..00000000 --- a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs +++ /dev/null | |||
@@ -1,135 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Globalization; | ||
7 | using WixToolset.Data; | ||
8 | |||
9 | /// <summary> | ||
10 | /// Binds the summary information table of a database. | ||
11 | /// </summary> | ||
12 | internal class BindSummaryInfoCommand : ICommand | ||
13 | { | ||
14 | /// <summary> | ||
15 | /// The output to bind. | ||
16 | /// </summary> | ||
17 | public Output Output { private get; set; } | ||
18 | |||
19 | /// <summary> | ||
20 | /// Returns a flag indicating if files are compressed by default. | ||
21 | /// </summary> | ||
22 | public bool Compressed { get; private set; } | ||
23 | |||
24 | /// <summary> | ||
25 | /// Returns a flag indicating if uncompressed files use long filenames. | ||
26 | /// </summary> | ||
27 | public bool LongNames { get; private set; } | ||
28 | |||
29 | public int InstallerVersion { get; private set; } | ||
30 | |||
31 | /// <summary> | ||
32 | /// Modularization guid, or null if the output is not a module. | ||
33 | /// </summary> | ||
34 | public string ModularizationGuid { get; private set; } | ||
35 | |||
36 | public void Execute() | ||
37 | { | ||
38 | this.Compressed = false; | ||
39 | this.LongNames = false; | ||
40 | this.InstallerVersion = 0; | ||
41 | this.ModularizationGuid = null; | ||
42 | |||
43 | Table summaryInformationTable = this.Output.Tables["_SummaryInformation"]; | ||
44 | |||
45 | if (null != summaryInformationTable) | ||
46 | { | ||
47 | bool foundCreateDataTime = false; | ||
48 | bool foundLastSaveDataTime = false; | ||
49 | bool foundCreatingApplication = false; | ||
50 | string now = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture); | ||
51 | |||
52 | foreach (Row summaryInformationRow in summaryInformationTable.Rows) | ||
53 | { | ||
54 | switch (summaryInformationRow.FieldAsInteger(0)) | ||
55 | { | ||
56 | case 1: // PID_CODEPAGE | ||
57 | // make sure the code page is an int and not a web name or null | ||
58 | string codepage = summaryInformationRow.FieldAsString(1); | ||
59 | |||
60 | if (null == codepage) | ||
61 | { | ||
62 | codepage = "0"; | ||
63 | } | ||
64 | else | ||
65 | { | ||
66 | summaryInformationRow[1] = Common.GetValidCodePage(codepage, false, false, summaryInformationRow.SourceLineNumbers).ToString(CultureInfo.InvariantCulture); | ||
67 | } | ||
68 | break; | ||
69 | case 9: // PID_REVNUMBER | ||
70 | string packageCode = (string)summaryInformationRow[1]; | ||
71 | |||
72 | if (OutputType.Module == this.Output.Type) | ||
73 | { | ||
74 | this.ModularizationGuid = packageCode.Substring(1, 36).Replace('-', '_'); | ||
75 | } | ||
76 | else if ("*" == packageCode) | ||
77 | { | ||
78 | // set the revision number (package/patch code) if it should be automatically generated | ||
79 | summaryInformationRow[1] = Common.GenerateGuid(); | ||
80 | } | ||
81 | break; | ||
82 | case 12: // PID_CREATE_DTM | ||
83 | foundCreateDataTime = true; | ||
84 | break; | ||
85 | case 13: // PID_LASTSAVE_DTM | ||
86 | foundLastSaveDataTime = true; | ||
87 | break; | ||
88 | case 14: | ||
89 | this.InstallerVersion = summaryInformationRow.FieldAsInteger(1); | ||
90 | break; | ||
91 | case 15: // PID_WORDCOUNT | ||
92 | if (OutputType.Patch == this.Output.Type) | ||
93 | { | ||
94 | this.LongNames = true; | ||
95 | this.Compressed = true; | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | this.LongNames = (0 == (summaryInformationRow.FieldAsInteger(1) & 1)); | ||
100 | this.Compressed = (2 == (summaryInformationRow.FieldAsInteger(1) & 2)); | ||
101 | } | ||
102 | break; | ||
103 | case 18: // PID_APPNAME | ||
104 | foundCreatingApplication = true; | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | // add a summary information row for the create time/date property if its not already set | ||
110 | if (!foundCreateDataTime) | ||
111 | { | ||
112 | Row createTimeDateRow = summaryInformationTable.CreateRow(null); | ||
113 | createTimeDateRow[0] = 12; | ||
114 | createTimeDateRow[1] = now; | ||
115 | } | ||
116 | |||
117 | // add a summary information row for the last save time/date property if its not already set | ||
118 | if (!foundLastSaveDataTime) | ||
119 | { | ||
120 | Row lastSaveTimeDateRow = summaryInformationTable.CreateRow(null); | ||
121 | lastSaveTimeDateRow[0] = 13; | ||
122 | lastSaveTimeDateRow[1] = now; | ||
123 | } | ||
124 | |||
125 | // add a summary information row for the creating application property if its not already set | ||
126 | if (!foundCreatingApplication) | ||
127 | { | ||
128 | Row creatingApplicationRow = summaryInformationTable.CreateRow(null); | ||
129 | creatingApplicationRow[0] = 18; | ||
130 | creatingApplicationRow[1] = String.Format(CultureInfo.InvariantCulture, AppCommon.GetCreatingApplicationString()); | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs b/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs deleted file mode 100644 index 2de6ec25..00000000 --- a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs +++ /dev/null | |||
@@ -1,176 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Threading; | ||
10 | using WixToolset.Cab; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Rows; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Builds cabinets using multiple threads. This implements a thread pool that generates cabinets with multiple | ||
16 | /// threads. Unlike System.Threading.ThreadPool, it waits until all threads are finished. | ||
17 | /// </summary> | ||
18 | internal sealed class CabinetBuilder | ||
19 | { | ||
20 | private Queue cabinetWorkItems; | ||
21 | private object lockObject; | ||
22 | private int threadCount; | ||
23 | |||
24 | // Address of Binder's callback function for Cabinet Splitting | ||
25 | private IntPtr newCabNamesCallBackAddress; | ||
26 | |||
27 | public int MaximumCabinetSizeForLargeFileSplitting { get; set; } | ||
28 | public int MaximumUncompressedMediaSize { get; set; } | ||
29 | |||
30 | /// <summary> | ||
31 | /// Instantiate a new CabinetBuilder. | ||
32 | /// </summary> | ||
33 | /// <param name="threadCount">number of threads to use</param> | ||
34 | /// <param name="newCabNamesCallBackAddress">Address of Binder's callback function for Cabinet Splitting</param> | ||
35 | public CabinetBuilder(int threadCount, IntPtr newCabNamesCallBackAddress) | ||
36 | { | ||
37 | if (0 >= threadCount) | ||
38 | { | ||
39 | throw new ArgumentOutOfRangeException("threadCount"); | ||
40 | } | ||
41 | |||
42 | this.cabinetWorkItems = new Queue(); | ||
43 | this.lockObject = new object(); | ||
44 | |||
45 | this.threadCount = threadCount; | ||
46 | |||
47 | // Set Address of Binder's callback function for Cabinet Splitting | ||
48 | this.newCabNamesCallBackAddress = newCabNamesCallBackAddress; | ||
49 | } | ||
50 | |||
51 | /// <summary> | ||
52 | /// Enqueues a CabinetWorkItem to the queue. | ||
53 | /// </summary> | ||
54 | /// <param name="cabinetWorkItem">cabinet work item</param> | ||
55 | public void Enqueue(CabinetWorkItem cabinetWorkItem) | ||
56 | { | ||
57 | this.cabinetWorkItems.Enqueue(cabinetWorkItem); | ||
58 | } | ||
59 | |||
60 | /// <summary> | ||
61 | /// Create the queued cabinets. | ||
62 | /// </summary> | ||
63 | /// <returns>error message number (zero if no error)</returns> | ||
64 | public void CreateQueuedCabinets() | ||
65 | { | ||
66 | // don't create more threads than the number of cabinets to build | ||
67 | if (this.cabinetWorkItems.Count < this.threadCount) | ||
68 | { | ||
69 | this.threadCount = this.cabinetWorkItems.Count; | ||
70 | } | ||
71 | |||
72 | if (0 < this.threadCount) | ||
73 | { | ||
74 | Thread[] threads = new Thread[this.threadCount]; | ||
75 | |||
76 | for (int i = 0; i < threads.Length; i++) | ||
77 | { | ||
78 | threads[i] = new Thread(new ThreadStart(this.ProcessWorkItems)); | ||
79 | threads[i].Start(); | ||
80 | } | ||
81 | |||
82 | // wait for all threads to finish | ||
83 | foreach (Thread thread in threads) | ||
84 | { | ||
85 | thread.Join(); | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /// <summary> | ||
91 | /// This function gets called by multiple threads to do actual work. | ||
92 | /// It takes one work item at a time and calls this.CreateCabinet(). | ||
93 | /// It does not return until cabinetWorkItems queue is empty | ||
94 | /// </summary> | ||
95 | private void ProcessWorkItems() | ||
96 | { | ||
97 | try | ||
98 | { | ||
99 | while (true) | ||
100 | { | ||
101 | CabinetWorkItem cabinetWorkItem; | ||
102 | |||
103 | lock (this.cabinetWorkItems) | ||
104 | { | ||
105 | // check if there are any more cabinets to create | ||
106 | if (0 == this.cabinetWorkItems.Count) | ||
107 | { | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | cabinetWorkItem = (CabinetWorkItem)this.cabinetWorkItems.Dequeue(); | ||
112 | } | ||
113 | |||
114 | // create a cabinet | ||
115 | this.CreateCabinet(cabinetWorkItem); | ||
116 | } | ||
117 | } | ||
118 | catch (WixException we) | ||
119 | { | ||
120 | Messaging.Instance.OnMessage(we.Error); | ||
121 | } | ||
122 | catch (Exception e) | ||
123 | { | ||
124 | Messaging.Instance.OnMessage(WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /// <summary> | ||
129 | /// Creates a cabinet using the wixcab.dll interop layer. | ||
130 | /// </summary> | ||
131 | /// <param name="cabinetWorkItem">CabinetWorkItem containing information about the cabinet to create.</param> | ||
132 | private void CreateCabinet(CabinetWorkItem cabinetWorkItem) | ||
133 | { | ||
134 | Messaging.Instance.OnMessage(WixVerboses.CreateCabinet(cabinetWorkItem.CabinetFile)); | ||
135 | |||
136 | int maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting | ||
137 | ulong maxPreCompressedSizeInBytes = 0; | ||
138 | |||
139 | if (MaximumCabinetSizeForLargeFileSplitting != 0) | ||
140 | { | ||
141 | // User Specified Max Cab Size for File Splitting, So Check if this cabinet has a single file larger than MaximumUncompressedFileSize | ||
142 | // If a file is larger than MaximumUncompressedFileSize, then the cabinet containing it will have only this file | ||
143 | if (1 == cabinetWorkItem.FileFacades.Count()) | ||
144 | { | ||
145 | // Cabinet has Single File, Check if this is Large File than needs Splitting into Multiple cabs | ||
146 | // Get the Value for Max Uncompressed Media Size | ||
147 | maxPreCompressedSizeInBytes = (ulong)MaximumUncompressedMediaSize * 1024 * 1024; | ||
148 | |||
149 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row | ||
150 | { | ||
151 | if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) | ||
152 | { | ||
153 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting | ||
154 | maxCabinetSize = MaximumCabinetSizeForLargeFileSplitting; | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // create the cabinet file | ||
161 | string cabinetFileName = Path.GetFileName(cabinetWorkItem.CabinetFile); | ||
162 | string cabinetDirectory = Path.GetDirectoryName(cabinetWorkItem.CabinetFile); | ||
163 | |||
164 | using (WixCreateCab cab = new WixCreateCab(cabinetFileName, cabinetDirectory, cabinetWorkItem.FileFacades.Count(), maxCabinetSize, cabinetWorkItem.MaxThreshold, cabinetWorkItem.CompressionLevel)) | ||
165 | { | ||
166 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) | ||
167 | { | ||
168 | cab.AddFile(facade); | ||
169 | } | ||
170 | |||
171 | cab.Complete(newCabNamesCallBackAddress); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
diff --git a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs b/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs deleted file mode 100644 index 20241bc9..00000000 --- a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System.Collections.Generic; | ||
6 | using WixToolset.Data; | ||
7 | using WixToolset.Data.Rows; | ||
8 | |||
9 | /// <summary> | ||
10 | /// A cabinet builder work item. | ||
11 | /// </summary> | ||
12 | internal sealed class CabinetWorkItem | ||
13 | { | ||
14 | private string cabinetFile; | ||
15 | private CompressionLevel compressionLevel; | ||
16 | //private BinderFileManager binderFileManager; | ||
17 | private int maxThreshold; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Instantiate a new CabinetWorkItem. | ||
21 | /// </summary> | ||
22 | /// <param name="fileFacades">The collection of files in this cabinet.</param> | ||
23 | /// <param name="cabinetFile">The cabinet file.</param> | ||
24 | /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> | ||
25 | /// <param name="compressionLevel">The compression level of the cabinet.</param> | ||
26 | /// <param name="binderFileManager">The binder file manager.</param> | ||
27 | public CabinetWorkItem(IEnumerable<FileFacade> fileFacades, string cabinetFile, int maxThreshold, CompressionLevel compressionLevel /*, BinderFileManager binderFileManager*/) | ||
28 | { | ||
29 | this.cabinetFile = cabinetFile; | ||
30 | this.compressionLevel = compressionLevel; | ||
31 | this.FileFacades = fileFacades; | ||
32 | //this.binderFileManager = binderFileManager; | ||
33 | this.maxThreshold = maxThreshold; | ||
34 | } | ||
35 | |||
36 | /// <summary> | ||
37 | /// Gets the cabinet file. | ||
38 | /// </summary> | ||
39 | /// <value>The cabinet file.</value> | ||
40 | public string CabinetFile | ||
41 | { | ||
42 | get { return this.cabinetFile; } | ||
43 | } | ||
44 | |||
45 | /// <summary> | ||
46 | /// Gets the compression level of the cabinet. | ||
47 | /// </summary> | ||
48 | /// <value>The compression level of the cabinet.</value> | ||
49 | public CompressionLevel CompressionLevel | ||
50 | { | ||
51 | get { return this.compressionLevel; } | ||
52 | } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Gets the collection of files in this cabinet. | ||
56 | /// </summary> | ||
57 | /// <value>The collection of files in this cabinet.</value> | ||
58 | public IEnumerable<FileFacade> FileFacades { get; private set; } | ||
59 | |||
60 | /// <summary> | ||
61 | /// Gets the binder file manager. | ||
62 | /// </summary> | ||
63 | /// <value>The binder file manager.</value> | ||
64 | //public BinderFileManager BinderFileManager | ||
65 | //{ | ||
66 | // get { return this.binderFileManager; } | ||
67 | //} | ||
68 | |||
69 | /// <summary> | ||
70 | /// Gets the max threshold. | ||
71 | /// </summary> | ||
72 | /// <value>The maximum threshold for a folder in a cabinet.</value> | ||
73 | public int MaxThreshold | ||
74 | { | ||
75 | get { return this.maxThreshold; } | ||
76 | } | ||
77 | } | ||
78 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs b/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs deleted file mode 100644 index 7cb18e0f..00000000 --- a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs +++ /dev/null | |||
@@ -1,91 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Globalization; | ||
8 | using WixToolset.MergeMod; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Callback object for configurable merge modules. | ||
12 | /// </summary> | ||
13 | internal sealed class ConfigurationCallback : IMsmConfigureModule | ||
14 | { | ||
15 | private const int SOk = 0x0; | ||
16 | private const int SFalse = 0x1; | ||
17 | private Hashtable configurationData; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Creates a ConfigurationCallback object. | ||
21 | /// </summary> | ||
22 | /// <param name="configData">String to break up into name/value pairs.</param> | ||
23 | public ConfigurationCallback(string configData) | ||
24 | { | ||
25 | if (String.IsNullOrEmpty(configData)) | ||
26 | { | ||
27 | throw new ArgumentNullException("configData"); | ||
28 | } | ||
29 | |||
30 | string[] pairs = configData.Split(','); | ||
31 | this.configurationData = new Hashtable(pairs.Length); | ||
32 | for (int i = 0; i < pairs.Length; ++i) | ||
33 | { | ||
34 | string[] nameVal = pairs[i].Split('='); | ||
35 | string name = nameVal[0]; | ||
36 | string value = nameVal[1]; | ||
37 | |||
38 | name = name.Replace("%2C", ","); | ||
39 | name = name.Replace("%3D", "="); | ||
40 | name = name.Replace("%25", "%"); | ||
41 | |||
42 | value = value.Replace("%2C", ","); | ||
43 | value = value.Replace("%3D", "="); | ||
44 | value = value.Replace("%25", "%"); | ||
45 | |||
46 | this.configurationData[name] = value; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /// <summary> | ||
51 | /// Returns text data based on name. | ||
52 | /// </summary> | ||
53 | /// <param name="name">Name of value to return.</param> | ||
54 | /// <param name="configData">Out param to put configuration data into.</param> | ||
55 | /// <returns>S_OK if value provided, S_FALSE if not.</returns> | ||
56 | public int ProvideTextData(string name, out string configData) | ||
57 | { | ||
58 | if (this.configurationData.Contains(name)) | ||
59 | { | ||
60 | configData = (string)this.configurationData[name]; | ||
61 | return SOk; | ||
62 | } | ||
63 | else | ||
64 | { | ||
65 | configData = null; | ||
66 | return SFalse; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /// <summary> | ||
71 | /// Returns integer data based on name. | ||
72 | /// </summary> | ||
73 | /// <param name="name">Name of value to return.</param> | ||
74 | /// <param name="configData">Out param to put configuration data into.</param> | ||
75 | /// <returns>S_OK if value provided, S_FALSE if not.</returns> | ||
76 | public int ProvideIntegerData(string name, out int configData) | ||
77 | { | ||
78 | if (this.configurationData.Contains(name)) | ||
79 | { | ||
80 | string val = (string)this.configurationData[name]; | ||
81 | configData = Convert.ToInt32(val, CultureInfo.InvariantCulture); | ||
82 | return SOk; | ||
83 | } | ||
84 | else | ||
85 | { | ||
86 | configData = 0; | ||
87 | return SFalse; | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs b/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs deleted file mode 100644 index af1ab3b0..00000000 --- a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs +++ /dev/null | |||
@@ -1,606 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using WixToolset.Data; | ||
9 | using WixToolset.Data.Rows; | ||
10 | using WixToolset.Extensibility; | ||
11 | using WixToolset.Core.Native; | ||
12 | |||
13 | internal class CopyTransformDataCommand : ICommand | ||
14 | { | ||
15 | public bool CopyOutFileRows { private get; set; } | ||
16 | |||
17 | public BinderFileManagerCore FileManagerCore { private get; set; } | ||
18 | |||
19 | public IEnumerable<IBinderFileManager> FileManagers { private get; set; } | ||
20 | |||
21 | public Output Output { private get; set; } | ||
22 | |||
23 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
24 | |||
25 | public IEnumerable<FileFacade> FileFacades { get; private set; } | ||
26 | |||
27 | public void Execute() | ||
28 | { | ||
29 | Debug.Assert(OutputType.Patch != this.Output.Type); | ||
30 | |||
31 | List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; | ||
32 | |||
33 | #if false // TODO: Fix this patching related code to work correctly with FileFacades. | ||
34 | bool copyToPatch = (allFileRows != null); | ||
35 | bool copyFromPatch = !copyToPatch; | ||
36 | |||
37 | RowDictionary<MediaRow> patchMediaRows = new RowDictionary<MediaRow>(); | ||
38 | |||
39 | Dictionary<int, RowDictionary<WixFileRow>> patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); | ||
40 | |||
41 | Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | ||
42 | Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); | ||
43 | |||
44 | if (copyFromPatch) | ||
45 | { | ||
46 | // index patch files by diskId+fileId | ||
47 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) | ||
48 | { | ||
49 | int diskId = patchFileRow.DiskId; | ||
50 | RowDictionary<WixFileRow> mediaFileRows; | ||
51 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
52 | { | ||
53 | mediaFileRows = new RowDictionary<WixFileRow>(); | ||
54 | patchMediaFileRows.Add(diskId, mediaFileRows); | ||
55 | } | ||
56 | |||
57 | mediaFileRows.Add(patchFileRow); | ||
58 | } | ||
59 | |||
60 | Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); | ||
61 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); | ||
62 | } | ||
63 | |||
64 | // index paired transforms | ||
65 | Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); | ||
66 | foreach (SubStorage substorage in this.Output.SubStorages) | ||
67 | { | ||
68 | if (substorage.Name.StartsWith("#")) | ||
69 | { | ||
70 | pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | try | ||
75 | { | ||
76 | // copy File bind data into substorages | ||
77 | foreach (SubStorage substorage in this.Output.SubStorages) | ||
78 | { | ||
79 | if (substorage.Name.StartsWith("#")) | ||
80 | { | ||
81 | // no changes necessary for paired transforms | ||
82 | continue; | ||
83 | } | ||
84 | |||
85 | Output mainTransform = substorage.Data; | ||
86 | Table mainWixFileTable = mainTransform.Tables["WixFile"]; | ||
87 | Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; | ||
88 | |||
89 | this.FileManagerCore.ActiveSubStorage = substorage; | ||
90 | |||
91 | RowDictionary<WixFileRow> mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable); | ||
92 | RowDictionary<Row> mainMsiFileHashIndex = new RowDictionary<Row>(); | ||
93 | |||
94 | Table mainFileTable = mainTransform.Tables["File"]; | ||
95 | Output pairedTransform = (Output)pairedTransforms[substorage.Name]; | ||
96 | |||
97 | // copy Media.LastSequence and index the MsiFileHash table if it exists. | ||
98 | if (copyFromPatch) | ||
99 | { | ||
100 | Table pairedMediaTable = pairedTransform.Tables["Media"]; | ||
101 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | ||
102 | { | ||
103 | MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | ||
104 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; | ||
105 | } | ||
106 | |||
107 | if (null != mainMsiFileHashTable) | ||
108 | { | ||
109 | mainMsiFileHashIndex = new RowDictionary<Row>(mainMsiFileHashTable); | ||
110 | } | ||
111 | |||
112 | // Validate file row changes for keypath-related issues | ||
113 | this.ValidateFileRowChanges(mainTransform); | ||
114 | } | ||
115 | |||
116 | // Index File table of pairedTransform | ||
117 | Table pairedFileTable = pairedTransform.Tables["File"]; | ||
118 | RowDictionary<FileRow> pairedFileRows = new RowDictionary<FileRow>(pairedFileTable); | ||
119 | |||
120 | if (null != mainFileTable) | ||
121 | { | ||
122 | if (copyFromPatch) | ||
123 | { | ||
124 | // Remove the MsiFileHash table because it will be updated later with the final file hash for each file | ||
125 | mainTransform.Tables.Remove("MsiFileHash"); | ||
126 | } | ||
127 | |||
128 | foreach (FileRow mainFileRow in mainFileTable.Rows) | ||
129 | { | ||
130 | if (RowOperation.Delete == mainFileRow.Operation) | ||
131 | { | ||
132 | continue; | ||
133 | } | ||
134 | else if (RowOperation.None == mainFileRow.Operation && !copyToPatch) | ||
135 | { | ||
136 | continue; | ||
137 | } | ||
138 | |||
139 | WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File); | ||
140 | |||
141 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. | ||
142 | { | ||
143 | ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6]; | ||
144 | FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File); | ||
145 | |||
146 | // If the file is new, we always need to add it to the patch. | ||
147 | if (mainFileRow.Operation != RowOperation.Add) | ||
148 | { | ||
149 | // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. | ||
150 | if (null == objectField.PreviousData) | ||
151 | { | ||
152 | if (mainFileRow.Operation == RowOperation.None) | ||
153 | { | ||
154 | continue; | ||
155 | } | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | // TODO: should this entire condition be placed in the binder file manager? | ||
160 | if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && | ||
161 | !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) | ||
162 | { | ||
163 | // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. | ||
164 | mainFileRow.Operation = RowOperation.Modify; | ||
165 | if (null != pairedFileRow) | ||
166 | { | ||
167 | // Always patch-added, but never non-compressed. | ||
168 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | ||
169 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | ||
170 | pairedFileRow.Fields[6].Modified = true; | ||
171 | pairedFileRow.Operation = RowOperation.Modify; | ||
172 | } | ||
173 | } | ||
174 | else | ||
175 | { | ||
176 | // The File is same. We need mark all the attributes as unchanged. | ||
177 | mainFileRow.Operation = RowOperation.None; | ||
178 | foreach (Field field in mainFileRow.Fields) | ||
179 | { | ||
180 | field.Modified = false; | ||
181 | } | ||
182 | |||
183 | if (null != pairedFileRow) | ||
184 | { | ||
185 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded; | ||
186 | pairedFileRow.Fields[6].Modified = false; | ||
187 | pairedFileRow.Operation = RowOperation.None; | ||
188 | } | ||
189 | continue; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | else if (null != pairedFileRow) // RowOperation.Add | ||
194 | { | ||
195 | // Always patch-added, but never non-compressed. | ||
196 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | ||
197 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | ||
198 | pairedFileRow.Fields[6].Modified = true; | ||
199 | pairedFileRow.Operation = RowOperation.Add; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | // index patch files by diskId+fileId | ||
204 | int diskId = mainWixFileRow.DiskId; | ||
205 | |||
206 | RowDictionary<WixFileRow> mediaFileRows; | ||
207 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
208 | { | ||
209 | mediaFileRows = new RowDictionary<WixFileRow>(); | ||
210 | patchMediaFileRows.Add(diskId, mediaFileRows); | ||
211 | } | ||
212 | |||
213 | string fileId = mainFileRow.File; | ||
214 | WixFileRow patchFileRow = mediaFileRows.Get(fileId); | ||
215 | if (copyToPatch) | ||
216 | { | ||
217 | if (null == patchFileRow) | ||
218 | { | ||
219 | FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
220 | patchActualFileRow.CopyFrom(mainFileRow); | ||
221 | |||
222 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
223 | patchFileRow.CopyFrom(mainWixFileRow); | ||
224 | |||
225 | mediaFileRows.Add(patchFileRow); | ||
226 | |||
227 | allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right? | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | // TODO: confirm the rest of data is identical? | ||
232 | |||
233 | // make sure Source is same. Otherwise we are silently ignoring a file. | ||
234 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) | ||
235 | { | ||
236 | Messaging.Instance.OnMessage(WixErrors.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); | ||
237 | } | ||
238 | |||
239 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. | ||
240 | patchFileRow.AppendPreviousDataFrom(mainWixFileRow); | ||
241 | } | ||
242 | } | ||
243 | else | ||
244 | { | ||
245 | // copy data from the patch back to the transform | ||
246 | if (null != patchFileRow) | ||
247 | { | ||
248 | FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId); | ||
249 | for (int i = 0; i < patchFileRow.Fields.Length; i++) | ||
250 | { | ||
251 | string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString(); | ||
252 | string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString(); | ||
253 | |||
254 | if (1 == i) | ||
255 | { | ||
256 | // File.Component_ changes should not come from the shared file rows | ||
257 | // that contain the file information as each individual transform might | ||
258 | // have different changes (or no changes at all). | ||
259 | } | ||
260 | // File.Attributes should not changed for binary deltas | ||
261 | else if (6 == i) | ||
262 | { | ||
263 | if (null != patchFileRow.Patch) | ||
264 | { | ||
265 | // File.Attribute should not change for binary deltas | ||
266 | pairedFileRow.Attributes = mainFileRow.Attributes; | ||
267 | mainFileRow.Fields[i].Modified = false; | ||
268 | } | ||
269 | } | ||
270 | // File.Sequence is updated in pairedTransform, not mainTransform | ||
271 | else if (7 == i) | ||
272 | { | ||
273 | // file sequence is updated in Patch table instead of File table for delta patches | ||
274 | if (null != patchFileRow.Patch) | ||
275 | { | ||
276 | pairedFileRow.Fields[i].Modified = false; | ||
277 | } | ||
278 | else | ||
279 | { | ||
280 | pairedFileRow[i] = patchFileRow[i]; | ||
281 | pairedFileRow.Fields[i].Modified = true; | ||
282 | } | ||
283 | mainFileRow.Fields[i].Modified = false; | ||
284 | } | ||
285 | else if (patchValue != mainValue) | ||
286 | { | ||
287 | mainFileRow[i] = patchFileRow[i]; | ||
288 | mainFileRow.Fields[i].Modified = true; | ||
289 | if (mainFileRow.Operation == RowOperation.None) | ||
290 | { | ||
291 | mainFileRow.Operation = RowOperation.Modify; | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | // copy MsiFileHash row for this File | ||
297 | Row patchHashRow; | ||
298 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow)) | ||
299 | { | ||
300 | patchHashRow = patchFileRow.Hash; | ||
301 | } | ||
302 | |||
303 | if (null != patchHashRow) | ||
304 | { | ||
305 | Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
306 | Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
307 | for (int i = 0; i < patchHashRow.Fields.Length; i++) | ||
308 | { | ||
309 | mainHashRow[i] = patchHashRow[i]; | ||
310 | if (i > 1) | ||
311 | { | ||
312 | // assume all hash fields have been modified | ||
313 | mainHashRow.Fields[i].Modified = true; | ||
314 | } | ||
315 | } | ||
316 | |||
317 | // assume the MsiFileHash operation follows the File one | ||
318 | mainHashRow.Operation = mainFileRow.Operation; | ||
319 | } | ||
320 | |||
321 | // copy MsiAssemblyName rows for this File | ||
322 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | ||
323 | if (null != patchAssemblyNameRows) | ||
324 | { | ||
325 | Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
326 | foreach (Row patchAssemblyNameRow in patchAssemblyNameRows) | ||
327 | { | ||
328 | // Copy if there isn't an identical modified/added row already in the transform. | ||
329 | bool foundMatchingModifiedRow = false; | ||
330 | foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows) | ||
331 | { | ||
332 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | ||
333 | { | ||
334 | foundMatchingModifiedRow = true; | ||
335 | break; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | if (!foundMatchingModifiedRow) | ||
340 | { | ||
341 | Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
342 | for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | ||
343 | { | ||
344 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | ||
345 | } | ||
346 | |||
347 | // assume value field has been modified | ||
348 | mainAssemblyNameRow.Fields[2].Modified = true; | ||
349 | mainAssemblyNameRow.Operation = mainFileRow.Operation; | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | // Add patch header for this file | ||
355 | if (null != patchFileRow.Patch) | ||
356 | { | ||
357 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | ||
358 | AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
359 | AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
360 | |||
361 | // Add to Patch table | ||
362 | Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); | ||
363 | if (0 == patchTable.Rows.Count) | ||
364 | { | ||
365 | patchTable.Operation = TableOperation.Add; | ||
366 | } | ||
367 | |||
368 | Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
369 | patchRow[0] = patchFileRow.File; | ||
370 | patchRow[1] = patchFileRow.Sequence; | ||
371 | |||
372 | FileInfo patchFile = new FileInfo(patchFileRow.Source); | ||
373 | patchRow[2] = (int)patchFile.Length; | ||
374 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | ||
375 | |||
376 | string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | ||
377 | if (MsiInterop.MsiMaxStreamNameLength < streamName.Length) | ||
378 | { | ||
379 | streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); | ||
380 | Table patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); | ||
381 | if (0 == patchHeadersTable.Rows.Count) | ||
382 | { | ||
383 | patchHeadersTable.Operation = TableOperation.Add; | ||
384 | } | ||
385 | Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
386 | patchHeadersRow[0] = streamName; | ||
387 | patchHeadersRow[1] = patchFileRow.Patch; | ||
388 | patchRow[5] = streamName; | ||
389 | patchHeadersRow.Operation = RowOperation.Add; | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | patchRow[4] = patchFileRow.Patch; | ||
394 | } | ||
395 | patchRow.Operation = RowOperation.Add; | ||
396 | } | ||
397 | } | ||
398 | else | ||
399 | { | ||
400 | // TODO: throw because all transform rows should have made it into the patch | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | } | ||
405 | |||
406 | if (copyFromPatch) | ||
407 | { | ||
408 | this.Output.Tables.Remove("Media"); | ||
409 | this.Output.Tables.Remove("File"); | ||
410 | this.Output.Tables.Remove("MsiFileHash"); | ||
411 | this.Output.Tables.Remove("MsiAssemblyName"); | ||
412 | } | ||
413 | } | ||
414 | } | ||
415 | finally | ||
416 | { | ||
417 | this.FileManagerCore.ActiveSubStorage = null; | ||
418 | } | ||
419 | #endif | ||
420 | this.FileFacades = allFileRows; | ||
421 | } | ||
422 | |||
423 | /// <summary> | ||
424 | /// Adds the PatchFiles action to the sequence table if it does not already exist. | ||
425 | /// </summary> | ||
426 | /// <param name="table">The sequence table to check or modify.</param> | ||
427 | /// <param name="mainTransform">The primary authoring transform.</param> | ||
428 | /// <param name="pairedTransform">The secondary patch transform.</param> | ||
429 | /// <param name="mainFileRow">The file row that contains information about the patched file.</param> | ||
430 | private void AddPatchFilesActionToSequenceTable(SequenceTable table, Output mainTransform, Output pairedTransform, Row mainFileRow) | ||
431 | { | ||
432 | // Find/add PatchFiles action (also determine sequence for it). | ||
433 | // Search mainTransform first, then pairedTransform (pairedTransform overrides). | ||
434 | bool hasPatchFilesAction = false; | ||
435 | int seqInstallFiles = 0; | ||
436 | int seqDuplicateFiles = 0; | ||
437 | string tableName = table.ToString(); | ||
438 | |||
439 | TestSequenceTableForPatchFilesAction( | ||
440 | mainTransform.Tables[tableName], | ||
441 | ref hasPatchFilesAction, | ||
442 | ref seqInstallFiles, | ||
443 | ref seqDuplicateFiles); | ||
444 | TestSequenceTableForPatchFilesAction( | ||
445 | pairedTransform.Tables[tableName], | ||
446 | ref hasPatchFilesAction, | ||
447 | ref seqInstallFiles, | ||
448 | ref seqDuplicateFiles); | ||
449 | if (!hasPatchFilesAction) | ||
450 | { | ||
451 | Table iesTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); | ||
452 | if (0 == iesTable.Rows.Count) | ||
453 | { | ||
454 | iesTable.Operation = TableOperation.Add; | ||
455 | } | ||
456 | |||
457 | Row patchAction = iesTable.CreateRow(null); | ||
458 | WixActionRow wixPatchAction = WindowsInstallerStandard.GetStandardActions()[table, "PatchFiles"]; | ||
459 | int sequence = wixPatchAction.Sequence; | ||
460 | // Test for default sequence value's appropriateness | ||
461 | if (seqInstallFiles >= sequence || (0 != seqDuplicateFiles && seqDuplicateFiles <= sequence)) | ||
462 | { | ||
463 | if (0 != seqDuplicateFiles) | ||
464 | { | ||
465 | if (seqDuplicateFiles < seqInstallFiles) | ||
466 | { | ||
467 | throw new WixException(WixErrors.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, iesTable.Name, "InstallFiles", "DuplicateFiles", wixPatchAction.Action)); | ||
468 | } | ||
469 | else | ||
470 | { | ||
471 | sequence = (seqDuplicateFiles + seqInstallFiles) / 2; | ||
472 | if (seqInstallFiles == sequence || seqDuplicateFiles == sequence) | ||
473 | { | ||
474 | throw new WixException(WixErrors.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, iesTable.Name, "InstallFiles", "DuplicateFiles", wixPatchAction.Action)); | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | else | ||
479 | { | ||
480 | sequence = seqInstallFiles + 1; | ||
481 | } | ||
482 | } | ||
483 | patchAction[0] = wixPatchAction.Action; | ||
484 | patchAction[1] = wixPatchAction.Condition; | ||
485 | patchAction[2] = sequence; | ||
486 | patchAction.Operation = RowOperation.Add; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | /// <summary> | ||
491 | /// Tests sequence table for PatchFiles and associated actions | ||
492 | /// </summary> | ||
493 | /// <param name="iesTable">The table to test.</param> | ||
494 | /// <param name="hasPatchFilesAction">Set to true if PatchFiles action is found. Left unchanged otherwise.</param> | ||
495 | /// <param name="seqInstallFiles">Set to sequence value of InstallFiles action if found. Left unchanged otherwise.</param> | ||
496 | /// <param name="seqDuplicateFiles">Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise.</param> | ||
497 | private static void TestSequenceTableForPatchFilesAction(Table iesTable, ref bool hasPatchFilesAction, ref int seqInstallFiles, ref int seqDuplicateFiles) | ||
498 | { | ||
499 | if (null != iesTable) | ||
500 | { | ||
501 | foreach (Row iesRow in iesTable.Rows) | ||
502 | { | ||
503 | if (String.Equals("PatchFiles", (string)iesRow[0], StringComparison.Ordinal)) | ||
504 | { | ||
505 | hasPatchFilesAction = true; | ||
506 | } | ||
507 | if (String.Equals("InstallFiles", (string)iesRow[0], StringComparison.Ordinal)) | ||
508 | { | ||
509 | seqInstallFiles = (int)iesRow.Fields[2].Data; | ||
510 | } | ||
511 | if (String.Equals("DuplicateFiles", (string)iesRow[0], StringComparison.Ordinal)) | ||
512 | { | ||
513 | seqDuplicateFiles = (int)iesRow.Fields[2].Data; | ||
514 | } | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | |||
519 | /// <summary> | ||
520 | /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component. | ||
521 | /// </summary> | ||
522 | /// <param name="output">The output to validate.</param> | ||
523 | private void ValidateFileRowChanges(Output transform) | ||
524 | { | ||
525 | Table componentTable = transform.Tables["Component"]; | ||
526 | Table fileTable = transform.Tables["File"]; | ||
527 | |||
528 | // There's no sense validating keypaths if the transform has no component or file table | ||
529 | if (componentTable == null || fileTable == null) | ||
530 | { | ||
531 | return; | ||
532 | } | ||
533 | |||
534 | Dictionary<string, string> componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | ||
535 | |||
536 | // Index the Component table for non-directory & non-registry key paths. | ||
537 | foreach (Row row in componentTable.Rows) | ||
538 | { | ||
539 | if (null != row.Fields[5].Data && | ||
540 | 0 != ((int)row.Fields[3].Data & MsiInterop.MsidbComponentAttributesRegistryKeyPath)) | ||
541 | { | ||
542 | componentKeyPath.Add(row.Fields[0].Data.ToString(), row.Fields[5].Data.ToString()); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | Dictionary<string, string> componentWithChangedKeyPath = new Dictionary<string, string>(); | ||
547 | Dictionary<string, string> componentWithNonKeyPathChanged = new Dictionary<string, string>(); | ||
548 | // Verify changes in the file table, now that file diffing has occurred | ||
549 | foreach (FileRow row in fileTable.Rows) | ||
550 | { | ||
551 | string fileId = row.Fields[0].Data.ToString(); | ||
552 | string componentId = row.Fields[1].Data.ToString(); | ||
553 | |||
554 | if (RowOperation.Modify != row.Operation) | ||
555 | { | ||
556 | continue; | ||
557 | } | ||
558 | |||
559 | // If this file is the keypath of a component | ||
560 | if (componentKeyPath.ContainsValue(fileId)) | ||
561 | { | ||
562 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
563 | { | ||
564 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
565 | } | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | if (!componentWithNonKeyPathChanged.ContainsKey(componentId)) | ||
570 | { | ||
571 | componentWithNonKeyPathChanged.Add(componentId, fileId); | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | |||
576 | foreach (KeyValuePair<string, string> componentFile in componentWithNonKeyPathChanged) | ||
577 | { | ||
578 | // Make sure all changes to non keypath files also had a change in the keypath. | ||
579 | if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.ContainsKey(componentFile.Key)) | ||
580 | { | ||
581 | Messaging.Instance.OnMessage(WixWarnings.UpdateOfNonKeyPathFile((string)componentFile.Value, (string)componentFile.Key, (string)componentKeyPath[componentFile.Key])); | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | |||
586 | private bool CompareFiles(string targetFile, string updatedFile) | ||
587 | { | ||
588 | bool? compared = null; | ||
589 | foreach (IBinderFileManager fileManager in this.FileManagers) | ||
590 | { | ||
591 | compared = fileManager.CompareFiles(targetFile, updatedFile); | ||
592 | if (compared.HasValue) | ||
593 | { | ||
594 | break; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | if (!compared.HasValue) | ||
599 | { | ||
600 | throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. | ||
601 | } | ||
602 | |||
603 | return compared.Value; | ||
604 | } | ||
605 | } | ||
606 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs deleted file mode 100644 index 35c8abb4..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs +++ /dev/null | |||
@@ -1,489 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.IO; | ||
9 | using System.Linq; | ||
10 | using System.Runtime.InteropServices; | ||
11 | using System.Threading; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Data.Rows; | ||
14 | using WixToolset.Extensibility; | ||
15 | |||
16 | /// <summary> | ||
17 | /// Creates cabinet files. | ||
18 | /// </summary> | ||
19 | internal class CreateCabinetsCommand : ICommand | ||
20 | { | ||
21 | private List<FileTransfer> fileTransfers; | ||
22 | |||
23 | private FileSplitCabNamesCallback newCabNamesCallBack; | ||
24 | |||
25 | private Dictionary<string, string> lastCabinetAddedToMediaTable; // Key is First Cabinet Name, Value is Last Cabinet Added in the Split Sequence | ||
26 | |||
27 | public CreateCabinetsCommand() | ||
28 | { | ||
29 | this.fileTransfers = new List<FileTransfer>(); | ||
30 | |||
31 | this.newCabNamesCallBack = NewCabNamesCallBack; | ||
32 | } | ||
33 | |||
34 | /// <summary> | ||
35 | /// Sets the number of threads to use for cabinet creation. | ||
36 | /// </summary> | ||
37 | public int CabbingThreadCount { private get; set; } | ||
38 | |||
39 | public string TempFilesLocation { private get; set; } | ||
40 | |||
41 | /// <summary> | ||
42 | /// Sets the default compression level to use for cabinets | ||
43 | /// that don't have their compression level explicitly set. | ||
44 | /// </summary> | ||
45 | public CompressionLevel DefaultCompressionLevel { private get; set; } | ||
46 | |||
47 | public Output Output { private get; set; } | ||
48 | |||
49 | public IEnumerable<IBinderFileManager> FileManagers { private get; set; } | ||
50 | |||
51 | public string LayoutDirectory { private get; set; } | ||
52 | |||
53 | public bool Compressed { private get; set; } | ||
54 | |||
55 | public Dictionary<MediaRow, IEnumerable<FileFacade>> FileRowsByCabinet { private get; set; } | ||
56 | |||
57 | public Func<MediaRow, string, string, string> ResolveMedia { private get; set; } | ||
58 | |||
59 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
60 | |||
61 | public Table WixMediaTable { private get; set; } | ||
62 | |||
63 | public IEnumerable<FileTransfer> FileTransfers { get { return this.fileTransfers; } } | ||
64 | |||
65 | /// <param name="output">Output to generate image for.</param> | ||
66 | /// <param name="fileTransfers">Array of files to be transfered.</param> | ||
67 | /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> | ||
68 | /// <param name="compressed">Flag if source image should be compressed.</param> | ||
69 | /// <returns>The uncompressed file rows.</returns> | ||
70 | public void Execute() | ||
71 | { | ||
72 | RowDictionary<WixMediaRow> wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable); | ||
73 | |||
74 | this.lastCabinetAddedToMediaTable = new Dictionary<string, string>(); | ||
75 | |||
76 | this.SetCabbingThreadCount(); | ||
77 | |||
78 | // Send Binder object to Facilitate NewCabNamesCallBack Callback | ||
79 | CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); | ||
80 | |||
81 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
82 | int MaximumCabinetSizeForLargeFileSplitting; | ||
83 | int MaximumUncompressedMediaSize; | ||
84 | this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); | ||
85 | cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; | ||
86 | cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; | ||
87 | |||
88 | foreach (var entry in this.FileRowsByCabinet) | ||
89 | { | ||
90 | MediaRow mediaRow = entry.Key; | ||
91 | IEnumerable<FileFacade> files = entry.Value; | ||
92 | CompressionLevel compressionLevel = this.DefaultCompressionLevel; | ||
93 | |||
94 | WixMediaRow wixMediaRow = null; | ||
95 | string mediaLayoutFolder = null; | ||
96 | |||
97 | if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) | ||
98 | { | ||
99 | mediaLayoutFolder = wixMediaRow.Layout; | ||
100 | |||
101 | if (wixMediaRow.CompressionLevel.HasValue) | ||
102 | { | ||
103 | compressionLevel = wixMediaRow.CompressionLevel.Value; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | string cabinetDir = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); | ||
108 | |||
109 | CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaRow, compressionLevel, files, this.fileTransfers); | ||
110 | if (null != cabinetWorkItem) | ||
111 | { | ||
112 | cabinetBuilder.Enqueue(cabinetWorkItem); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | // stop processing if an error previously occurred | ||
117 | if (Messaging.Instance.EncounteredError) | ||
118 | { | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | // create queued cabinets with multiple threads | ||
123 | cabinetBuilder.CreateQueuedCabinets(); | ||
124 | if (Messaging.Instance.EncounteredError) | ||
125 | { | ||
126 | return; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// Sets the thead count to the number of processors if the current thread count is set to 0. | ||
132 | /// </summary> | ||
133 | /// <remarks>The thread count value must be greater than 0 otherwise and exception will be thrown.</remarks> | ||
134 | private void SetCabbingThreadCount() | ||
135 | { | ||
136 | // default the number of cabbing threads to the number of processors if it wasn't specified | ||
137 | if (0 == this.CabbingThreadCount) | ||
138 | { | ||
139 | string numberOfProcessors = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); | ||
140 | |||
141 | try | ||
142 | { | ||
143 | if (null != numberOfProcessors) | ||
144 | { | ||
145 | this.CabbingThreadCount = Convert.ToInt32(numberOfProcessors, CultureInfo.InvariantCulture.NumberFormat); | ||
146 | |||
147 | if (0 >= this.CabbingThreadCount) | ||
148 | { | ||
149 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
150 | } | ||
151 | } | ||
152 | else // default to 1 if the environment variable is not set | ||
153 | { | ||
154 | this.CabbingThreadCount = 1; | ||
155 | } | ||
156 | |||
157 | Messaging.Instance.OnMessage(WixVerboses.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); | ||
158 | } | ||
159 | catch (ArgumentException) | ||
160 | { | ||
161 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
162 | } | ||
163 | catch (FormatException) | ||
164 | { | ||
165 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | |||
171 | /// <summary> | ||
172 | /// Creates a work item to create a cabinet. | ||
173 | /// </summary> | ||
174 | /// <param name="output">Output for the current database.</param> | ||
175 | /// <param name="cabinetDir">Directory to create cabinet in.</param> | ||
176 | /// <param name="mediaRow">MediaRow containing information about the cabinet.</param> | ||
177 | /// <param name="fileFacades">Collection of files in this cabinet.</param> | ||
178 | /// <param name="fileTransfers">Array of files to be transfered.</param> | ||
179 | /// <returns>created CabinetWorkItem object</returns> | ||
180 | private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable<FileFacade> fileFacades, List<FileTransfer> fileTransfers) | ||
181 | { | ||
182 | CabinetWorkItem cabinetWorkItem = null; | ||
183 | string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); | ||
184 | |||
185 | // check for an empty cabinet | ||
186 | if (!fileFacades.Any()) | ||
187 | { | ||
188 | string cabinetName = mediaRow.Cabinet; | ||
189 | |||
190 | // remove the leading '#' from the embedded cabinet name to make the warning easier to understand | ||
191 | if (cabinetName.StartsWith("#", StringComparison.Ordinal)) | ||
192 | { | ||
193 | cabinetName = cabinetName.Substring(1); | ||
194 | } | ||
195 | |||
196 | // If building a patch, remind them to run -p for torch. | ||
197 | if (OutputType.Patch == output.Type) | ||
198 | { | ||
199 | Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true)); | ||
200 | } | ||
201 | else | ||
202 | { | ||
203 | Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName)); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | ResolvedCabinet resolvedCabinet = this.ResolveCabinet(tempCabinetFileX, fileFacades); | ||
208 | |||
209 | // create a cabinet work item if it's not being skipped | ||
210 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) | ||
211 | { | ||
212 | int maxThreshold = 0; // default to the threshold for best smartcabbing (makes smallest cabinet). | ||
213 | |||
214 | cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold, compressionLevel/*, this.FileManager*/); | ||
215 | } | ||
216 | else // reuse the cabinet from the cabinet cache. | ||
217 | { | ||
218 | Messaging.Instance.OnMessage(WixVerboses.ReusingCabCache(mediaRow.SourceLineNumbers, mediaRow.Cabinet, resolvedCabinet.Path)); | ||
219 | |||
220 | try | ||
221 | { | ||
222 | // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The | ||
223 | // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that | ||
224 | // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from | ||
225 | // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) | ||
226 | // causing the project to look like it perpetually needs a rebuild until all of the reused | ||
227 | // cabinets get newer timestamps. | ||
228 | File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); | ||
229 | } | ||
230 | catch (Exception e) | ||
231 | { | ||
232 | Messaging.Instance.OnMessage(WixWarnings.CannotUpdateCabCache(mediaRow.SourceLineNumbers, resolvedCabinet.Path, e.Message)); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) | ||
237 | { | ||
238 | Table streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); | ||
239 | |||
240 | Row streamRow = streamsTable.CreateRow(mediaRow.SourceLineNumbers); | ||
241 | streamRow[0] = mediaRow.Cabinet.Substring(1); | ||
242 | streamRow[1] = resolvedCabinet.Path; | ||
243 | } | ||
244 | else | ||
245 | { | ||
246 | string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); | ||
247 | FileTransfer transfer; | ||
248 | if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer)) | ||
249 | { | ||
250 | transfer.Built = true; | ||
251 | fileTransfers.Add(transfer); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return cabinetWorkItem; | ||
256 | } | ||
257 | |||
258 | private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades) | ||
259 | { | ||
260 | ResolvedCabinet resolved = null; | ||
261 | |||
262 | List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList(); | ||
263 | |||
264 | foreach (IBinderFileManager fileManager in this.FileManagers) | ||
265 | { | ||
266 | resolved = fileManager.ResolveCabinet(cabinetPath, filesWithPath); | ||
267 | if (null != resolved) | ||
268 | { | ||
269 | break; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return resolved; | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// Delegate for Cabinet Split Callback | ||
278 | /// </summary> | ||
279 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] | ||
280 | internal delegate void FileSplitCabNamesCallback([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken); | ||
281 | |||
282 | /// <summary> | ||
283 | /// Call back to Add File Transfer for new Cab and add new Cab to Media table | ||
284 | /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe | ||
285 | /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored | ||
286 | /// </summary> | ||
287 | /// <param name="firstCabName">The name of splitting cabinet without extention e.g. "cab1".</param> | ||
288 | /// <param name="newCabName">The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"</param> | ||
289 | /// <param name="fileToken">The file token of the first file present in the splitting cabinet</param> | ||
290 | internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken) | ||
291 | { | ||
292 | // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads | ||
293 | Mutex mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); | ||
294 | try | ||
295 | { | ||
296 | if (!mutex.WaitOne(0, false)) // Check if you can get the lock | ||
297 | { | ||
298 | // Cound not get the Lock | ||
299 | Messaging.Instance.OnMessage(WixVerboses.CabinetsSplitInParallel()); | ||
300 | mutex.WaitOne(); // Wait on other thread | ||
301 | } | ||
302 | |||
303 | string firstCabinetName = firstCabName + ".cab"; | ||
304 | string newCabinetName = newCabName; | ||
305 | bool transferAdded = false; // Used for Error Handling | ||
306 | |||
307 | // Create File Transfer for new Cabinet using transfer of Base Cabinet | ||
308 | foreach (FileTransfer transfer in this.FileTransfers) | ||
309 | { | ||
310 | if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) | ||
311 | { | ||
312 | string newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); | ||
313 | string newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); | ||
314 | |||
315 | FileTransfer newTransfer; | ||
316 | if (FileTransfer.TryCreate(newCabSourcePath, newCabTargetPath, transfer.Move, "Cabinet", transfer.SourceLineNumbers, out newTransfer)) | ||
317 | { | ||
318 | newTransfer.Built = true; | ||
319 | this.fileTransfers.Add(newTransfer); | ||
320 | transferAdded = true; | ||
321 | break; | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | |||
326 | // Check if File Transfer was added | ||
327 | if (!transferAdded) | ||
328 | { | ||
329 | throw new WixException(WixErrors.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); | ||
330 | } | ||
331 | |||
332 | // Add the new Cabinets to media table using LastSequence of Base Cabinet | ||
333 | Table mediaTable = this.Output.Tables["Media"]; | ||
334 | Table wixFileTable = this.Output.Tables["WixFile"]; | ||
335 | int diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain | ||
336 | int lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain | ||
337 | bool lastSplitCabinetFound = false; // Used for Error Handling | ||
338 | |||
339 | string lastCabinetOfThisSequence = String.Empty; | ||
340 | // Get the Value of Last Cabinet Added in this split Sequence from Dictionary | ||
341 | if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) | ||
342 | { | ||
343 | // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence | ||
344 | lastCabinetOfThisSequence = firstCabinetName; | ||
345 | } | ||
346 | |||
347 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
348 | { | ||
349 | // Get details for the Last Cabinet Added in this Split Sequence | ||
350 | if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
351 | { | ||
352 | lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; | ||
353 | diskIDForLastSplitCabAdded = mediaRow.DiskId; | ||
354 | lastSplitCabinetFound = true; | ||
355 | } | ||
356 | |||
357 | // Check for Name Collision for the new Cabinet added | ||
358 | if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
359 | { | ||
360 | // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row | ||
361 | throw new WixException(WixErrors.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | // Check if the last Split Cabinet was found in the Media Table | ||
366 | if (!lastSplitCabinetFound) | ||
367 | { | ||
368 | throw new WixException(WixErrors.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); | ||
369 | } | ||
370 | |||
371 | // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort | ||
372 | // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with | ||
373 | // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction | ||
374 | MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
375 | newMediaRow.Cabinet = newCabinetName; | ||
376 | newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion | ||
377 | newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; | ||
378 | |||
379 | // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique | ||
380 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
381 | { | ||
382 | // Check if this row comes after inserted row and it is not the new cabinet inserted row | ||
383 | if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
384 | { | ||
385 | mediaRow.DiskId++; // Increment DiskID | ||
386 | } | ||
387 | } | ||
388 | |||
389 | // Now Increment DiskID for All files Rows so that they refer to the right Media Row | ||
390 | foreach (WixFileRow wixFileRow in wixFileTable.Rows) | ||
391 | { | ||
392 | // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet | ||
393 | // This check will work as we have only one large file in every splitting cabinet | ||
394 | // If we want to support splitting cabinet with more large files we need to update this code | ||
395 | if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) | ||
396 | { | ||
397 | wixFileRow.DiskId++; // Increment DiskID | ||
398 | } | ||
399 | } | ||
400 | |||
401 | // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback | ||
402 | this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; | ||
403 | |||
404 | mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails | ||
405 | } | ||
406 | finally | ||
407 | { | ||
408 | // Releasing the Mutex here | ||
409 | mutex.ReleaseMutex(); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | |||
414 | /// <summary> | ||
415 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides | ||
416 | /// </summary> | ||
417 | /// <param name="output">Output to generate image for.</param> | ||
418 | /// <param name="fileRows">The indexed file rows.</param> | ||
419 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) | ||
420 | { | ||
421 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | ||
422 | string mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
423 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
424 | int maxCabSizeForLargeFileInMB = 0; | ||
425 | int maxPreCompressedSizeInMB = 0; | ||
426 | ulong testOverFlow = 0; | ||
427 | |||
428 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
429 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
430 | if (mediaTemplateTable != null) | ||
431 | { | ||
432 | WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; | ||
433 | |||
434 | // Get the Value for Max Cab Size for File Splitting | ||
435 | try | ||
436 | { | ||
437 | // Override authored mcslfs value if environment variable is authored. | ||
438 | if (!String.IsNullOrEmpty(mcslfsString)) | ||
439 | { | ||
440 | maxCabSizeForLargeFileInMB = Int32.Parse(mcslfsString); | ||
441 | } | ||
442 | else | ||
443 | { | ||
444 | maxCabSizeForLargeFileInMB = mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting; | ||
445 | } | ||
446 | testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; | ||
447 | } | ||
448 | catch (FormatException) | ||
449 | { | ||
450 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MCSLFS", mcslfsString)); | ||
451 | } | ||
452 | catch (OverflowException) | ||
453 | { | ||
454 | throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, CompilerCore.MaxValueOfMaxCabSizeForLargeFileSplitting)); | ||
455 | } | ||
456 | |||
457 | try | ||
458 | { | ||
459 | // Override authored mums value if environment variable is authored. | ||
460 | if (!String.IsNullOrEmpty(mumsString)) | ||
461 | { | ||
462 | maxPreCompressedSizeInMB = Int32.Parse(mumsString); | ||
463 | } | ||
464 | else | ||
465 | { | ||
466 | maxPreCompressedSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; | ||
467 | } | ||
468 | testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; | ||
469 | } | ||
470 | catch (FormatException) | ||
471 | { | ||
472 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); | ||
473 | } | ||
474 | catch (OverflowException) | ||
475 | { | ||
476 | throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCompressedSizeInMB)); | ||
477 | } | ||
478 | |||
479 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; | ||
480 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | maxCabSizeForLargeFileSplitting = 0; | ||
485 | maxUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize; | ||
486 | } | ||
487 | } | ||
488 | } | ||
489 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs deleted file mode 100644 index 933a1ea8..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.IO; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Rows; | ||
11 | |||
12 | /// <summary> | ||
13 | /// Creates delta patches and updates the appropriate rows to point to the newly generated patches. | ||
14 | /// </summary> | ||
15 | internal class CreateDeltaPatchesCommand : ICommand | ||
16 | { | ||
17 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
18 | |||
19 | public Table WixPatchIdTable { private get; set; } | ||
20 | |||
21 | public string TempFilesLocation { private get; set; } | ||
22 | |||
23 | public void Execute() | ||
24 | { | ||
25 | bool optimizePatchSizeForLargeFiles = false; | ||
26 | PatchAPI.PatchInterop.PatchSymbolFlagsType apiPatchingSymbolFlags = 0; | ||
27 | |||
28 | if (null != this.WixPatchIdTable) | ||
29 | { | ||
30 | Row row = this.WixPatchIdTable.Rows[0]; | ||
31 | if (null != row) | ||
32 | { | ||
33 | if (null != row[2]) | ||
34 | { | ||
35 | optimizePatchSizeForLargeFiles = (1 == Convert.ToUInt32(row[2], CultureInfo.InvariantCulture)); | ||
36 | } | ||
37 | |||
38 | if (null != row[3]) | ||
39 | { | ||
40 | apiPatchingSymbolFlags = (PatchAPI.PatchInterop.PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture); | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | foreach (FileFacade facade in this.FileFacades) | ||
46 | { | ||
47 | if (RowOperation.Modify == facade.File.Operation && | ||
48 | 0 != (facade.WixFile.PatchAttributes & PatchAttributeType.IncludeWholeFile)) | ||
49 | { | ||
50 | string deltaBase = String.Concat("delta_", facade.File.File); | ||
51 | string deltaFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".dpf")); | ||
52 | string headerFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".phd")); | ||
53 | |||
54 | bool retainRangeWarning = false; | ||
55 | |||
56 | if (PatchAPI.PatchInterop.CreateDelta( | ||
57 | deltaFile, | ||
58 | facade.WixFile.Source, | ||
59 | facade.DeltaPatchFile.Symbols, | ||
60 | facade.DeltaPatchFile.RetainOffsets, | ||
61 | new[] { facade.WixFile.PreviousSource }, | ||
62 | facade.DeltaPatchFile.PreviousSymbols.Split(new[] { ';' }), | ||
63 | facade.DeltaPatchFile.PreviousIgnoreLengths.Split(new[] { ';' }), | ||
64 | facade.DeltaPatchFile.PreviousIgnoreOffsets.Split(new[] { ';' }), | ||
65 | facade.DeltaPatchFile.PreviousRetainLengths.Split(new[] { ';' }), | ||
66 | facade.DeltaPatchFile.PreviousRetainOffsets.Split(new[] { ';' }), | ||
67 | apiPatchingSymbolFlags, | ||
68 | optimizePatchSizeForLargeFiles, | ||
69 | out retainRangeWarning)) | ||
70 | { | ||
71 | PatchAPI.PatchInterop.ExtractDeltaHeader(deltaFile, headerFile); | ||
72 | |||
73 | facade.WixFile.Source = deltaFile; | ||
74 | facade.WixFile.DeltaPatchHeaderSource = headerFile; | ||
75 | } | ||
76 | |||
77 | if (retainRangeWarning) | ||
78 | { | ||
79 | // TODO: get patch family to add to warning message for PatchWiz parity. | ||
80 | Messaging.Instance.OnMessage(WixWarnings.RetainRangeMismatch(facade.File.SourceLineNumbers, facade.File.File)); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs deleted file mode 100644 index 5db2768b..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using WixToolset.Data; | ||
8 | using WixToolset.Data.Rows; | ||
9 | |||
10 | internal class CreateSpecialPropertiesCommand : ICommand | ||
11 | { | ||
12 | public Table PropertyTable { private get; set; } | ||
13 | |||
14 | public Table WixPropertyTable { private get; set; } | ||
15 | |||
16 | public void Execute() | ||
17 | { | ||
18 | // Create the special properties. | ||
19 | if (null != this.WixPropertyTable) | ||
20 | { | ||
21 | // Create lists of the properties that contribute to the special lists of properties. | ||
22 | SortedSet<string> adminProperties = new SortedSet<string>(); | ||
23 | SortedSet<string> secureProperties = new SortedSet<string>(); | ||
24 | SortedSet<string> hiddenProperties = new SortedSet<string>(); | ||
25 | |||
26 | foreach (WixPropertyRow wixPropertyRow in this.WixPropertyTable.Rows) | ||
27 | { | ||
28 | if (wixPropertyRow.Admin) | ||
29 | { | ||
30 | adminProperties.Add(wixPropertyRow.Id); | ||
31 | } | ||
32 | |||
33 | if (wixPropertyRow.Hidden) | ||
34 | { | ||
35 | hiddenProperties.Add(wixPropertyRow.Id); | ||
36 | } | ||
37 | |||
38 | if (wixPropertyRow.Secure) | ||
39 | { | ||
40 | secureProperties.Add(wixPropertyRow.Id); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | Table propertyTable = this.PropertyTable; | ||
45 | if (0 < adminProperties.Count) | ||
46 | { | ||
47 | PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); | ||
48 | row.Property = "AdminProperties"; | ||
49 | row.Value = String.Join(";", adminProperties); | ||
50 | } | ||
51 | |||
52 | if (0 < secureProperties.Count) | ||
53 | { | ||
54 | PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); | ||
55 | row.Property = "SecureCustomProperties"; | ||
56 | row.Value = String.Join(";", secureProperties); | ||
57 | } | ||
58 | |||
59 | if (0 < hiddenProperties.Count) | ||
60 | { | ||
61 | PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); | ||
62 | row.Property = "MsiHiddenProperties"; | ||
63 | row.Value = String.Join(";", hiddenProperties); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs deleted file mode 100644 index bee1488b..00000000 --- a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs +++ /dev/null | |||
@@ -1,225 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.ComponentModel; | ||
8 | using System.Globalization; | ||
9 | using System.IO; | ||
10 | using System.Linq; | ||
11 | using System.Runtime.InteropServices; | ||
12 | using WixToolset.Cab; | ||
13 | using WixToolset.Data; | ||
14 | using WixToolset.Data.Rows; | ||
15 | using WixToolset.MergeMod; | ||
16 | using WixToolset.Msi; | ||
17 | using WixToolset.Core.Native; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Retrieve files information and extract them from merge modules. | ||
21 | /// </summary> | ||
22 | internal class ExtractMergeModuleFilesCommand : ICommand | ||
23 | { | ||
24 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
25 | |||
26 | public Table FileTable { private get; set; } | ||
27 | |||
28 | public Table WixFileTable { private get; set; } | ||
29 | |||
30 | public Table WixMergeTable { private get; set; } | ||
31 | |||
32 | public int OutputInstallerVersion { private get; set; } | ||
33 | |||
34 | public bool SuppressLayout { private get; set; } | ||
35 | |||
36 | public string TempFilesLocation { private get; set; } | ||
37 | |||
38 | public IEnumerable<FileFacade> MergeModulesFileFacades { get; private set; } | ||
39 | |||
40 | public void Execute() | ||
41 | { | ||
42 | List<FileFacade> mergeModulesFileFacades = new List<FileFacade>(); | ||
43 | |||
44 | IMsmMerge2 merge = MsmInterop.GetMsmMerge(); | ||
45 | |||
46 | // Index all of the file rows to be able to detect collisions with files in the Merge Modules. | ||
47 | // It may seem a bit expensive to build up this index solely for the purpose of checking collisions | ||
48 | // and you may be thinking, "Surely, we must need the file rows indexed elsewhere." It turns out | ||
49 | // there are other cases where we need all the file rows indexed, however they are not common cases. | ||
50 | // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let | ||
51 | // this case be slightly more expensive because the cost of maintaining an indexed file row collection | ||
52 | // is a lot more costly for the common cases. | ||
53 | Dictionary<string, FileFacade> indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.File, StringComparer.Ordinal); | ||
54 | |||
55 | foreach (WixMergeRow wixMergeRow in this.WixMergeTable.Rows) | ||
56 | { | ||
57 | bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); | ||
58 | |||
59 | // If the module has files and creating layout | ||
60 | if (containsFiles && !this.SuppressLayout) | ||
61 | { | ||
62 | this.ExtractFilesFromMergeModule(merge, wixMergeRow); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | this.MergeModulesFileFacades = mergeModulesFileFacades; | ||
67 | } | ||
68 | |||
69 | private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) | ||
70 | { | ||
71 | bool containsFiles = false; | ||
72 | |||
73 | try | ||
74 | { | ||
75 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. | ||
76 | using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) | ||
77 | { | ||
78 | if (db.TableExists("File") && db.TableExists("Component")) | ||
79 | { | ||
80 | Dictionary<string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase); | ||
81 | |||
82 | using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`")) | ||
83 | { | ||
84 | // add each file row from the merge module into the file row collection (check for errors along the way) | ||
85 | while (true) | ||
86 | { | ||
87 | using (Record record = view.Fetch()) | ||
88 | { | ||
89 | if (null == record) | ||
90 | { | ||
91 | break; | ||
92 | } | ||
93 | |||
94 | // NOTE: this is very tricky - the merge module file rows are not added to the | ||
95 | // file table because they should not be created via idt import. Instead, these | ||
96 | // rows are created by merging in the actual modules. | ||
97 | FileRow fileRow = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); | ||
98 | fileRow.File = record[1]; | ||
99 | fileRow.Compressed = wixMergeRow.FileCompression; | ||
100 | |||
101 | WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); | ||
102 | wixFileRow.Directory = record[2]; | ||
103 | wixFileRow.DiskId = wixMergeRow.DiskId; | ||
104 | wixFileRow.PatchGroup = -1; | ||
105 | wixFileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture), Path.DirectorySeparatorChar, record[1]); | ||
106 | |||
107 | FileFacade mergeModuleFileFacade = new FileFacade(true, fileRow, wixFileRow); | ||
108 | |||
109 | FileFacade collidingFacade; | ||
110 | |||
111 | // If case-sensitive collision with another merge module or a user-authored file identifier. | ||
112 | if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) | ||
113 | { | ||
114 | Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFacade.File.File)); | ||
115 | } | ||
116 | else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module | ||
117 | { | ||
118 | Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File)); | ||
119 | } | ||
120 | else // no collision | ||
121 | { | ||
122 | mergeModulesFileFacades.Add(mergeModuleFileFacade); | ||
123 | |||
124 | // Keep updating the indexes as new rows are added. | ||
125 | indexedFileFacades.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); | ||
126 | uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); | ||
127 | } | ||
128 | |||
129 | containsFiles = true; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | // Get the summary information to detect the Schema | ||
136 | using (SummaryInformation summaryInformation = new SummaryInformation(db)) | ||
137 | { | ||
138 | string moduleInstallerVersionString = summaryInformation.GetProperty(14); | ||
139 | |||
140 | try | ||
141 | { | ||
142 | int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); | ||
143 | if (moduleInstallerVersion > this.OutputInstallerVersion) | ||
144 | { | ||
145 | Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, this.OutputInstallerVersion)); | ||
146 | } | ||
147 | } | ||
148 | catch (FormatException) | ||
149 | { | ||
150 | throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString)); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | catch (FileNotFoundException) | ||
156 | { | ||
157 | throw new WixException(WixErrors.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); | ||
158 | } | ||
159 | catch (Win32Exception) | ||
160 | { | ||
161 | throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile)); | ||
162 | } | ||
163 | |||
164 | return containsFiles; | ||
165 | } | ||
166 | |||
167 | private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeRow wixMergeRow) | ||
168 | { | ||
169 | bool moduleOpen = false; | ||
170 | short mergeLanguage; | ||
171 | |||
172 | try | ||
173 | { | ||
174 | mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); | ||
175 | } | ||
176 | catch (System.FormatException) | ||
177 | { | ||
178 | Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | try | ||
183 | { | ||
184 | merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); | ||
185 | moduleOpen = true; | ||
186 | |||
187 | string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat); | ||
188 | |||
189 | // extract the module cabinet, then explode all of the files to a temp directory | ||
190 | string moduleCabPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, safeMergeId, ".module.cab"); | ||
191 | merge.ExtractCAB(moduleCabPath); | ||
192 | |||
193 | string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId); | ||
194 | Directory.CreateDirectory(mergeIdPath); | ||
195 | |||
196 | using (WixExtractCab extractCab = new WixExtractCab()) | ||
197 | { | ||
198 | try | ||
199 | { | ||
200 | extractCab.Extract(moduleCabPath, mergeIdPath); | ||
201 | } | ||
202 | catch (FileNotFoundException) | ||
203 | { | ||
204 | throw new WixException(WixErrors.CabFileDoesNotExist(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); | ||
205 | } | ||
206 | catch | ||
207 | { | ||
208 | throw new WixException(WixErrors.CabExtractionFailed(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | catch (COMException ce) | ||
213 | { | ||
214 | throw new WixException(WixErrors.UnableToOpenModule(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile, ce.Message)); | ||
215 | } | ||
216 | finally | ||
217 | { | ||
218 | if (moduleOpen) | ||
219 | { | ||
220 | merge.CloseModule(); | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/FileFacade.cs b/src/WixToolset.Core/Bind/Databases/FileFacade.cs deleted file mode 100644 index 37115c97..00000000 --- a/src/WixToolset.Core/Bind/Databases/FileFacade.cs +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System.Collections.Generic; | ||
6 | using WixToolset.Data; | ||
7 | using WixToolset.Data.Rows; | ||
8 | |||
9 | public class FileFacade | ||
10 | { | ||
11 | public FileFacade(FileRow file, WixFileRow wixFile, WixDeltaPatchFileRow deltaPatchFile) | ||
12 | { | ||
13 | this.File = file; | ||
14 | this.WixFile = wixFile; | ||
15 | this.DeltaPatchFile = deltaPatchFile; | ||
16 | } | ||
17 | |||
18 | public FileFacade(bool fromModule, FileRow file, WixFileRow wixFile) | ||
19 | { | ||
20 | this.FromModule = fromModule; | ||
21 | this.File = file; | ||
22 | this.WixFile = wixFile; | ||
23 | } | ||
24 | |||
25 | public bool FromModule { get; private set; } | ||
26 | |||
27 | public FileRow File { get; private set; } | ||
28 | |||
29 | public WixFileRow WixFile { get; private set; } | ||
30 | |||
31 | public WixDeltaPatchFileRow DeltaPatchFile { get; private set; } | ||
32 | |||
33 | /// <summary> | ||
34 | /// Gets the set of MsiAssemblyName rows created for this file. | ||
35 | /// </summary> | ||
36 | /// <value>RowCollection of MsiAssemblyName table.</value> | ||
37 | public List<Row> AssemblyNames { get; set; } | ||
38 | |||
39 | /// <summary> | ||
40 | /// Gets or sets the MsiFileHash row for this file. | ||
41 | /// </summary> | ||
42 | public Row Hash { get; set; } | ||
43 | } | ||
44 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs b/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs deleted file mode 100644 index b6bcd3af..00000000 --- a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs +++ /dev/null | |||
@@ -1,148 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Rows; | ||
11 | |||
12 | internal class GetFileFacadesCommand : ICommand | ||
13 | { | ||
14 | public Table FileTable { private get; set; } | ||
15 | |||
16 | public Table WixFileTable { private get; set; } | ||
17 | |||
18 | public Table WixDeltaPatchFileTable { private get; set; } | ||
19 | |||
20 | public Table WixDeltaPatchSymbolPathsTable { private get; set; } | ||
21 | |||
22 | public List<FileFacade> FileFacades { get; private set; } | ||
23 | |||
24 | public void Execute() | ||
25 | { | ||
26 | List<FileFacade> facades = new List<FileFacade>(this.FileTable.Rows.Count); | ||
27 | |||
28 | RowDictionary<WixFileRow> wixFiles = new RowDictionary<WixFileRow>(this.WixFileTable); | ||
29 | RowDictionary<WixDeltaPatchFileRow> deltaPatchFiles = new RowDictionary<WixDeltaPatchFileRow>(this.WixDeltaPatchFileTable); | ||
30 | |||
31 | foreach (FileRow file in this.FileTable.Rows) | ||
32 | { | ||
33 | WixDeltaPatchFileRow deltaPatchFile = null; | ||
34 | |||
35 | deltaPatchFiles.TryGetValue(file.File, out deltaPatchFile); | ||
36 | |||
37 | facades.Add(new FileFacade(file, wixFiles[file.File], deltaPatchFile)); | ||
38 | } | ||
39 | |||
40 | if (null != this.WixDeltaPatchSymbolPathsTable) | ||
41 | { | ||
42 | this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades); | ||
43 | } | ||
44 | |||
45 | this.FileFacades = facades; | ||
46 | } | ||
47 | |||
48 | /// <summary> | ||
49 | /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows. | ||
50 | /// </summary> | ||
51 | public RowDictionary<WixDeltaPatchFileRow> ResolveDeltaPatchSymbolPaths(RowDictionary<WixDeltaPatchFileRow> deltaPatchFiles, IEnumerable<FileFacade> facades) | ||
52 | { | ||
53 | ILookup<string, FileFacade> filesByComponent = null; | ||
54 | ILookup<string, FileFacade> filesByDirectory = null; | ||
55 | ILookup<string, FileFacade> filesByDiskId = null; | ||
56 | |||
57 | foreach (WixDeltaPatchSymbolPathsRow row in this.WixDeltaPatchSymbolPathsTable.RowsAs<WixDeltaPatchSymbolPathsRow>().OrderBy(r => r.Type)) | ||
58 | { | ||
59 | switch (row.Type) | ||
60 | { | ||
61 | case SymbolPathType.File: | ||
62 | this.MergeSymbolPaths(row, deltaPatchFiles[row.Id]); | ||
63 | break; | ||
64 | |||
65 | case SymbolPathType.Component: | ||
66 | if (null == filesByComponent) | ||
67 | { | ||
68 | filesByComponent = facades.ToLookup(f => f.File.Component); | ||
69 | } | ||
70 | |||
71 | foreach (FileFacade facade in filesByComponent[row.Id]) | ||
72 | { | ||
73 | this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); | ||
74 | } | ||
75 | break; | ||
76 | |||
77 | case SymbolPathType.Directory: | ||
78 | if (null == filesByDirectory) | ||
79 | { | ||
80 | filesByDirectory = facades.ToLookup(f => f.WixFile.Directory); | ||
81 | } | ||
82 | |||
83 | foreach (FileFacade facade in filesByDirectory[row.Id]) | ||
84 | { | ||
85 | this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); | ||
86 | } | ||
87 | break; | ||
88 | |||
89 | case SymbolPathType.Media: | ||
90 | if (null == filesByDiskId) | ||
91 | { | ||
92 | filesByDiskId = facades.ToLookup(f => f.WixFile.DiskId.ToString(CultureInfo.InvariantCulture)); | ||
93 | } | ||
94 | |||
95 | foreach (FileFacade facade in filesByDiskId[row.Id]) | ||
96 | { | ||
97 | this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); | ||
98 | } | ||
99 | break; | ||
100 | |||
101 | case SymbolPathType.Product: | ||
102 | foreach (WixDeltaPatchFileRow fileRow in deltaPatchFiles.Values) | ||
103 | { | ||
104 | this.MergeSymbolPaths(row, fileRow); | ||
105 | } | ||
106 | break; | ||
107 | |||
108 | default: | ||
109 | // error | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | return deltaPatchFiles; | ||
115 | } | ||
116 | |||
117 | /// <summary> | ||
118 | /// Merge data from a row in the WixPatchSymbolsPaths table into an associated WixDeltaPatchFile row. | ||
119 | /// </summary> | ||
120 | /// <param name="row">Row from the WixPatchSymbolsPaths table.</param> | ||
121 | /// <param name="file">FileRow into which to set symbol information.</param> | ||
122 | /// <comment>This includes PreviousData as well.</comment> | ||
123 | private void MergeSymbolPaths(WixDeltaPatchSymbolPathsRow row, WixDeltaPatchFileRow file) | ||
124 | { | ||
125 | if (null == file.Symbols) | ||
126 | { | ||
127 | file.Symbols = row.SymbolPaths; | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | file.Symbols = String.Concat(file.Symbols, ";", row.SymbolPaths); | ||
132 | } | ||
133 | |||
134 | Field field = row.Fields[2]; | ||
135 | if (null != field.PreviousData) | ||
136 | { | ||
137 | if (null == file.PreviousSymbols) | ||
138 | { | ||
139 | file.PreviousSymbols = field.PreviousData; | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData); | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs b/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs deleted file mode 100644 index 035ef059..00000000 --- a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs +++ /dev/null | |||
@@ -1,350 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Collections.Specialized; | ||
8 | using System.ComponentModel; | ||
9 | using System.Diagnostics; | ||
10 | using System.Globalization; | ||
11 | using System.IO; | ||
12 | using System.Linq; | ||
13 | using System.Runtime.InteropServices; | ||
14 | using System.Text; | ||
15 | using System.Xml; | ||
16 | using System.Xml.XPath; | ||
17 | using WixToolset.Clr.Interop; | ||
18 | using WixToolset.Data; | ||
19 | using WixToolset.Data.Rows; | ||
20 | using WixToolset.MergeMod; | ||
21 | using WixToolset.Msi; | ||
22 | using WixToolset.Core.Native; | ||
23 | |||
24 | /// <summary> | ||
25 | /// Update file information. | ||
26 | /// </summary> | ||
27 | internal class MergeModulesCommand : ICommand | ||
28 | { | ||
29 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
30 | |||
31 | public Output Output { private get; set; } | ||
32 | |||
33 | public string OutputPath { private get; set; } | ||
34 | |||
35 | public IEnumerable<string> SuppressedTableNames { private get; set; } | ||
36 | |||
37 | public string TempFilesLocation { private get; set; } | ||
38 | |||
39 | public void Execute() | ||
40 | { | ||
41 | Debug.Assert(OutputType.Product == this.Output.Type); | ||
42 | |||
43 | Table wixMergeTable = this.Output.Tables["WixMerge"]; | ||
44 | Table wixFeatureModulesTable = this.Output.Tables["WixFeatureModules"]; | ||
45 | |||
46 | // check for merge rows to see if there is any work to do | ||
47 | if (null == wixMergeTable || 0 == wixMergeTable.Rows.Count) | ||
48 | { | ||
49 | return; | ||
50 | } | ||
51 | |||
52 | IMsmMerge2 merge = null; | ||
53 | bool commit = true; | ||
54 | bool logOpen = false; | ||
55 | bool databaseOpen = false; | ||
56 | string logPath = null; | ||
57 | try | ||
58 | { | ||
59 | merge = MsmInterop.GetMsmMerge(); | ||
60 | |||
61 | logPath = Path.Combine(this.TempFilesLocation, "merge.log"); | ||
62 | merge.OpenLog(logPath); | ||
63 | logOpen = true; | ||
64 | |||
65 | merge.OpenDatabase(this.OutputPath); | ||
66 | databaseOpen = true; | ||
67 | |||
68 | // process all the merge rows | ||
69 | foreach (WixMergeRow wixMergeRow in wixMergeTable.Rows) | ||
70 | { | ||
71 | bool moduleOpen = false; | ||
72 | |||
73 | try | ||
74 | { | ||
75 | short mergeLanguage; | ||
76 | |||
77 | try | ||
78 | { | ||
79 | mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); | ||
80 | } | ||
81 | catch (System.FormatException) | ||
82 | { | ||
83 | Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); | ||
84 | continue; | ||
85 | } | ||
86 | |||
87 | Messaging.Instance.OnMessage(WixVerboses.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage)); | ||
88 | merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); | ||
89 | moduleOpen = true; | ||
90 | |||
91 | // If there is merge configuration data, create a callback object to contain it all. | ||
92 | ConfigurationCallback callback = null; | ||
93 | if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData)) | ||
94 | { | ||
95 | callback = new ConfigurationCallback(wixMergeRow.ConfigurationData); | ||
96 | } | ||
97 | |||
98 | // merge the module into the database that's being built | ||
99 | Messaging.Instance.OnMessage(WixVerboses.MergingMergeModule(wixMergeRow.SourceFile)); | ||
100 | merge.MergeEx(wixMergeRow.Feature, wixMergeRow.Directory, callback); | ||
101 | |||
102 | // connect any non-primary features | ||
103 | if (null != wixFeatureModulesTable) | ||
104 | { | ||
105 | foreach (Row row in wixFeatureModulesTable.Rows) | ||
106 | { | ||
107 | if (wixMergeRow.Id == (string)row[1]) | ||
108 | { | ||
109 | Messaging.Instance.OnMessage(WixVerboses.ConnectingMergeModule(wixMergeRow.SourceFile, (string)row[0])); | ||
110 | merge.Connect((string)row[0]); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | catch (COMException) | ||
116 | { | ||
117 | commit = false; | ||
118 | } | ||
119 | finally | ||
120 | { | ||
121 | IMsmErrors mergeErrors = merge.Errors; | ||
122 | |||
123 | // display all the errors encountered during the merge operations for this module | ||
124 | for (int i = 1; i <= mergeErrors.Count; i++) | ||
125 | { | ||
126 | IMsmError mergeError = mergeErrors[i]; | ||
127 | StringBuilder databaseKeys = new StringBuilder(); | ||
128 | StringBuilder moduleKeys = new StringBuilder(); | ||
129 | |||
130 | // build a string of the database keys | ||
131 | for (int j = 1; j <= mergeError.DatabaseKeys.Count; j++) | ||
132 | { | ||
133 | if (1 != j) | ||
134 | { | ||
135 | databaseKeys.Append(';'); | ||
136 | } | ||
137 | databaseKeys.Append(mergeError.DatabaseKeys[j]); | ||
138 | } | ||
139 | |||
140 | // build a string of the module keys | ||
141 | for (int j = 1; j <= mergeError.ModuleKeys.Count; j++) | ||
142 | { | ||
143 | if (1 != j) | ||
144 | { | ||
145 | moduleKeys.Append(';'); | ||
146 | } | ||
147 | moduleKeys.Append(mergeError.ModuleKeys[j]); | ||
148 | } | ||
149 | |||
150 | // display the merge error based on the msm error type | ||
151 | switch (mergeError.Type) | ||
152 | { | ||
153 | case MsmErrorType.msmErrorExclusion: | ||
154 | Messaging.Instance.OnMessage(WixErrors.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString())); | ||
155 | break; | ||
156 | case MsmErrorType.msmErrorFeatureRequired: | ||
157 | Messaging.Instance.OnMessage(WixErrors.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id)); | ||
158 | break; | ||
159 | case MsmErrorType.msmErrorLanguageFailed: | ||
160 | Messaging.Instance.OnMessage(WixErrors.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); | ||
161 | break; | ||
162 | case MsmErrorType.msmErrorLanguageUnsupported: | ||
163 | Messaging.Instance.OnMessage(WixErrors.MergeLanguageUnsupported(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); | ||
164 | break; | ||
165 | case MsmErrorType.msmErrorResequenceMerge: | ||
166 | Messaging.Instance.OnMessage(WixWarnings.MergeRescheduledAction(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile)); | ||
167 | break; | ||
168 | case MsmErrorType.msmErrorTableMerge: | ||
169 | if ("_Validation" != mergeError.DatabaseTable) // ignore merge errors in the _Validation table | ||
170 | { | ||
171 | Messaging.Instance.OnMessage(WixWarnings.MergeTableFailed(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile)); | ||
172 | } | ||
173 | break; | ||
174 | case MsmErrorType.msmErrorPlatformMismatch: | ||
175 | Messaging.Instance.OnMessage(WixErrors.MergePlatformMismatch(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); | ||
176 | break; | ||
177 | default: | ||
178 | Messaging.Instance.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorWithType, Enum.GetName(typeof(MsmErrorType), mergeError.Type), logPath), "InvalidOperationException", Environment.StackTrace)); | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | if (0 >= mergeErrors.Count && !commit) | ||
184 | { | ||
185 | Messaging.Instance.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorInSourceFile, wixMergeRow.SourceFile, logPath), "InvalidOperationException", Environment.StackTrace)); | ||
186 | } | ||
187 | |||
188 | if (moduleOpen) | ||
189 | { | ||
190 | merge.CloseModule(); | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | finally | ||
196 | { | ||
197 | if (databaseOpen) | ||
198 | { | ||
199 | merge.CloseDatabase(commit); | ||
200 | } | ||
201 | |||
202 | if (logOpen) | ||
203 | { | ||
204 | merge.CloseLog(); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | // stop processing if an error previously occurred | ||
209 | if (Messaging.Instance.EncounteredError) | ||
210 | { | ||
211 | return; | ||
212 | } | ||
213 | |||
214 | using (Database db = new Database(this.OutputPath, OpenDatabase.Direct)) | ||
215 | { | ||
216 | Table suppressActionTable = this.Output.Tables["WixSuppressAction"]; | ||
217 | |||
218 | // suppress individual actions | ||
219 | if (null != suppressActionTable) | ||
220 | { | ||
221 | foreach (Row row in suppressActionTable.Rows) | ||
222 | { | ||
223 | if (db.TableExists((string)row[0])) | ||
224 | { | ||
225 | string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]); | ||
226 | |||
227 | using (View view = db.OpenExecuteView(query)) | ||
228 | { | ||
229 | using (Record record = view.Fetch()) | ||
230 | { | ||
231 | if (null != record) | ||
232 | { | ||
233 | Messaging.Instance.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString())); | ||
234 | view.Modify(ModifyView.Delete, record); | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | // query for merge module actions in suppressed sequences and drop them | ||
243 | foreach (string tableName in this.SuppressedTableNames) | ||
244 | { | ||
245 | if (!db.TableExists(tableName)) | ||
246 | { | ||
247 | continue; | ||
248 | } | ||
249 | |||
250 | using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName))) | ||
251 | { | ||
252 | while (true) | ||
253 | { | ||
254 | using (Record resultRecord = view.Fetch()) | ||
255 | { | ||
256 | if (null == resultRecord) | ||
257 | { | ||
258 | break; | ||
259 | } | ||
260 | |||
261 | Messaging.Instance.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName)); | ||
262 | } | ||
263 | } | ||
264 | } | ||
265 | |||
266 | // drop suppressed sequences | ||
267 | using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName))) | ||
268 | { | ||
269 | } | ||
270 | |||
271 | // delete the validation rows | ||
272 | using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?"))) | ||
273 | { | ||
274 | using (Record record = new Record(1)) | ||
275 | { | ||
276 | record.SetString(1, tableName); | ||
277 | view.Execute(record); | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | // now update the Attributes column for the files from the Merge Modules | ||
283 | Messaging.Instance.OnMessage(WixVerboses.ResequencingMergeModuleFiles()); | ||
284 | using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) | ||
285 | { | ||
286 | foreach (FileFacade file in this.FileFacades) | ||
287 | { | ||
288 | if (!file.FromModule) | ||
289 | { | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | using (Record record = new Record(1)) | ||
294 | { | ||
295 | record.SetString(1, file.File.File); | ||
296 | view.Execute(record); | ||
297 | } | ||
298 | |||
299 | using (Record recordUpdate = view.Fetch()) | ||
300 | { | ||
301 | if (null == recordUpdate) | ||
302 | { | ||
303 | throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module."); | ||
304 | } | ||
305 | |||
306 | recordUpdate.SetInteger(1, file.File.Sequence); | ||
307 | |||
308 | // update the file attributes to match the compression specified | ||
309 | // on the Merge element or on the Package element | ||
310 | int attributes = 0; | ||
311 | |||
312 | // get the current value if its not null | ||
313 | if (!recordUpdate.IsNull(2)) | ||
314 | { | ||
315 | attributes = recordUpdate.GetInteger(2); | ||
316 | } | ||
317 | |||
318 | if (YesNoType.Yes == file.File.Compressed) | ||
319 | { | ||
320 | // these are mutually exclusive | ||
321 | attributes |= MsiInterop.MsidbFileAttributesCompressed; | ||
322 | attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | ||
323 | } | ||
324 | else if (YesNoType.No == file.File.Compressed) | ||
325 | { | ||
326 | // these are mutually exclusive | ||
327 | attributes |= MsiInterop.MsidbFileAttributesNoncompressed; | ||
328 | attributes &= ~MsiInterop.MsidbFileAttributesCompressed; | ||
329 | } | ||
330 | else // not specified | ||
331 | { | ||
332 | Debug.Assert(YesNoType.NotSet == file.File.Compressed); | ||
333 | |||
334 | // clear any compression bits | ||
335 | attributes &= ~MsiInterop.MsidbFileAttributesCompressed; | ||
336 | attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | ||
337 | } | ||
338 | |||
339 | recordUpdate.SetInteger(2, attributes); | ||
340 | |||
341 | view.Modify(ModifyView.Update, recordUpdate); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | db.Commit(); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs deleted file mode 100644 index dd7b85b7..00000000 --- a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs +++ /dev/null | |||
@@ -1,115 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Collections.Generic; | ||
8 | using System.IO; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Rows; | ||
11 | using WixToolset.Msi; | ||
12 | using WixToolset.Core.Native; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Defines the file transfers necessary to layout the uncompressed files. | ||
16 | /// </summary> | ||
17 | internal class ProcessUncompressedFilesCommand : ICommand | ||
18 | { | ||
19 | public string DatabasePath { private get; set; } | ||
20 | |||
21 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
22 | |||
23 | public RowDictionary<MediaRow> MediaRows { private get; set; } | ||
24 | |||
25 | public string LayoutDirectory { private get; set; } | ||
26 | |||
27 | public bool Compressed { private get; set; } | ||
28 | |||
29 | public bool LongNamesInImage { private get; set; } | ||
30 | |||
31 | public Func<MediaRow, string, string, string> ResolveMedia { private get; set; } | ||
32 | |||
33 | public Table WixMediaTable { private get; set; } | ||
34 | |||
35 | public IEnumerable<FileTransfer> FileTransfers { get; private set; } | ||
36 | |||
37 | public void Execute() | ||
38 | { | ||
39 | List<FileTransfer> fileTransfers = new List<FileTransfer>(); | ||
40 | |||
41 | Hashtable directories = new Hashtable(); | ||
42 | |||
43 | RowDictionary<WixMediaRow> wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable); | ||
44 | |||
45 | using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) | ||
46 | { | ||
47 | using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) | ||
48 | { | ||
49 | while (true) | ||
50 | { | ||
51 | using (Record directoryRecord = directoryView.Fetch()) | ||
52 | { | ||
53 | if (null == directoryRecord) | ||
54 | { | ||
55 | break; | ||
56 | } | ||
57 | |||
58 | string sourceName = Installer.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage); | ||
59 | |||
60 | directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?")) | ||
66 | { | ||
67 | using (Record fileQueryRecord = new Record(1)) | ||
68 | { | ||
69 | // for each file in the array of uncompressed files | ||
70 | foreach (FileFacade facade in this.FileFacades) | ||
71 | { | ||
72 | MediaRow mediaRow = this.MediaRows.Get(facade.WixFile.DiskId); | ||
73 | string relativeFileLayoutPath = null; | ||
74 | |||
75 | WixMediaRow wixMediaRow = null; | ||
76 | string mediaLayoutFolder = null; | ||
77 | |||
78 | if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) | ||
79 | { | ||
80 | mediaLayoutFolder = wixMediaRow.Layout; | ||
81 | } | ||
82 | |||
83 | string mediaLayoutDirectory = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); | ||
84 | |||
85 | // setup up the query record and find the appropriate file in the | ||
86 | // previously executed file view | ||
87 | fileQueryRecord[1] = facade.File.File; | ||
88 | fileView.Execute(fileQueryRecord); | ||
89 | |||
90 | using (Record fileRecord = fileView.Fetch()) | ||
91 | { | ||
92 | if (null == fileRecord) | ||
93 | { | ||
94 | throw new WixException(WixErrors.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.File)); | ||
95 | } | ||
96 | |||
97 | relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); | ||
98 | } | ||
99 | |||
100 | // finally put together the base media layout path and the relative file layout path | ||
101 | string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); | ||
102 | FileTransfer transfer; | ||
103 | if (FileTransfer.TryCreate(facade.WixFile.Source, fileLayoutPath, false, "File", facade.File.SourceLineNumbers, out transfer)) | ||
104 | { | ||
105 | fileTransfers.Add(transfer); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | this.FileTransfers = fileTransfers; | ||
113 | } | ||
114 | } | ||
115 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs b/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs deleted file mode 100644 index 9e17ee02..00000000 --- a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs +++ /dev/null | |||
@@ -1,80 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using WixToolset.Data; | ||
8 | using WixToolset.Data.Rows; | ||
9 | |||
10 | internal class UpdateControlTextCommand : ICommand | ||
11 | { | ||
12 | public Table BBControlTable { private get; set; } | ||
13 | |||
14 | public Table WixBBControlTable { private get; set; } | ||
15 | |||
16 | public Table ControlTable { private get; set; } | ||
17 | |||
18 | public Table WixControlTable { private get; set; } | ||
19 | |||
20 | public void Execute() | ||
21 | { | ||
22 | if (null != this.WixBBControlTable) | ||
23 | { | ||
24 | RowDictionary<BBControlRow> bbControlRows = new RowDictionary<BBControlRow>(this.BBControlTable); | ||
25 | foreach (Row wixRow in this.WixBBControlTable.Rows) | ||
26 | { | ||
27 | BBControlRow bbControlRow = bbControlRows.Get(wixRow.GetPrimaryKey()); | ||
28 | bbControlRow.Text = this.ReadTextFile(bbControlRow.SourceLineNumbers, wixRow.FieldAsString(2)); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | if (null != this.WixControlTable) | ||
33 | { | ||
34 | RowDictionary<ControlRow> controlRows = new RowDictionary<ControlRow>(this.ControlTable); | ||
35 | foreach (Row wixRow in this.WixControlTable.Rows) | ||
36 | { | ||
37 | ControlRow controlRow = controlRows.Get(wixRow.GetPrimaryKey()); | ||
38 | controlRow.Text = this.ReadTextFile(controlRow.SourceLineNumbers, wixRow.FieldAsString(2)); | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /// <summary> | ||
44 | /// Reads a text file and returns the contents. | ||
45 | /// </summary> | ||
46 | /// <param name="sourceLineNumbers">Source line numbers for row from source.</param> | ||
47 | /// <param name="source">Source path to file to read.</param> | ||
48 | /// <returns>Text string read from file.</returns> | ||
49 | private string ReadTextFile(SourceLineNumber sourceLineNumbers, string source) | ||
50 | { | ||
51 | string text = null; | ||
52 | |||
53 | try | ||
54 | { | ||
55 | using (StreamReader reader = new StreamReader(source)) | ||
56 | { | ||
57 | text = reader.ReadToEnd(); | ||
58 | } | ||
59 | } | ||
60 | catch (DirectoryNotFoundException e) | ||
61 | { | ||
62 | Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); | ||
63 | } | ||
64 | catch (FileNotFoundException e) | ||
65 | { | ||
66 | Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); | ||
67 | } | ||
68 | catch (IOException e) | ||
69 | { | ||
70 | Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); | ||
71 | } | ||
72 | catch (NotSupportedException) | ||
73 | { | ||
74 | Messaging.Instance.OnMessage(WixErrors.FileNotFound(sourceLineNumbers, source)); | ||
75 | } | ||
76 | |||
77 | return text; | ||
78 | } | ||
79 | } | ||
80 | } | ||
diff --git a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs b/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs deleted file mode 100644 index 36818afa..00000000 --- a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs +++ /dev/null | |||
@@ -1,532 +0,0 @@ | |||
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.Bind.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Collections.Specialized; | ||
8 | using System.ComponentModel; | ||
9 | using System.Globalization; | ||
10 | using System.IO; | ||
11 | using System.Linq; | ||
12 | using System.Xml; | ||
13 | using System.Xml.XPath; | ||
14 | using WixToolset.Clr.Interop; | ||
15 | using WixToolset.Data; | ||
16 | using WixToolset.Data.Rows; | ||
17 | using WixToolset.Msi; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Update file information. | ||
21 | /// </summary> | ||
22 | internal class UpdateFileFacadesCommand : ICommand | ||
23 | { | ||
24 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
25 | |||
26 | public IEnumerable<FileFacade> UpdateFileFacades { private get; set; } | ||
27 | |||
28 | public string ModularizationGuid { private get; set; } | ||
29 | |||
30 | public Output Output { private get; set; } | ||
31 | |||
32 | public bool OverwriteHash { private get; set; } | ||
33 | |||
34 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
35 | |||
36 | public IDictionary<string, string> VariableCache { private get; set; } | ||
37 | |||
38 | public void Execute() | ||
39 | { | ||
40 | foreach (FileFacade file in this.UpdateFileFacades) | ||
41 | { | ||
42 | this.UpdateFileFacade(file); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | private void UpdateFileFacade(FileFacade file) | ||
47 | { | ||
48 | FileInfo fileInfo = null; | ||
49 | try | ||
50 | { | ||
51 | fileInfo = new FileInfo(file.WixFile.Source); | ||
52 | } | ||
53 | catch (ArgumentException) | ||
54 | { | ||
55 | Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
56 | return; | ||
57 | } | ||
58 | catch (PathTooLongException) | ||
59 | { | ||
60 | Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
61 | return; | ||
62 | } | ||
63 | catch (NotSupportedException) | ||
64 | { | ||
65 | Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | if (!fileInfo.Exists) | ||
70 | { | ||
71 | Messaging.Instance.OnMessage(WixErrors.CannotFindFile(file.File.SourceLineNumbers, file.File.File, file.File.FileName, file.WixFile.Source)); | ||
72 | return; | ||
73 | } | ||
74 | |||
75 | using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||
76 | { | ||
77 | if (Int32.MaxValue < fileStream.Length) | ||
78 | { | ||
79 | throw new WixException(WixErrors.FileTooLarge(file.File.SourceLineNumbers, file.WixFile.Source)); | ||
80 | } | ||
81 | |||
82 | file.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); | ||
83 | } | ||
84 | |||
85 | string version = null; | ||
86 | string language = null; | ||
87 | try | ||
88 | { | ||
89 | Installer.GetFileVersion(fileInfo.FullName, out version, out language); | ||
90 | } | ||
91 | catch (Win32Exception e) | ||
92 | { | ||
93 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | ||
94 | { | ||
95 | throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, e.Message)); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | // If there is no version, it is assumed there is no language because it won't matter in the versioning of the install. | ||
104 | if (String.IsNullOrEmpty(version)) // unversioned files have their hashes added to the MsiFileHash table | ||
105 | { | ||
106 | if (!this.OverwriteHash) | ||
107 | { | ||
108 | // not overwriting hash, so don't do the rest of these options. | ||
109 | } | ||
110 | else if (null != file.File.Version) | ||
111 | { | ||
112 | // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks | ||
113 | // very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up. | ||
114 | // That's a reasonable thought but companion file usage is usually pretty rare so we'd be doing something expensive (indexing | ||
115 | // all the file rows) for a relatively uncommon situation. Let's not do that. | ||
116 | // | ||
117 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version | ||
118 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. | ||
119 | if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) | ||
120 | { | ||
121 | Messaging.Instance.OnMessage(WixWarnings.DefaultVersionUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Version, file.File.File)); | ||
122 | } | ||
123 | } | ||
124 | else | ||
125 | { | ||
126 | if (null != file.File.Language) | ||
127 | { | ||
128 | Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); | ||
129 | } | ||
130 | |||
131 | int[] hash; | ||
132 | try | ||
133 | { | ||
134 | Installer.GetFileHash(fileInfo.FullName, 0, out hash); | ||
135 | } | ||
136 | catch (Win32Exception e) | ||
137 | { | ||
138 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | ||
139 | { | ||
140 | throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, fileInfo.FullName, e.Message)); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | if (null == file.Hash) | ||
149 | { | ||
150 | Table msiFileHashTable = this.Output.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
151 | file.Hash = msiFileHashTable.CreateRow(file.File.SourceLineNumbers); | ||
152 | } | ||
153 | |||
154 | file.Hash[0] = file.File.File; | ||
155 | file.Hash[1] = 0; | ||
156 | file.Hash[2] = hash[0]; | ||
157 | file.Hash[3] = hash[1]; | ||
158 | file.Hash[4] = hash[2]; | ||
159 | file.Hash[5] = hash[3]; | ||
160 | } | ||
161 | } | ||
162 | else // update the file row with the version and language information. | ||
163 | { | ||
164 | // If no version was provided by the user, use the version from the file itself. | ||
165 | // This is the most common case. | ||
166 | if (String.IsNullOrEmpty(file.File.Version)) | ||
167 | { | ||
168 | file.File.Version = version; | ||
169 | } | ||
170 | else if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) // this looks expensive, but see explanation below. | ||
171 | { | ||
172 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching | ||
173 | // the version value). We didn't find it so, we will override the default version they provided with the actual | ||
174 | // version from the file itself. Now, I know it looks expensive to search through all the file rows trying to match | ||
175 | // on the Id. However, the alternative is to build a big index of all file rows to do look ups. Since this case | ||
176 | // where the file version is already present is rare (companion files are pretty uncommon), we'll do the more | ||
177 | // CPU intensive search to save on the memory intensive index that wouldn't be used much. | ||
178 | // | ||
179 | // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. | ||
180 | // That's typically even more rare than companion files so again, no index, just search. | ||
181 | file.File.Version = version; | ||
182 | } | ||
183 | |||
184 | if (!String.IsNullOrEmpty(file.File.Language) && String.IsNullOrEmpty(language)) | ||
185 | { | ||
186 | Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForVersionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); | ||
187 | } | ||
188 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. | ||
189 | { | ||
190 | file.File.Language = language; | ||
191 | } | ||
192 | |||
193 | // Populate the binder variables for this file information if requested. | ||
194 | if (null != this.VariableCache) | ||
195 | { | ||
196 | if (!String.IsNullOrEmpty(file.File.Version)) | ||
197 | { | ||
198 | string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)); | ||
199 | this.VariableCache[key] = file.File.Version; | ||
200 | } | ||
201 | |||
202 | if (!String.IsNullOrEmpty(file.File.Language)) | ||
203 | { | ||
204 | string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, ModularizationGuid, file.File.File)); | ||
205 | this.VariableCache[key] = file.File.Language; | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // If this is a CLR assembly, load the assembly and get the assembly name information | ||
211 | if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType) | ||
212 | { | ||
213 | bool targetNetfx1 = false; | ||
214 | StringDictionary assemblyNameValues = new StringDictionary(); | ||
215 | |||
216 | ClrInterop.IReferenceIdentity referenceIdentity = null; | ||
217 | Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid; | ||
218 | uint result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out referenceIdentity); | ||
219 | if (0 == result && null != referenceIdentity) | ||
220 | { | ||
221 | string imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion"); | ||
222 | if (null != imageRuntimeVersion) | ||
223 | { | ||
224 | targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase); | ||
225 | } | ||
226 | |||
227 | string culture = referenceIdentity.GetAttribute(null, "Culture") ?? "neutral"; | ||
228 | assemblyNameValues.Add("Culture", culture); | ||
229 | |||
230 | string name = referenceIdentity.GetAttribute(null, "Name"); | ||
231 | if (null != name) | ||
232 | { | ||
233 | assemblyNameValues.Add("Name", name); | ||
234 | } | ||
235 | |||
236 | string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture"); | ||
237 | if (null != processorArchitecture) | ||
238 | { | ||
239 | assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture); | ||
240 | } | ||
241 | |||
242 | string publicKeyToken = referenceIdentity.GetAttribute(null, "PublicKeyToken"); | ||
243 | if (null != publicKeyToken) | ||
244 | { | ||
245 | bool publicKeyIsNeutral = (String.Equals(publicKeyToken, "neutral", StringComparison.OrdinalIgnoreCase)); | ||
246 | |||
247 | // Managed code expects "null" instead of "neutral", and | ||
248 | // this won't be installed to the GAC since it's not signed anyway. | ||
249 | assemblyNameValues.Add("publicKeyToken", publicKeyIsNeutral ? "null" : publicKeyToken.ToUpperInvariant()); | ||
250 | assemblyNameValues.Add("publicKeyTokenPreservedCase", publicKeyIsNeutral ? "null" : publicKeyToken); | ||
251 | } | ||
252 | else if (file.WixFile.AssemblyApplication == null) | ||
253 | { | ||
254 | throw new WixException(WixErrors.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component)); | ||
255 | } | ||
256 | |||
257 | string assemblyVersion = referenceIdentity.GetAttribute(null, "Version"); | ||
258 | if (null != version) | ||
259 | { | ||
260 | assemblyNameValues.Add("Version", assemblyVersion); | ||
261 | } | ||
262 | } | ||
263 | else | ||
264 | { | ||
265 | Messaging.Instance.OnMessage(WixErrors.InvalidAssemblyFile(file.File.SourceLineNumbers, fileInfo.FullName, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", result))); | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
270 | if (assemblyNameValues.ContainsKey("name")) | ||
271 | { | ||
272 | this.SetMsiAssemblyName(assemblyNameTable, file, "name", assemblyNameValues["name"]); | ||
273 | } | ||
274 | |||
275 | if (!String.IsNullOrEmpty(version)) | ||
276 | { | ||
277 | this.SetMsiAssemblyName(assemblyNameTable, file, "fileVersion", version); | ||
278 | } | ||
279 | |||
280 | if (assemblyNameValues.ContainsKey("version")) | ||
281 | { | ||
282 | string assemblyVersion = assemblyNameValues["version"]; | ||
283 | |||
284 | if (!targetNetfx1) | ||
285 | { | ||
286 | // There is a bug in v1 fusion that requires the assembly's "version" attribute | ||
287 | // to be equal to or longer than the "fileVersion" in length when its present; | ||
288 | // the workaround is to prepend zeroes to the last version number in the assembly | ||
289 | // version. | ||
290 | if (null != version && version.Length > assemblyVersion.Length) | ||
291 | { | ||
292 | string padding = new string('0', version.Length - assemblyVersion.Length); | ||
293 | string[] assemblyVersionNumbers = assemblyVersion.Split('.'); | ||
294 | |||
295 | if (assemblyVersionNumbers.Length > 0) | ||
296 | { | ||
297 | assemblyVersionNumbers[assemblyVersionNumbers.Length - 1] = String.Concat(padding, assemblyVersionNumbers[assemblyVersionNumbers.Length - 1]); | ||
298 | assemblyVersion = String.Join(".", assemblyVersionNumbers); | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | this.SetMsiAssemblyName(assemblyNameTable, file, "version", assemblyVersion); | ||
304 | } | ||
305 | |||
306 | if (assemblyNameValues.ContainsKey("culture")) | ||
307 | { | ||
308 | this.SetMsiAssemblyName(assemblyNameTable, file, "culture", assemblyNameValues["culture"]); | ||
309 | } | ||
310 | |||
311 | if (assemblyNameValues.ContainsKey("publicKeyToken")) | ||
312 | { | ||
313 | this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]); | ||
314 | } | ||
315 | |||
316 | if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) | ||
317 | { | ||
318 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); | ||
319 | } | ||
320 | |||
321 | if (assemblyNameValues.ContainsKey("processorArchitecture")) | ||
322 | { | ||
323 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]); | ||
324 | } | ||
325 | |||
326 | // add the assembly name to the information cache | ||
327 | if (null != this.VariableCache) | ||
328 | { | ||
329 | string fileId = BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File); | ||
330 | string key = String.Concat("assemblyfullname.", fileId); | ||
331 | string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]); | ||
332 | if (assemblyNameValues.ContainsKey("processorArchitecture")) | ||
333 | { | ||
334 | assemblyName = String.Concat(assemblyName, ", processorArchitecture=", assemblyNameValues["processorArchitecture"]); | ||
335 | } | ||
336 | |||
337 | this.VariableCache[key] = assemblyName; | ||
338 | |||
339 | // Add entries with the preserved case publicKeyToken | ||
340 | string pcAssemblyNameKey = String.Concat("assemblyfullnamepreservedcase.", fileId); | ||
341 | this.VariableCache[pcAssemblyNameKey] = (assemblyNameValues["publicKeyToken"] == assemblyNameValues["publicKeyTokenPreservedCase"]) ? assemblyName : assemblyName.Replace(assemblyNameValues["publicKeyToken"], assemblyNameValues["publicKeyTokenPreservedCase"]); | ||
342 | |||
343 | string pcPublicKeyTokenKey = String.Concat("assemblypublickeytokenpreservedcase.", fileId); | ||
344 | this.VariableCache[pcPublicKeyTokenKey] = assemblyNameValues["publicKeyTokenPreservedCase"]; | ||
345 | } | ||
346 | } | ||
347 | else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType) | ||
348 | { | ||
349 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through | ||
350 | // all files like this. Even though this is a rare case it looks like we might be able to index the | ||
351 | // file earlier. | ||
352 | FileFacade fileManifest = this.FileFacades.SingleOrDefault(r => r.File.File.Equals(file.WixFile.AssemblyManifest, StringComparison.Ordinal)); | ||
353 | if (null == fileManifest) | ||
354 | { | ||
355 | Messaging.Instance.OnMessage(WixErrors.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.AssemblyManifest)); | ||
356 | } | ||
357 | |||
358 | string win32Type = null; | ||
359 | string win32Name = null; | ||
360 | string win32Version = null; | ||
361 | string win32ProcessorArchitecture = null; | ||
362 | string win32PublicKeyToken = null; | ||
363 | |||
364 | // loading the dom is expensive we want more performant APIs than the DOM | ||
365 | // Navigator is cheaper than dom. Perhaps there is a cheaper API still. | ||
366 | try | ||
367 | { | ||
368 | XPathDocument doc = new XPathDocument(fileManifest.WixFile.Source); | ||
369 | XPathNavigator nav = doc.CreateNavigator(); | ||
370 | nav.MoveToRoot(); | ||
371 | |||
372 | // this assumes a particular schema for a win32 manifest and does not | ||
373 | // provide error checking if the file does not conform to schema. | ||
374 | // The fallback case here is that nothing is added to the MsiAssemblyName | ||
375 | // table for an out of tolerance Win32 manifest. Perhaps warnings needed. | ||
376 | if (nav.MoveToFirstChild()) | ||
377 | { | ||
378 | while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly") | ||
379 | { | ||
380 | nav.MoveToNext(); | ||
381 | } | ||
382 | |||
383 | if (nav.MoveToFirstChild()) | ||
384 | { | ||
385 | bool hasNextSibling = true; | ||
386 | while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity" && hasNextSibling) | ||
387 | { | ||
388 | hasNextSibling = nav.MoveToNext(); | ||
389 | } | ||
390 | if (!hasNextSibling) | ||
391 | { | ||
392 | Messaging.Instance.OnMessage(WixErrors.InvalidManifestContent(file.File.SourceLineNumbers, fileManifest.WixFile.Source)); | ||
393 | return; | ||
394 | } | ||
395 | |||
396 | if (nav.MoveToAttribute("type", String.Empty)) | ||
397 | { | ||
398 | win32Type = nav.Value; | ||
399 | nav.MoveToParent(); | ||
400 | } | ||
401 | |||
402 | if (nav.MoveToAttribute("name", String.Empty)) | ||
403 | { | ||
404 | win32Name = nav.Value; | ||
405 | nav.MoveToParent(); | ||
406 | } | ||
407 | |||
408 | if (nav.MoveToAttribute("version", String.Empty)) | ||
409 | { | ||
410 | win32Version = nav.Value; | ||
411 | nav.MoveToParent(); | ||
412 | } | ||
413 | |||
414 | if (nav.MoveToAttribute("processorArchitecture", String.Empty)) | ||
415 | { | ||
416 | win32ProcessorArchitecture = nav.Value; | ||
417 | nav.MoveToParent(); | ||
418 | } | ||
419 | |||
420 | if (nav.MoveToAttribute("publicKeyToken", String.Empty)) | ||
421 | { | ||
422 | win32PublicKeyToken = nav.Value; | ||
423 | nav.MoveToParent(); | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | } | ||
428 | catch (FileNotFoundException fe) | ||
429 | { | ||
430 | Messaging.Instance.OnMessage(WixErrors.FileNotFound(new SourceLineNumber(fileManifest.WixFile.Source), fe.FileName, "AssemblyManifest")); | ||
431 | } | ||
432 | catch (XmlException xe) | ||
433 | { | ||
434 | Messaging.Instance.OnMessage(WixErrors.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source), "manifest", xe.Message)); | ||
435 | } | ||
436 | |||
437 | Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
438 | if (!String.IsNullOrEmpty(win32Name)) | ||
439 | { | ||
440 | this.SetMsiAssemblyName(assemblyNameTable, file, "name", win32Name); | ||
441 | } | ||
442 | |||
443 | if (!String.IsNullOrEmpty(win32Version)) | ||
444 | { | ||
445 | this.SetMsiAssemblyName(assemblyNameTable, file, "version", win32Version); | ||
446 | } | ||
447 | |||
448 | if (!String.IsNullOrEmpty(win32Type)) | ||
449 | { | ||
450 | this.SetMsiAssemblyName(assemblyNameTable, file, "type", win32Type); | ||
451 | } | ||
452 | |||
453 | if (!String.IsNullOrEmpty(win32ProcessorArchitecture)) | ||
454 | { | ||
455 | this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", win32ProcessorArchitecture); | ||
456 | } | ||
457 | |||
458 | if (!String.IsNullOrEmpty(win32PublicKeyToken)) | ||
459 | { | ||
460 | this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", win32PublicKeyToken); | ||
461 | } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /// <summary> | ||
466 | /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise | ||
467 | /// create a new row. | ||
468 | /// </summary> | ||
469 | /// <param name="assemblyNameTable">MsiAssemblyName table.</param> | ||
470 | /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param> | ||
471 | /// <param name="name">MsiAssemblyName name.</param> | ||
472 | /// <param name="value">MsiAssemblyName value.</param> | ||
473 | private void SetMsiAssemblyName(Table assemblyNameTable, FileFacade file, string name, string value) | ||
474 | { | ||
475 | // check for null value (this can occur when grabbing the file version from an assembly without one) | ||
476 | if (String.IsNullOrEmpty(value)) | ||
477 | { | ||
478 | Messaging.Instance.OnMessage(WixWarnings.NullMsiAssemblyNameValue(file.File.SourceLineNumbers, file.File.Component, name)); | ||
479 | } | ||
480 | else | ||
481 | { | ||
482 | Row assemblyNameRow = null; | ||
483 | |||
484 | // override directly authored value | ||
485 | foreach (Row row in assemblyNameTable.Rows) | ||
486 | { | ||
487 | if ((string)row[0] == file.File.Component && (string)row[1] == name) | ||
488 | { | ||
489 | assemblyNameRow = row; | ||
490 | break; | ||
491 | } | ||
492 | } | ||
493 | |||
494 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. | ||
495 | if ("name" == name && FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType && | ||
496 | String.IsNullOrEmpty(file.WixFile.AssemblyApplication) && | ||
497 | !String.Equals(Path.GetFileNameWithoutExtension(file.File.LongFileName), value, StringComparison.OrdinalIgnoreCase)) | ||
498 | { | ||
499 | Messaging.Instance.OnMessage(WixErrors.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.LongFileName), value)); | ||
500 | } | ||
501 | |||
502 | if (null == assemblyNameRow) | ||
503 | { | ||
504 | assemblyNameRow = assemblyNameTable.CreateRow(file.File.SourceLineNumbers); | ||
505 | assemblyNameRow[0] = file.File.Component; | ||
506 | assemblyNameRow[1] = name; | ||
507 | assemblyNameRow[2] = value; | ||
508 | |||
509 | // put the MsiAssemblyName row in the same section as the related File row | ||
510 | assemblyNameRow.SectionId = file.File.SectionId; | ||
511 | |||
512 | if (null == file.AssemblyNames) | ||
513 | { | ||
514 | file.AssemblyNames = new List<Row>(); | ||
515 | } | ||
516 | |||
517 | file.AssemblyNames.Add(assemblyNameRow); | ||
518 | } | ||
519 | else | ||
520 | { | ||
521 | assemblyNameRow[2] = value; | ||
522 | } | ||
523 | |||
524 | if (this.VariableCache != null) | ||
525 | { | ||
526 | string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant(); | ||
527 | this.VariableCache[key] = (string)assemblyNameRow[2]; | ||
528 | } | ||
529 | } | ||
530 | } | ||
531 | } | ||
532 | } | ||