diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs new file mode 100644 index 00000000..23c481b7 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | |||
@@ -0,0 +1,314 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller.Databases | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using WixToolset.Core.Bind; | ||
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 | ||
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 | } | ||