Exemple #1
0
        public void Execute()
        {
            var section = this.Intermediate.Sections.Single();

            var fileTransfers = new List <FileTransfer>();

            var containsMergeModules = false;
            var suppressedTableNames = new HashSet <string>();

            // If there are any fields to resolve later, create the cache to populate during bind.
            var variableCache = this.DelayedFields.Any() ? new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase) : null;

            // Process the summary information table before the other tables.
            bool   compressed;
            bool   longNames;
            int    installerVersion;
            string modularizationGuid;

            {
                var command = new BindSummaryInfoCommand(section);
                command.Execute();

                compressed         = command.Compressed;
                longNames          = command.LongNames;
                installerVersion   = command.InstallerVersion;
                modularizationGuid = command.ModularizationGuid;
            }

            // Add binder variables for all properties.
            if (SectionType.Product == section.Type || variableCache != null)
            {
                foreach (var propertyRow in section.Tuples.OfType <PropertyTuple>())
                {
                    // Set the ProductCode if it is to be generated.
                    if ("ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
                    {
                        propertyRow.Value = Common.GenerateGuid();

#if TODO_FIX_INSTANCE_TRANSFORM // Is this still necessary?
                        // Update the target ProductCode in any instance transforms.
                        foreach (SubStorage subStorage in this.Output.SubStorages)
                        {
                            Output subStorageOutput = subStorage.Data;
                            if (OutputType.Transform != subStorageOutput.Type)
                            {
                                continue;
                            }

                            Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
                            foreach (Row row in instanceSummaryInformationTable.Rows)
                            {
                                if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
                                {
                                    row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
                                    break;
                                }
                            }
                        }
#endif
                    }

                    // Add the property name and value to the variableCache.
                    if (variableCache != null)
                    {
                        var key = String.Concat("property.", propertyRow.Property);
                        variableCache[key] = propertyRow.Value;
                    }
                }
            }

            // Sequence all the actions.
            {
                var command = new SequenceActionsCommand(section);
                command.Messaging = this.Messaging;
                command.Execute();
            }

            {
                var command = new CreateSpecialPropertiesCommand(section);
                command.Execute();
            }

#if TODO_FINISH_PATCH
            ////if (OutputType.Patch == this.Output.Type)
            ////{
            ////    foreach (SubStorage substorage in this.Output.SubStorages)
            ////    {
            ////        Output transform = substorage.Data;

            ////        ResolveFieldsCommand command = new ResolveFieldsCommand();
            ////        command.Tables = transform.Tables;
            ////        command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
            ////        command.FileManagerCore = this.FileManagerCore;
            ////        command.FileManagers = this.FileManagers;
            ////        command.SupportDelayedResolution = false;
            ////        command.TempFilesLocation = this.TempFilesLocation;
            ////        command.WixVariableResolver = this.WixVariableResolver;
            ////        command.Execute();

            ////        this.MergeUnrealTables(transform.Tables);
            ////    }
            ////}
#endif

            if (this.Messaging.EncounteredError)
            {
                return;
            }

            this.Messaging.Write(VerboseMessages.UpdatingFileInformation());

            // This must occur after all variables and source paths have been resolved.
            List <FileFacade> fileFacades;
            {
                var command = new GetFileFacadesCommand(section);
                command.Execute();

                fileFacades = command.FileFacades;
            }

            // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
            {
                var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles);
                command.Execute();
            }

            // Gather information about files that do not come from merge modules.
            {
                var command = new UpdateFileFacadesCommand(this.Messaging, section);
                command.FileFacades       = fileFacades;
                command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
                command.OverwriteHash     = true;
                command.TableDefinitions  = this.TableDefinitions;
                command.VariableCache     = variableCache;
                command.Execute();
            }

            // Now that the variable cache is populated, resolve any delayed fields.
            if (this.DelayedFields.Any())
            {
                var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
                command.Execute();
            }

            // Set generated component guids.
            {
                var command = new CalculateComponentGuids(this.Messaging, section);
                command.Execute();
            }

            // Retrieve file information from merge modules.
            if (SectionType.Product == section.Type)
            {
                var wixMergeTuples = section.Tuples.OfType <WixMergeTuple>().ToList();

                if (wixMergeTuples.Any())
                {
                    containsMergeModules = true;

                    var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples);
                    command.FileFacades            = fileFacades;
                    command.OutputInstallerVersion = installerVersion;
                    command.SuppressLayout         = this.SuppressLayout;
                    command.IntermediateFolder     = this.IntermediateFolder;
                    command.Execute();

                    fileFacades.AddRange(command.MergeModulesFileFacades);
                }
            }
#if TODO_FINISH_PATCH
            else if (OutputType.Patch == this.Output.Type)
            {
                // Merge transform data into the output object.
                IEnumerable <FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output);

                fileFacades.AddRange(filesFromTransform);
            }
#endif

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // Assign files to media.
            Dictionary <int, MediaTuple> assignedMediaRows;
            Dictionary <MediaTuple, IEnumerable <FileFacade> > filesByCabinetMedia;
            IEnumerable <FileFacade> uncompressedFiles;
            {
                var command = new AssignMediaCommand(section, this.Messaging);
                command.FileFacades     = fileFacades;
                command.FilesCompressed = compressed;
                command.Execute();

                assignedMediaRows   = command.MediaRows;
                filesByCabinetMedia = command.FileFacadesByCabinetMedia;
                uncompressedFiles   = command.UncompressedFileFacades;
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // Time to create the output object. Try to put as much above here as possible, updating the IR is better.
            Output output;
            {
                var command = new CreateOutputFromIRCommand(section, this.TableDefinitions, this.BackendExtensions);
                command.Execute();

                output = command.Output;
            }

            // Update file sequence.
            {
                var command = new UpdateMediaSequencesCommand(output, fileFacades, assignedMediaRows);
                command.Execute();
            }

            // Modularize identifiers.
            if (OutputType.Module == output.Type)
            {
                var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType <WixSuppressModularizationTuple>());
                command.Execute();
            }
            else // we can create instance transforms since Component Guids are set.
            {
#if TODO_FIX_INSTANCE_TRANSFORM
                this.CreateInstanceTransforms(this.Output);
#endif
            }

#if TODO_FINISH_UPDATE
            // Extended binder extensions can be called now that fields are resolved.
            {
                Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);

                foreach (IBinderExtension extension in this.Extensions)
                {
                    extension.AfterResolvedFields(this.Output);
                }

                List <FileFacade> updatedFileFacades = new List <FileFacade>();

                foreach (Row updatedFile in updatedFiles.Rows)
                {
                    string updatedId = updatedFile.FieldAsString(0);

                    FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId));

                    updatedFileFacades.Add(updatedFacade);
                }

                if (updatedFileFacades.Any())
                {
                    UpdateFileFacadesCommand command = new UpdateFileFacadesCommand();
                    command.FileFacades        = fileFacades;
                    command.UpdateFileFacades  = updatedFileFacades;
                    command.ModularizationGuid = modularizationGuid;
                    command.Output             = this.Output;
                    command.OverwriteHash      = true;
                    command.TableDefinitions   = this.TableDefinitions;
                    command.VariableCache      = variableCache;
                    command.Execute();
                }
            }
#endif

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // Ensure the intermediate folder is created since delta patches will be
            // created there.
            Directory.CreateDirectory(this.IntermediateFolder);

            if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
            {
                var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType <WixPatchIdTuple>().FirstOrDefault());
                command.Execute();
            }

            // create cabinet files and process uncompressed files
            var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
            if (!this.SuppressLayout || OutputType.Module == output.Type)
            {
                this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());

                var command = new CreateCabinetsCommand();
                command.CabbingThreadCount      = this.CabbingThreadCount;
                command.CabCachePath            = this.CabCachePath;
                command.DefaultCompressionLevel = this.DefaultCompressionLevel;
                command.Output            = output;
                command.Messaging         = this.Messaging;
                command.BackendExtensions = this.BackendExtensions;
                command.LayoutDirectory   = layoutDirectory;
                command.Compressed        = compressed;
                command.FileRowsByCabinet = filesByCabinetMedia;
                command.ResolveMedia      = this.ResolveMedia;
                command.TableDefinitions  = this.TableDefinitions;
                command.TempFilesLocation = this.IntermediateFolder;
                command.WixMediaTuples    = section.Tuples.OfType <WixMediaTuple>();
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
            }

#if TODO_FINISH_PATCH
            if (OutputType.Patch == this.Output.Type)
            {
                // copy output data back into the transforms
                this.CopyToTransformData(this.Output);
            }
#endif

            this.ValidateComponentGuids(output);

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // Generate database file.
            this.Messaging.Write(VerboseMessages.GeneratingDatabase());
            string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
            this.GenerateDatabase(output, tempDatabaseFile, false, false);

            if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, output.Type.ToString(), null, out var transfer)) // note where this database needs to move in the future
            {
                transfer.Built = true;
                fileTransfers.Add(transfer);
            }

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // Merge modules.
            if (containsMergeModules)
            {
                this.Messaging.Write(VerboseMessages.MergingModules());

                // Add back possibly suppressed sequence tables since all sequence tables must be present
                // for the merge process to work. We'll drop the suppressed sequence tables again as
                // necessary.
                foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
                {
                    var sequenceTableName = sequence.ToString();
                    var sequenceTable     = output.Tables[sequenceTableName];

                    if (null == sequenceTable)
                    {
                        sequenceTable = output.EnsureTable(this.TableDefinitions[sequenceTableName]);
                    }

                    if (0 == sequenceTable.Rows.Count)
                    {
                        suppressedTableNames.Add(sequenceTableName);
                    }
                }

                var command = new MergeModulesCommand();
                command.FileFacades          = fileFacades;
                command.Output               = output;
                command.OutputPath           = tempDatabaseFile;
                command.SuppressedTableNames = suppressedTableNames;
                command.Execute();
            }

            if (this.Messaging.EncounteredError)
            {
                return;
            }

#if TODO_FINISH_VALIDATION
            // Validate the output if there is an MSI validator.
            if (null != this.Validator)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();

                // set the output file for source line information
                this.Validator.Output = this.Output;

                Messaging.Instance.Write(WixVerboses.ValidatingDatabase());

                this.Validator.Validate(tempDatabaseFile);

                stopwatch.Stop();
                Messaging.Instance.Write(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));

                // Stop processing if an error occurred.
                if (Messaging.Instance.EncounteredError)
                {
                    return;
                }
            }
#endif

            // Process uncompressed files.
            if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
            {
                var command = new ProcessUncompressedFilesCommand(section);
                command.Compressed       = compressed;
                command.FileFacades      = uncompressedFiles;
                command.LayoutDirectory  = layoutDirectory;
                command.LongNamesInImage = longNames;
                command.ResolveMedia     = this.ResolveMedia;
                command.DatabasePath     = tempDatabaseFile;
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
            }

            this.FileTransfers    = fileTransfers;
            this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source.Path).ToList();
            this.Pdb = new Pdb {
                Output = output
            };

            // TODO: Eventually this gets removed
            var intermediate = new Intermediate(this.Intermediate.Id, new[] { section }, this.Intermediate.Localizations.ToDictionary(l => l.Culture, StringComparer.OrdinalIgnoreCase), this.Intermediate.EmbedFilePaths);
            intermediate.Save(Path.ChangeExtension(this.OutputPath, "wir"));
        }
Exemple #2
0
        public IBindResult Execute()
        {
            if (!this.Intermediate.HasLevel(Data.IntermediateLevels.Linked) && !this.Intermediate.HasLevel(Data.IntermediateLevels.Resolved))
            {
                this.Messaging.Write(ErrorMessages.IntermediatesMustBeResolved(this.Intermediate.Id));
            }

            var section = this.Intermediate.Sections.Single();

            var fileTransfers = new List <IFileTransfer>();
            var trackedFiles  = new List <ITrackedFile>();

            var containsMergeModules = false;

            // If there are any fields to resolve later, create the cache to populate during bind.
            var variableCache = this.DelayedFields.Any() ? new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase) : null;

            // Load standard tables, authored custom tables, and extension custom tables.
            TableDefinitionCollection tableDefinitions;
            {
                var command = new LoadTableDefinitionsCommand(this.Messaging, section, this.BackendExtensions);
                command.Execute();

                tableDefinitions = command.TableDefinitions;
            }

            // Process the summary information table before the other tables.
            bool   compressed;
            bool   longNames;
            int    installerVersion;
            string modularizationSuffix;

            {
                var command = new BindSummaryInfoCommand(section);
                command.Execute();

                compressed           = command.Compressed;
                longNames            = command.LongNames;
                installerVersion     = command.InstallerVersion;
                modularizationSuffix = command.ModularizationSuffix;
            }

            // Add binder variables for all properties.
            if (SectionType.Product == section.Type || variableCache != null)
            {
                foreach (var propertyRow in section.Symbols.OfType <PropertySymbol>())
                {
                    // Set the ProductCode if it is to be generated.
                    if ("ProductCode".Equals(propertyRow.Id.Id, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
                    {
                        propertyRow.Value = Common.GenerateGuid();

#if TODO_PATCHING // Is this still necessary?
                        // Update the target ProductCode in any instance transforms.
                        foreach (SubStorage subStorage in this.Output.SubStorages)
                        {
                            Output subStorageOutput = subStorage.Data;
                            if (OutputType.Transform != subStorageOutput.Type)
                            {
                                continue;
                            }

                            Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
                            foreach (Row row in instanceSummaryInformationTable.Rows)
                            {
                                if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
                                {
                                    row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
                                    break;
                                }
                            }
                        }
#endif
                    }

                    // Add the property name and value to the variableCache.
                    if (variableCache != null)
                    {
                        var key = String.Concat("property.", propertyRow.Id.Id);
                        variableCache[key] = propertyRow.Value;
                    }
                }
            }

            // Sequence all the actions.
            {
                var command = new SequenceActionsCommand(this.Messaging, section);
                command.Execute();
            }

            {
                var command = new CreateSpecialPropertiesCommand(section);
                command.Execute();
            }

#if TODO_PATCHING
            ////if (OutputType.Patch == this.Output.Type)
            ////{
            ////    foreach (SubStorage substorage in this.Output.SubStorages)
            ////    {
            ////        Output transform = substorage.Data;

            ////        ResolveFieldsCommand command = new ResolveFieldsCommand();
            ////        command.Tables = transform.Tables;
            ////        command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
            ////        command.FileManagerCore = this.FileManagerCore;
            ////        command.FileManagers = this.FileManagers;
            ////        command.SupportDelayedResolution = false;
            ////        command.TempFilesLocation = this.TempFilesLocation;
            ////        command.WixVariableResolver = this.WixVariableResolver;
            ////        command.Execute();

            ////        this.MergeUnrealTables(transform.Tables);
            ////    }
            ////}
#endif

            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound);
            this.Messaging.Write(VerboseMessages.UpdatingFileInformation());

            // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
            {
                var command = new ExtractEmbeddedFilesCommand(this.BackendHelper, this.ExpectedEmbeddedFiles);
                command.Execute();

                trackedFiles.AddRange(command.TrackedFiles);
            }

            // This must occur after all variables and source paths have been resolved.
            List <FileFacade> fileFacades;
            if (SectionType.Patch == section.Type)
            {
                var command = new GetFileFacadesFromTransforms(this.Messaging, this.FileSystemManager, this.SubStorages);
                command.Execute();

                fileFacades = command.FileFacades;
            }
            else
            {
                var command = new GetFileFacadesCommand(section);
                command.Execute();

                fileFacades = command.FileFacades;
            }

            // Retrieve file information from merge modules.
            if (SectionType.Product == section.Type)
            {
                var wixMergeSymbols = section.Symbols.OfType <WixMergeSymbol>().ToList();

                if (wixMergeSymbols.Any())
                {
                    containsMergeModules = true;

                    var command = new ExtractMergeModuleFilesCommand(this.Messaging, wixMergeSymbols, fileFacades, installerVersion, this.IntermediateFolder, this.SuppressLayout);
                    command.Execute();

                    fileFacades.AddRange(command.MergeModulesFileFacades);
                }
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Gather information about files that do not come from merge modules.
            {
                var command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, fileFacades.Where(f => !f.FromModule), variableCache, overwriteHash: true);
                command.Execute();
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Now that the variable cache is populated, resolve any delayed fields.
            if (this.DelayedFields.Any())
            {
                var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
                command.Execute();
            }

#if TODO_FINISH_UPDATE // use symbols instead of rows
            // Extended binder extensions can be called now that fields are resolved.
            {
                Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);

                foreach (IBinderExtension extension in this.Extensions)
                {
                    extension.AfterResolvedFields(this.Output);
                }

                List <FileFacade> updatedFileFacades = new List <FileFacade>();

                foreach (Row updatedFile in updatedFiles.Rows)
                {
                    string updatedId = updatedFile.FieldAsString(0);

                    FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId));

                    updatedFileFacades.Add(updatedFacade);
                }

                if (updatedFileFacades.Any())
                {
                    UpdateFileFacadesCommand command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, updateFileFacades, variableCache, overwriteHash: false);
                    //command.FileFacades = fileFacades;
                    //command.UpdateFileFacades = updatedFileFacades;
                    //command.ModularizationGuid = modularizationGuid;
                    //command.Output = this.Output;
                    //command.OverwriteHash = true;
                    //command.TableDefinitions = this.TableDefinitions;
                    //command.VariableCache = variableCache;
                    command.Execute();
                }
            }
#endif

            // Set generated component guids.
            {
                var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section);
                command.Execute();
            }

            {
                var command = new ValidateComponentGuidsCommand(this.Messaging, section);
                command.Execute();
            }

            // Add missing CreateFolder symbols to null-keypath components.
            {
                var command = new AddCreateFoldersCommand(section);
                command.Execute();
            }

            // Update symbols that reference text files on disk.
            {
                var command = new UpdateFromTextFilesCommand(this.Messaging, section);
                command.Execute();
            }

            // Assign files to media and update file sequences.
            Dictionary <MediaSymbol, IEnumerable <FileFacade> > filesByCabinetMedia;
            IEnumerable <FileFacade> uncompressedFiles;
            {
                var order = new OptimizeFileFacadesOrderCommand(fileFacades);
                order.Execute();

                fileFacades = order.FileFacades;

                var assign = new AssignMediaCommand(section, this.Messaging, fileFacades, compressed);
                assign.Execute();

                filesByCabinetMedia = assign.FileFacadesByCabinetMedia;
                uncompressedFiles   = assign.UncompressedFileFacades;

                var update = new UpdateMediaSequencesCommand(section, fileFacades);
                update.Execute();
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Time to create the output object. Try to put as much above here as possible, updating the IR is better.
            WindowsInstallerData output;
            {
                var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions, this.WindowsInstallerBackendHelper);
                command.Execute();

                output = command.Output;
            }

            IEnumerable <string> suppressedTableNames = null;
            if (output.Type == OutputType.Module)
            {
                // Modularize identifiers.
                var modularize = new ModularizeCommand(output, modularizationSuffix, section.Symbols.OfType <WixSuppressModularizationSymbol>());
                modularize.Execute();

                // Ensure all sequence tables in place because, mergemod.dll requires them.
                var unsuppress = new AddBackSuppressedSequenceTablesCommand(output, tableDefinitions);
                suppressedTableNames = unsuppress.Execute();
            }
            else if (output.Type == OutputType.Patch)
            {
                foreach (var storage in this.SubStorages)
                {
                    output.SubStorages.Add(storage);
                }
            }

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Ensure the intermediate folder is created since delta patches will be
            // created there.
            Directory.CreateDirectory(this.IntermediateFolder);

            if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
            {
                var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Symbols.OfType <WixPatchIdSymbol>().FirstOrDefault());
                command.Execute();
            }

            // create cabinet files and process uncompressed files
            var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
            if (!this.SuppressLayout || OutputType.Module == output.Type)
            {
                this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());

                var mediaTemplate = section.Symbols.OfType <WixMediaTemplateSymbol>().FirstOrDefault();

                var command = new CreateCabinetsCommand(this.ServiceProvider, this.BackendHelper, mediaTemplate);
                command.CabbingThreadCount      = this.CabbingThreadCount;
                command.CabCachePath            = this.CabCachePath;
                command.DefaultCompressionLevel = this.DefaultCompressionLevel;
                command.Output               = output;
                command.Messaging            = this.Messaging;
                command.BackendExtensions    = this.BackendExtensions;
                command.LayoutDirectory      = layoutDirectory;
                command.Compressed           = compressed;
                command.ModularizationSuffix = modularizationSuffix;
                command.FileFacadesByCabinet = filesByCabinetMedia;
                command.ResolveMedia         = this.ResolveMedia;
                command.TableDefinitions     = tableDefinitions;
                command.IntermediateFolder   = this.IntermediateFolder;
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
                trackedFiles.AddRange(command.TrackedFiles);
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // We can create instance transforms since Component Guids and Outputs are created.
            if (output.Type == OutputType.Product)
            {
                var command = new CreateInstanceTransformsCommand(section, output, tableDefinitions, this.BackendHelper);
                command.Execute();
            }
            else if (output.Type == OutputType.Patch)
            {
                // Copy output data back into the transforms.
                var command = new UpdateTransformsWithFileFacades(this.Messaging, output, this.SubStorages, tableDefinitions, fileFacades);
                command.Execute();
            }

            // Generate database file.
            this.Messaging.Write(VerboseMessages.GeneratingDatabase());

            {
                var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final);
                trackedFiles.Add(trackMsi);

                var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, trackMsi.Path, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false);
                command.Execute();

                trackedFiles.AddRange(command.GeneratedTemporaryFiles);
            }

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Merge modules.
            if (containsMergeModules)
            {
                this.Messaging.Write(VerboseMessages.MergingModules());

                var command = new MergeModulesCommand(this.Messaging, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
                command.Execute();
            }

            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

#if TODO_FINISH_VALIDATION
            // Validate the output if there is an MSI validator.
            if (null != this.Validator)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();

                // set the output file for source line information
                this.Validator.Output = this.Output;

                Messaging.Instance.Write(WixVerboses.ValidatingDatabase());

                this.Validator.Validate(this.OutputPath);

                stopwatch.Stop();
                Messaging.Instance.Write(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));

                // Stop processing if an error occurred.
                if (Messaging.Instance.EncounteredError)
                {
                    return;
                }
            }
#endif

            // Process uncompressed files.
            if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
            {
                var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver);
                command.Compressed       = compressed;
                command.FileFacades      = uncompressedFiles;
                command.LayoutDirectory  = layoutDirectory;
                command.LongNamesInImage = longNames;
                command.ResolveMedia     = this.ResolveMedia;
                command.DatabasePath     = this.OutputPath;
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
                trackedFiles.AddRange(command.TrackedFiles);
            }

            // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables).
            trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber)));

            var result = this.ServiceProvider.GetService <IBindResult>();
            result.FileTransfers = fileTransfers;
            result.TrackedFiles  = trackedFiles;
            result.Wixout        = this.CreateWixout(trackedFiles, this.Intermediate, output);

            return(result);
        }
        public IBindResult Execute()
        {
            if (!this.Intermediate.HasLevel(Data.IntermediateLevels.Linked) || !this.Intermediate.HasLevel(Data.IntermediateLevels.Resolved))
            {
                this.Messaging.Write(ErrorMessages.IntermediatesMustBeResolved(this.Intermediate.Id));
            }

            var section = this.Intermediate.Sections.Single();

            var packageSymbol = (section.Type == SectionType.Product) ? this.GetSingleSymbol <WixPackageSymbol>(section) : null;
            var moduleSymbol  = (section.Type == SectionType.Module) ? this.GetSingleSymbol <WixModuleSymbol>(section) : null;
            var patchSymbol   = (section.Type == SectionType.Patch) ? this.GetSingleSymbol <WixPatchSymbol>(section) : null;

            var fileTransfers = new List <IFileTransfer>();
            var trackedFiles  = new List <ITrackedFile>();

            var containsMergeModules = false;

            // Load standard tables, authored custom tables, and extension custom tables.
            TableDefinitionCollection tableDefinitions;
            {
                var command = new LoadTableDefinitionsCommand(this.Messaging, section, this.BackendExtensions);
                command.Execute();

                tableDefinitions = command.TableDefinitions;
            }

            // Calculate codepage
            var codepage = this.CalculateCodepage(packageSymbol, moduleSymbol, patchSymbol);

            // Process properties and create the delayed variable cache if needed.
            Dictionary <string, string> variableCache = null;
            string productLanguage = null;
            {
                var command = new ProcessPropertiesCommand(section, packageSymbol, this.ResolvedLcid ?? 0, this.DelayedFields.Any(), this.WindowsInstallerBackendHelper);
                command.Execute();

                variableCache   = command.DelayedVariablesCache;
                productLanguage = command.ProductLanguage;
            }

            // Process the summary information table after properties are processed.
            bool     compressed;
            bool     longNames;
            int      installerVersion;
            Platform platform;
            string   modularizationSuffix;

            {
                var branding = this.ServiceProvider.GetService <IWixBranding>();

                var command = new BindSummaryInfoCommand(section, this.ResolvedSummaryInformationCodepage, productLanguage, this.WindowsInstallerBackendHelper, branding);
                command.Execute();

                compressed           = command.Compressed;
                longNames            = command.LongNames;
                installerVersion     = command.InstallerVersion;
                platform             = command.Platform;
                modularizationSuffix = command.ModularizationSuffix;
            }

            // Sequence all the actions.
            {
                var command = new SequenceActionsCommand(this.Messaging, section);
                command.Execute();
            }

            if (section.Type == SectionType.Product || section.Type == SectionType.Module)
            {
                var command = new AddRequiredStandardDirectories(section, platform);
                command.Execute();
            }

            {
                var command = new CreateSpecialPropertiesCommand(section);
                command.Execute();
            }

#if TODO_PATCHING
            ////if (OutputType.Patch == this.Output.Type)
            ////{
            ////    foreach (SubStorage substorage in this.Output.SubStorages)
            ////    {
            ////        Output transform = substorage.Data;

            ////        ResolveFieldsCommand command = new ResolveFieldsCommand();
            ////        command.Tables = transform.Tables;
            ////        command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
            ////        command.FileManagerCore = this.FileManagerCore;
            ////        command.FileManagers = this.FileManagers;
            ////        command.SupportDelayedResolution = false;
            ////        command.TempFilesLocation = this.TempFilesLocation;
            ////        command.WixVariableResolver = this.WixVariableResolver;
            ////        command.Execute();

            ////        this.MergeUnrealTables(transform.Tables);
            ////    }
            ////}
#endif

            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound);
            this.Messaging.Write(VerboseMessages.UpdatingFileInformation());

            // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
            {
                var extractedFiles = this.WindowsInstallerBackendHelper.ExtractEmbeddedFiles(this.ExpectedEmbeddedFiles);

                trackedFiles.AddRange(extractedFiles);
            }

            // This must occur after all variables and source paths have been resolved.
            List <IFileFacade> fileFacades;
            if (SectionType.Patch == section.Type)
            {
                var command = new GetFileFacadesFromTransforms(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, this.SubStorages);
                command.Execute();

                fileFacades = command.FileFacades;
            }
            else
            {
                var command = new GetFileFacadesCommand(section, this.WindowsInstallerBackendHelper);
                command.Execute();

                fileFacades = command.FileFacades;
            }

            // Retrieve file information from merge modules.
            if (SectionType.Product == section.Type)
            {
                var wixMergeSymbols = section.Symbols.OfType <WixMergeSymbol>().ToList();

                if (wixMergeSymbols.Any())
                {
                    containsMergeModules = true;

                    var command = new ExtractMergeModuleFilesCommand(this.Messaging, this.WindowsInstallerBackendHelper, wixMergeSymbols, fileFacades, installerVersion, this.IntermediateFolder, this.SuppressLayout);
                    command.Execute();

                    fileFacades.AddRange(command.MergeModulesFileFacades);
                }
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Process SoftwareTags in MSI packages.
            if (SectionType.Product == section.Type)
            {
                var softwareTags = section.Symbols.OfType <WixProductTagSymbol>().ToList();

                if (softwareTags.Any())
                {
                    var command = new ProcessPackageSoftwareTagsCommand(section, softwareTags, this.IntermediateFolder);
                    command.Execute();
                }
            }

            // Gather information about files that do not come from merge modules.
            {
                var command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, fileFacades.Where(f => !f.FromModule), variableCache, overwriteHash: true);
                command.Execute();
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Now that the variable cache is populated, resolve any delayed fields.
            if (this.DelayedFields.Any())
            {
                this.WindowsInstallerBackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
            }

            // Update symbols that reference text files on disk.
            {
                var command = new UpdateFromTextFilesCommand(this.Messaging, section);
                command.Execute();
            }

            // Add missing CreateFolder symbols to null-keypath components.
            {
                var command = new AddCreateFoldersCommand(section);
                command.Execute();
            }

            // Process dependency references.
            if (SectionType.Product == section.Type || SectionType.Module == section.Type)
            {
                var dependencyRefs = section.Symbols.OfType <WixDependencyRefSymbol>().ToList();

                if (dependencyRefs.Any())
                {
                    var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs);
                    command.Execute();
                }
            }

            // If there are any backend extensions, give them the opportunity to process
            // the section now that the fields have all be resolved.
            //
            if (this.BackendExtensions.Any())
            {
                using (new IntermediateFieldContext("wix.bind.finalize"))
                {
                    foreach (var extension in this.BackendExtensions)
                    {
                        extension.SymbolsFinalized(section);
                    }

                    var reresolvedFiles = section.Symbols
                                          .OfType <FileSymbol>()
                                          .Where(s => s.Fields.Any(f => f?.Context == "wix.bind.finalize"))
                                          .ToList();

                    if (reresolvedFiles.Any())
                    {
                        var updatedFacades = reresolvedFiles.Select(f => fileFacades.First(ff => ff.Id == f.Id?.Id));

                        var command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, updatedFacades, variableCache, overwriteHash: false);
                        command.Execute();
                    }
                }

                if (this.Messaging.EncounteredError)
                {
                    return(null);
                }
            }

            // Set generated component guids and validate all guids.
            {
                var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform);
                command.Execute();
            }

            // Assign files to media and update file sequences.
            Dictionary <MediaSymbol, IEnumerable <IFileFacade> > filesByCabinetMedia;
            IEnumerable <IFileFacade> uncompressedFiles;
            {
                var order = new OptimizeFileFacadesOrderCommand(this.WindowsInstallerBackendHelper, this.PathResolver, section, platform, fileFacades);
                order.Execute();

                fileFacades = order.FileFacades;

                var assign = new AssignMediaCommand(section, this.Messaging, fileFacades, compressed);
                assign.Execute();

                filesByCabinetMedia = assign.FileFacadesByCabinetMedia;
                uncompressedFiles   = assign.UncompressedFileFacades;

                var update = new UpdateMediaSequencesCommand(section, fileFacades);
                update.Execute();
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better.
            WindowsInstallerData data;
            {
                var command = new CreateWindowsInstallerDataFromIRCommand(this.Messaging, section, tableDefinitions, codepage, this.BackendExtensions, this.WindowsInstallerBackendHelper);
                data = command.Execute();
            }

            IEnumerable <string> suppressedTableNames = null;
            if (data.Type == OutputType.Module)
            {
                // Modularize identifiers.
                var modularize = new ModularizeCommand(this.WindowsInstallerBackendHelper, data, modularizationSuffix, section.Symbols.OfType <WixSuppressModularizationSymbol>());
                modularize.Execute();

                // Ensure all sequence tables in place because, mergemod.dll requires them.
                var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions);
                suppressedTableNames = unsuppress.Execute();
            }
            else if (data.Type == OutputType.Patch)
            {
                foreach (var storage in this.SubStorages)
                {
                    data.SubStorages.Add(storage);
                }
            }

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Ensure the intermediate folder is created since delta patches will be
            // created there.
            Directory.CreateDirectory(this.IntermediateFolder);

            if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
            {
                var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Symbols.OfType <WixPatchSymbol>().FirstOrDefault());
                command.Execute();
            }

            // create cabinet files and process uncompressed files
            var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
            if (!this.SuppressLayout || OutputType.Module == data.Type)
            {
                this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());

                var mediaTemplate = section.Symbols.OfType <WixMediaTemplateSymbol>().FirstOrDefault();

                var command = new CreateCabinetsCommand(this.ServiceProvider, this.WindowsInstallerBackendHelper, mediaTemplate);
                command.CabbingThreadCount      = this.CabbingThreadCount;
                command.CabCachePath            = this.CabCachePath;
                command.DefaultCompressionLevel = this.DefaultCompressionLevel;
                command.Data                 = data;
                command.Messaging            = this.Messaging;
                command.BackendExtensions    = this.BackendExtensions;
                command.LayoutDirectory      = layoutDirectory;
                command.Compressed           = compressed;
                command.ModularizationSuffix = modularizationSuffix;
                command.FileFacadesByCabinet = filesByCabinetMedia;
                command.ResolveMedia         = this.ResolveMedia;
                command.TableDefinitions     = tableDefinitions;
                command.IntermediateFolder   = this.IntermediateFolder;
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
                trackedFiles.AddRange(command.TrackedFiles);
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // We can create instance transforms since Component Guids and Outputs are created.
            if (data.Type == OutputType.Product)
            {
                var command = new CreateInstanceTransformsCommand(section, data, tableDefinitions, this.WindowsInstallerBackendHelper);
                command.Execute();
            }
            else if (data.Type == OutputType.Patch)
            {
                // Copy output data back into the transforms.
                var command = new UpdateTransformsWithFileFacades(this.Messaging, data, this.SubStorages, tableDefinitions, fileFacades);
                command.Execute();
            }

            // Generate database file.
            {
                this.Messaging.Write(VerboseMessages.GeneratingDatabase());

                var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final);
                trackedFiles.Add(trackMsi);

                var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false);
                command.Execute();

                trackedFiles.AddRange(command.GeneratedTemporaryFiles);
            }

            // Stop processing if an error previously occurred.
            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Merge modules.
            if (containsMergeModules)
            {
                this.Messaging.Write(VerboseMessages.MergingModules());

                var command = new MergeModulesCommand(this.Messaging, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
                command.Execute();
            }

            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Validate the output if there are CUBe files and we're not explicitly suppressing validation.
            if (this.CubeFiles != null && !this.SuppressValidation)
            {
                var command = new ValidateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.IntermediateFolder, data, this.OutputPath, this.CubeFiles, this.Ices, this.SuppressedIces);
                command.Execute();

                trackedFiles.AddRange(command.TrackedFiles);
            }

            if (this.Messaging.EncounteredError)
            {
                return(null);
            }

            // Process uncompressed files.
            if (!this.SuppressLayout && uncompressedFiles.Any())
            {
                var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver);
                command.Compressed       = compressed;
                command.FileFacades      = uncompressedFiles;
                command.LayoutDirectory  = layoutDirectory;
                command.LongNamesInImage = longNames;
                command.ResolveMedia     = this.ResolveMedia;
                command.DatabasePath     = this.OutputPath;
                command.Execute();

                fileTransfers.AddRange(command.FileTransfers);
                trackedFiles.AddRange(command.TrackedFiles);
            }

            // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables).
            trackedFiles.AddRange(fileFacades.Select(f => this.WindowsInstallerBackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber)));

            var result = this.ServiceProvider.GetService <IBindResult>();
            result.FileTransfers = fileTransfers;
            result.TrackedFiles  = trackedFiles;
            result.Wixout        = this.CreateWixout(trackedFiles, this.Intermediate, data);

            return(result);
        }