// 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. namespace WixToolset.Core.Link { using System; using System.Collections.Generic; using System.Linq; using WixToolset.Data; using WixToolset.Extensibility.Services; internal class FindEntrySectionAndLoadSymbolsCommand { public FindEntrySectionAndLoadSymbolsCommand(IMessaging messaging, IEnumerable sections, OutputType expectedOutpuType) { this.Messaging = messaging; this.Sections = sections; this.ExpectedOutputType = expectedOutpuType; } private IMessaging Messaging { get; } private IEnumerable Sections { get; } private OutputType ExpectedOutputType { get; } /// /// Gets the located entry section after the command is executed. /// public IntermediateSection EntrySection { get; private set; } /// /// Gets the collection of loaded symbols. /// public IDictionary SymbolsByName { get; private set; } /// /// Gets the collection of possibly conflicting symbols. /// public IEnumerable PossibleConflicts { get; private set; } /// /// Gets the collection of redundant symbols that should not be included /// in the final output. /// public ISet RedundantSymbols { get; private set; } public void Execute() { var symbolsByName = new Dictionary(); var possibleConflicts = new HashSet(); var redundantSymbols = new HashSet(); if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType)) { expectedEntrySectionType = SectionType.Unknown; } foreach (var section in this.Sections) { // Try to find the one and only entry section. if (SectionType.Product == section.Type || SectionType.Module == section.Type || SectionType.PatchCreation == section.Type || SectionType.Patch == section.Type || SectionType.Bundle == section.Type) { // TODO: remove this? //if (SectionType.Unknown != expectedEntrySectionType && section.Type != expectedEntrySectionType) //{ // string outputExtension = Output.GetExtension(this.ExpectedOutputType); // this.Messaging.Write(WixWarnings.UnexpectedEntrySection(section.SourceLineNumbers, section.Type.ToString(), expectedEntrySectionType.ToString(), outputExtension)); //} if (null == this.EntrySection) { this.EntrySection = section; } else { this.Messaging.Write(ErrorMessages.MultipleEntrySections(this.EntrySection.Symbols.FirstOrDefault()?.SourceLineNumbers, this.EntrySection.Id, section.Id)); this.Messaging.Write(ErrorMessages.MultipleEntrySections2(section.Symbols.FirstOrDefault()?.SourceLineNumbers)); } } // Load all the symbols from the section's tables that create symbols. foreach (var symbol in section.Symbols.Where(t => t.Id != null)) { var symbolWithSection = new SymbolWithSection(section, symbol); if (!symbolsByName.TryGetValue(symbolWithSection.Name, out var existingSymbol)) { symbolsByName.Add(symbolWithSection.Name, symbolWithSection); } else // uh-oh, duplicate symbols. { // If the duplicate symbols are both private directories, there is a chance that they // point to identical symbols. Identical directory symbols are redundant and will not cause // conflicts. if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access && SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol)) { // Ensure identical symbol's symbol is marked redundant to ensure (should the symbol be // referenced into the final output) it will not add duplicate primary keys during // the .IDT importing. existingSymbol.AddRedundant(symbolWithSection); redundantSymbols.Add(symbolWithSection.Symbol); } else { symbolWithSection.AddPossibleConflict(existingSymbol); existingSymbol.AddPossibleConflict(symbolWithSection); possibleConflicts.Add(symbolWithSection); } } } } this.SymbolsByName = symbolsByName; this.PossibleConflicts = possibleConflicts; this.RedundantSymbols = redundantSymbols; } } }