main
cs 689 lines 27.9 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.WindowsInstaller.Bind
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Threading;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 using WixToolset.Data.WindowsInstaller;
13 using WixToolset.Extensibility;
14 using WixToolset.Extensibility.Data;
15 using WixToolset.Extensibility.Services;
16
17 /// <summary>
18 /// Binds a databse.
19 /// </summary>
20 internal class BindDatabaseCommand
21 {
22 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
23 internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
24
25 public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> patchSubStorages = null)
26 {
27 this.ServiceProvider = context.ServiceProvider;
28
29 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
30
31 this.WindowsInstallerBackendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
32 this.FileSystem = context.ServiceProvider.GetService<IFileSystem>();
33 this.PathResolver = context.ServiceProvider.GetService<IPathResolver>();
34
35 this.CabbingThreadCount = context.CabbingThreadCount;
36 this.CabCachePath = context.CabCachePath;
37 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
38 this.DelayedFields = context.DelayedFields;
39 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
40 this.FileSystemManager = new FileSystemManager(this.FileSystem, context.FileSystemExtensions);
41 this.Intermediate = context.IntermediateRepresentation;
42 this.IntermediateFolder = context.IntermediateFolder;
43 this.OutputPath = context.OutputPath;
44 this.OutputPdbPath = context.PdbPath;
45 this.PdbType = context.PdbType;
46 this.ResolvedCodepage = context.ResolvedCodepage;
47 this.ResolvedSummaryInformationCodepage = context.ResolvedSummaryInformationCodepage;
48 this.ResolvedLcid = context.ResolvedLcid;
49 this.SuppressLayout = context.SuppressLayout;
50
51 this.PatchSubStorages = patchSubStorages;
52
53 this.BackendExtensions = backendExtension;
54
55 this.CancellationToken = context.CancellationToken;
56 }
57
58 private IServiceProvider ServiceProvider { get; }
59
60 private IMessaging Messaging { get; }
61
62 private IWindowsInstallerBackendHelper WindowsInstallerBackendHelper { get; }
63
64 private IFileSystem FileSystem { get; }
65
66 private IPathResolver PathResolver { get; }
67
68 private int CabbingThreadCount { get; }
69
70 private string CabCachePath { get; }
71
72 private CompressionLevel? DefaultCompressionLevel { get; }
73
74 public IEnumerable<IDelayedField> DelayedFields { get; }
75
76 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
77
78 public FileSystemManager FileSystemManager { get; }
79
80 public bool DeltaBinaryPatch { get; set; }
81
82 private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; }
83
84 private IEnumerable<SubStorage> PatchSubStorages { get; }
85
86 private Intermediate Intermediate { get; }
87
88 private string OutputPath { get; }
89
90 public PdbType PdbType { get; set; }
91
92 private string OutputPdbPath { get; }
93
94 private int? ResolvedCodepage { get; }
95
96 private int? ResolvedSummaryInformationCodepage { get; }
97
98 private int? ResolvedLcid { get; }
99
100 private bool SuppressAddingValidationRows { get; }
101
102 private bool SuppressLayout { get; }
103
104 private string IntermediateFolder { get; }
105
106 private CancellationToken CancellationToken { get; }
107
108 private int CalculateCabbingThreadCount()
109 {
110 var processorCount = Environment.ProcessorCount;
111
112 // If the number of processors is invalid, default to a single processor.
113 if (processorCount == 0)
114 {
115 processorCount = 1;
116
117 this.Messaging.Write(WarningMessages.InvalidEnvironmentVariable("NUMBER_OF_PROCESSORS", Environment.ProcessorCount.ToString(), processorCount.ToString()));
118 }
119
120 // If the cabbing thread count was provided, and it isn't more than double the number of processors, use it.
121 if (0 < this.CabbingThreadCount && this.CabbingThreadCount < processorCount * 2)
122 {
123 processorCount = this.CabbingThreadCount;
124 }
125
126 this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(processorCount.ToString()));
127
128 return processorCount;
129 }
130
131 public IBindResult Execute()
132 {
133 if (!this.Intermediate.HasLevel(Data.IntermediateLevels.Linked) || !this.Intermediate.HasLevel(Data.IntermediateLevels.Resolved))
134 {
135 this.Messaging.Write(ErrorMessages.IntermediatesMustBeResolved(this.Intermediate.Id));
136 }
137
138 var section = this.Intermediate.Sections.Single();
139
140 var packageSymbol = (section.Type == SectionType.Package) ? this.GetSingleSymbol<WixPackageSymbol>(section) : null;
141 var moduleSymbol = (section.Type == SectionType.Module) ? this.GetSingleSymbol<WixModuleSymbol>(section) : null;
142 var patchSymbol = (section.Type == SectionType.Patch) ? this.GetSingleSymbol<WixPatchSymbol>(section) : null;
143
144 var fileTransfers = new List<IFileTransfer>();
145 var trackedFiles = new List<ITrackedFile>();
146
147 var containsMergeModules = false;
148
149 int calculatedCabbingThreadCount = this.CalculateCabbingThreadCount();
150
151 // Load standard tables, authored custom tables, and extension custom tables.
152 TableDefinitionCollection tableDefinitions;
153 {
154 var command = new LoadTableDefinitionsCommand(this.Messaging, section, this.BackendExtensions);
155 command.Execute();
156
157 tableDefinitions = command.TableDefinitions;
158 }
159
160 if (section.Type == SectionType.Package)
161 {
162 this.ProcessProductVersion(packageSymbol, section, validate: false);
163 }
164
165 // Calculate codepage
166 var codepage = this.CalculateCodepage(packageSymbol, moduleSymbol, patchSymbol);
167
168 // Process properties and create the delayed variable cache if needed.
169 Dictionary<string, string> variableCache = null;
170 string productLanguage = null;
171 {
172 var command = new ProcessPropertiesCommand(section, packageSymbol, this.ResolvedLcid ?? 0, this.DelayedFields.Any(), this.WindowsInstallerBackendHelper);
173 command.Execute();
174
175 variableCache = command.DelayedVariablesCache;
176 productLanguage = command.ProductLanguage;
177 }
178
179 // Process the summary information table after properties are processed.
180 bool compressed;
181 bool longNames;
182 int installerVersion;
183 Platform platform;
184 string modularizationSuffix;
185 {
186 var branding = this.ServiceProvider.GetService<IWixBranding>();
187
188 var command = new BindSummaryInfoCommand(section, this.ResolvedSummaryInformationCodepage, productLanguage, this.WindowsInstallerBackendHelper, branding);
189 command.Execute();
190
191 compressed = command.Compressed;
192 longNames = command.LongNames;
193 installerVersion = command.InstallerVersion;
194 platform = command.Platform;
195 modularizationSuffix = command.ModularizationSuffix;
196 }
197
198 // Sequence all the actions.
199 {
200 var command = new SequenceActionsCommand(this.Messaging, section);
201 command.Execute();
202 }
203
204 {
205 var command = new CreateSpecialPropertiesCommand(section);
206 command.Execute();
207 }
208
209 // Add missing CreateFolder symbols to null-keypath components.
210 {
211 var command = new AddCreateFoldersCommand(section);
212 command.Execute();
213 }
214
215 if (this.Messaging.EncounteredError)
216 {
217 return null;
218 }
219
220 // Process dependency references.
221 if (SectionType.Package == section.Type || SectionType.Module == section.Type)
222 {
223 var dependencyRefs = section.Symbols.OfType<WixDependencyRefSymbol>().ToList();
224
225 if (dependencyRefs.Any())
226 {
227 var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs);
228 command.Execute();
229 }
230 }
231
232 // Process SoftwareTags in MSI packages.
233 if (SectionType.Package == section.Type)
234 {
235 var softwareTags = section.Symbols.OfType<WixPackageTagSymbol>().ToList();
236
237 if (softwareTags.Any())
238 {
239 var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, this.FileSystem, softwareTags, this.IntermediateFolder);
240 command.Execute();
241
242 trackedFiles.AddRange(command.TrackedFiles);
243 }
244 }
245
246 // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
247 {
248 var extractedFiles = this.WindowsInstallerBackendHelper.ExtractEmbeddedFiles(this.ExpectedEmbeddedFiles);
249
250 trackedFiles.AddRange(extractedFiles);
251 }
252
253 // Update symbols that reference text files on disk. Some of those files may have come from .wixlibs and WixExtensions
254 // extracted above.
255 {
256 var command = new UpdateFromTextFilesCommand(this.Messaging, section);
257 command.Execute();
258 }
259
260 this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound);
261 this.Messaging.Write(VerboseMessages.UpdatingFileInformation());
262
263 // This must occur after all variables and source paths have been resolved.
264 List<IFileFacade> allFileFacades;
265 List<IFileFacade> fileFacadesFromIntermediate;
266 List<IFileFacade> fileFacadesFromModule = null;
267 if (section.Type == SectionType.Patch)
268 {
269 var command = new GetFileFacadesFromTransforms(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, this.PatchSubStorages);
270 command.Execute();
271
272 allFileFacades = fileFacadesFromIntermediate = command.FileFacades;
273 }
274 else
275 {
276 var command = new GetFileFacadesCommand(section, this.WindowsInstallerBackendHelper);
277 command.Execute();
278
279 allFileFacades = fileFacadesFromIntermediate = command.FileFacades;
280 }
281
282 // Retrieve file information from merge modules.
283 if (SectionType.Package == section.Type)
284 {
285 var wixMergeSymbols = section.Symbols.OfType<WixMergeSymbol>().ToList();
286
287 if (wixMergeSymbols.Any())
288 {
289 containsMergeModules = true;
290
291 var command = new ExtractMergeModuleFilesCommand(this.Messaging, this.WindowsInstallerBackendHelper, wixMergeSymbols, fileFacadesFromIntermediate, installerVersion, this.IntermediateFolder, this.SuppressLayout);
292 command.Execute();
293
294 fileFacadesFromModule = new List<IFileFacade>(command.MergeModulesFileFacades);
295 allFileFacades.AddRange(fileFacadesFromModule);
296 trackedFiles.AddRange(command.TrackedFiles);
297 }
298 }
299
300 // stop processing if an error previously occurred
301 if (this.Messaging.EncounteredError)
302 {
303 return null;
304 }
305
306 // Gather information about files that do not come from merge modules.
307 {
308 var command = new UpdateFileFacadesCommand(this.Messaging, this.FileSystem, section, allFileFacades, fileFacadesFromIntermediate, variableCache, overwriteHash: true, this.CancellationToken, calculatedCabbingThreadCount);
309 command.Execute();
310 }
311
312 // stop processing if an error previously occurred
313 if (this.Messaging.EncounteredError)
314 {
315 return null;
316 }
317
318 // Now that the variable cache is populated, resolve any delayed fields.
319 if (this.DelayedFields.Any())
320 {
321 this.WindowsInstallerBackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
322 }
323
324 // Now that delayed fields are processed, fixup the package version (if needed) and validate it
325 // which will short circuit duplicate errors later if the ProductVersion is invalid.
326 if (SectionType.Package == section.Type)
327 {
328 this.ProcessProductVersion(packageSymbol, section, validate: true);
329 }
330
331 // If there are any backend extensions, give them the opportunity to process
332 // the section now that the fields have all be resolved.
333 //
334 if (this.BackendExtensions.Any())
335 {
336 using (new IntermediateFieldContext("wix.bind.finalize"))
337 {
338 foreach (var extension in this.BackendExtensions)
339 {
340 extension.SymbolsFinalized(section);
341 }
342
343 var reresolvedFiles = section.Symbols
344 .OfType<FileSymbol>()
345 .Where(s => s.Fields.Any(f => f?.Context == "wix.bind.finalize"))
346 .ToList();
347
348 if (reresolvedFiles.Any())
349 {
350 var updatedFacades = reresolvedFiles.Select(f => allFileFacades.First(ff => ff.Id == f.Id?.Id));
351
352 var command = new UpdateFileFacadesCommand(this.Messaging, this.FileSystem, section, allFileFacades, updatedFacades, variableCache, overwriteHash: false, this.CancellationToken, calculatedCabbingThreadCount);
353 command.Execute();
354 }
355 }
356
357 if (this.Messaging.EncounteredError)
358 {
359 return null;
360 }
361 }
362
363 if (SectionType.Package == section.Type)
364 {
365 var command = new ValidateWindowsInstallerProductConstraints(this.Messaging, section);
366 command.Execute();
367
368 if (this.Messaging.EncounteredError)
369 {
370 return null;
371 }
372 }
373
374 // Assign files to media and update file sequences.
375 Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia;
376 IEnumerable<IFileFacade> uncompressedFiles;
377 {
378 var order = new OptimizeFileFacadesOrderCommand(this.WindowsInstallerBackendHelper, this.PathResolver, section, platform, allFileFacades);
379 order.Execute();
380
381 allFileFacades = order.FileFacades;
382
383 var assign = new AssignMediaCommand(section, this.Messaging, allFileFacades, compressed);
384 assign.Execute();
385
386 filesByCabinetMedia = assign.FileFacadesByCabinetMedia;
387 uncompressedFiles = assign.UncompressedFileFacades;
388
389 var update = new UpdateMediaSequencesCommand(section, allFileFacades);
390 update.Execute();
391 }
392
393 // stop processing if an error previously occurred
394 if (this.Messaging.EncounteredError)
395 {
396 return null;
397 }
398
399 // Copy updated file facade data back into the transforms or symbols as appropriate.
400 if (section.Type == SectionType.Patch)
401 {
402 var command = new UpdateTransformsWithFileFacades(this.Messaging, section, this.PatchSubStorages, tableDefinitions, allFileFacades);
403 command.Execute();
404 }
405 else
406 {
407 var command = new UpdateSymbolsWithFileFacadesCommand(section, allFileFacades);
408 command.Execute();
409 }
410
411 // Set generated component guids and validate all guids.
412 {
413 var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform);
414 command.Execute();
415 }
416
417 // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better.
418 WindowsInstallerData data;
419 {
420 var command = new CreateWindowsInstallerDataFromIRCommand(this.Messaging, section, tableDefinitions, codepage, this.BackendExtensions, this.WindowsInstallerBackendHelper);
421 data = command.Execute();
422 }
423
424 IEnumerable<string> suppressedTableNames = null;
425 if (data.Type == OutputType.Module)
426 {
427 // Modularize identifiers.
428 var modularize = new ModularizeCommand(this.WindowsInstallerBackendHelper, data, modularizationSuffix, section.Symbols.OfType<WixSuppressModularizationSymbol>());
429 modularize.Execute();
430
431 // Ensure all sequence tables in place because mergemod.dll requires them.
432 var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions);
433 suppressedTableNames = unsuppress.Execute();
434 }
435 else if (data.Type == OutputType.Package) // we can create instance transforms since Component Guids and Outputs are created.
436 {
437 var command = new CreateInstanceTransformsCommand(section, data, tableDefinitions, this.WindowsInstallerBackendHelper);
438 command.Execute();
439
440 foreach (var storage in command.SubStorages)
441 {
442 data.SubStorages.Add(storage);
443 }
444 }
445 else if (data.Type == OutputType.Patch)
446 {
447 foreach (var storage in this.PatchSubStorages)
448 {
449 data.SubStorages.Add(storage);
450 }
451 }
452
453 // Stop processing if an error previously occurred.
454 if (this.Messaging.EncounteredError)
455 {
456 return null;
457 }
458
459 if (section.Type == SectionType.Patch && this.DeltaBinaryPatch)
460 {
461 var command = new CreateDeltaPatchesCommand(allFileFacades, this.IntermediateFolder, section.Symbols.OfType<WixPatchSymbol>().FirstOrDefault());
462 command.Execute();
463 }
464
465 // Create cabinet files.
466 if (!this.SuppressLayout || OutputType.Module == data.Type)
467 {
468 this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());
469
470 var command = new CreateCabinetsCommand(this.ServiceProvider, this.Messaging, this.WindowsInstallerBackendHelper, this.BackendExtensions, section, this.CabCachePath, calculatedCabbingThreadCount, this.OutputPath, this.IntermediateFolder, this.DefaultCompressionLevel, compressed, modularizationSuffix, filesByCabinetMedia, data, tableDefinitions, this.ResolveMedia);
471 command.Execute();
472
473 fileTransfers.AddRange(command.FileTransfers);
474 trackedFiles.AddRange(command.TrackedFiles);
475 }
476
477 // stop processing if an error previously occurred
478 if (this.Messaging.EncounteredError)
479 {
480 return null;
481 }
482
483 // Generate database file.
484 {
485 this.Messaging.Write(VerboseMessages.GeneratingDatabase());
486
487 var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltTargetOutput);
488 trackedFiles.Add(trackMsi);
489
490 var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystem, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false);
491 command.Execute();
492
493 trackedFiles.AddRange(command.GeneratedTemporaryFiles);
494 }
495
496 // Stop processing if an error previously occurred.
497 if (this.Messaging.EncounteredError)
498 {
499 return null;
500 }
501
502 // Merge modules.
503 if (containsMergeModules)
504 {
505 this.Messaging.Write(VerboseMessages.MergingModules());
506
507 var command = new MergeModulesCommand(this.Messaging, this.WindowsInstallerBackendHelper, fileFacadesFromModule, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
508 command.Execute();
509
510 if (command.TrackedFiles != null)
511 {
512 trackedFiles.AddRange(command.TrackedFiles);
513 }
514 }
515
516 if (this.Messaging.EncounteredError)
517 {
518 return null;
519 }
520
521 // Process uncompressed files.
522 if (!this.SuppressLayout && uncompressedFiles.Any())
523 {
524 var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver, uncompressedFiles, this.OutputPath, compressed, longNames, this.ResolveMedia);
525 command.Execute();
526
527 fileTransfers.AddRange(command.FileTransfers);
528 trackedFiles.AddRange(command.TrackedFiles);
529 }
530
531 // Best effort check to see if the MSI file is too large for the Windows Installer.
532 try
533 {
534 var fi = new FileInfo(this.OutputPath);
535 if (fi.Length > Int32.MaxValue)
536 {
537 this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(null, this.OutputPath, data.Type.ToString()));
538 }
539 }
540 catch
541 {
542 }
543
544 var trackedInputFiles = this.TrackInputFiles(data, trackedFiles);
545 trackedFiles.AddRange(trackedInputFiles);
546
547 var result = this.ServiceProvider.GetService<IBindResult>();
548 result.FileTransfers = fileTransfers;
549 result.TrackedFiles = trackedFiles;
550 result.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, data);
551
552 return result;
553 }
554
555 private void ProcessProductVersion(WixPackageSymbol packageSymbol, IntermediateSection section, bool validate)
556 {
557 if (this.WindowsInstallerBackendHelper.TryParseMsiProductVersion(packageSymbol.Version, strict: false, out var version))
558 {
559 if (packageSymbol.Version != version)
560 {
561 packageSymbol.Version = version;
562
563 var productVersionProperty = section.Symbols.OfType<PropertySymbol>().FirstOrDefault(p => p.Id.Id == "ProductVersion");
564 productVersionProperty.Value = version;
565 }
566 }
567 else if (validate)
568 {
569 this.Messaging.Write(WarningMessages.InvalidMsiProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
570 }
571 }
572
573 private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol)
574 {
575 var codepage = packageSymbol?.Codepage ?? moduleSymbol?.Codepage ?? patchSymbol?.Codepage;
576
577 if (String.IsNullOrEmpty(codepage))
578 {
579 codepage = this.ResolvedCodepage?.ToString() ?? "65001";
580
581 if (packageSymbol != null)
582 {
583 packageSymbol.Codepage = codepage;
584 }
585 else if (moduleSymbol != null)
586 {
587 moduleSymbol.Codepage = codepage;
588 }
589 else if (patchSymbol != null)
590 {
591 patchSymbol.Codepage = codepage;
592 }
593 }
594
595 return this.WindowsInstallerBackendHelper.GetValidCodePage(codepage);
596 }
597
598 private T GetSingleSymbol<T>(IntermediateSection section) where T : IntermediateSymbol
599 {
600 var symbols = section.Symbols.OfType<T>().ToList();
601
602 if (1 != symbols.Count)
603 {
604 throw new WixException($"Expected to find a single symbol of type {typeof(T).Name} but found {symbols.Count}");
605 }
606
607 return symbols[0];
608 }
609
610 private WixOutput CreateWixout(List<ITrackedFile> trackedFiles, Intermediate intermediate, WindowsInstallerData data)
611 {
612 WixOutput wixout;
613
614 if (String.IsNullOrEmpty(this.OutputPdbPath))
615 {
616 wixout = WixOutput.Create();
617 }
618 else
619 {
620 var trackPdb = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.BuiltPdbOutput);
621 trackedFiles.Add(trackPdb);
622
623 wixout = WixOutput.Create(trackPdb.Path);
624 }
625
626 intermediate.Save(wixout);
627
628 data.Save(wixout);
629
630 wixout.Reopen();
631
632 return wixout;
633 }
634
635 private string ResolveMedia(MediaSymbol media, string mediaLayoutDirectory, string layoutDirectory)
636 {
637 string layout = null;
638
639 foreach (var extension in this.BackendExtensions)
640 {
641 layout = extension.ResolveMedia(media, mediaLayoutDirectory, layoutDirectory);
642 if (!String.IsNullOrEmpty(layout))
643 {
644 break;
645 }
646 }
647
648 // If no binder file manager resolved the layout, do the default behavior.
649 if (String.IsNullOrEmpty(layout))
650 {
651 if (String.IsNullOrEmpty(mediaLayoutDirectory))
652 {
653 layout = layoutDirectory;
654 }
655 else if (Path.IsPathRooted(mediaLayoutDirectory))
656 {
657 layout = mediaLayoutDirectory;
658 }
659 else
660 {
661 layout = Path.Combine(layoutDirectory, mediaLayoutDirectory);
662 }
663 }
664
665 return layout;
666 }
667
668 private IEnumerable<ITrackedFile> TrackInputFiles(WindowsInstallerData data, List<ITrackedFile> trackedFiles)
669 {
670 var trackedInputFiles = new List<ITrackedFile>();
671 var intermediateAndTemporaryPaths = new HashSet<string>(trackedFiles.Where(t => t.Type == TrackedFileType.Intermediate || t.Type == TrackedFileType.Temporary).Select(t => t.Path), StringComparer.OrdinalIgnoreCase);
672
673 foreach (var row in data.Tables.SelectMany(t => t.Rows))
674 {
675 foreach (var field in row.Fields.Where(f => f.Column.Type == ColumnType.Object))
676 {
677 var path = field.AsString();
678
679 if (!intermediateAndTemporaryPaths.Contains(path))
680 {
681 trackedInputFiles.Add(this.WindowsInstallerBackendHelper.TrackFile(path, TrackedFileType.Input, row.SourceLineNumbers));
682 }
683 }
684 }
685
686 return trackedInputFiles;
687 }
688 }
689 }