diff options
| author | Rob Mensching <rob@firegiant.com> | 2017-12-30 17:09:15 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2017-12-30 17:09:15 -0800 |
| commit | c5190ae74ab8fe13609362efce88fa4b8cc24f34 (patch) | |
| tree | e7762224afad491c37b70bab13756552c72fdd26 /src | |
| parent | d4f73e72985dc2f36e4228358f4dc9b6114414ab (diff) | |
| download | wix-c5190ae74ab8fe13609362efce88fa4b8cc24f34.tar.gz wix-c5190ae74ab8fe13609362efce88fa4b8cc24f34.tar.bz2 wix-c5190ae74ab8fe13609362efce88fa4b8cc24f34.zip | |
Fix resolution of localizations that are embedded in intermediates
Diffstat (limited to 'src')
| -rw-r--r-- | src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 2 | ||||
| -rw-r--r-- | src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs | 75 | ||||
| -rw-r--r-- | src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | 4 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/BuildCommand.cs | 4 | ||||
| -rw-r--r-- | src/WixToolset.Core/Compiler.cs | 2 | ||||
| -rw-r--r-- | src/WixToolset.Core/Librarian.cs | 71 | ||||
| -rw-r--r-- | src/WixToolset.Core/Link/CollateLocalizationsCommand.cs | 71 | ||||
| -rw-r--r-- | src/WixToolset.Core/Linker.cs | 420 | ||||
| -rw-r--r-- | src/WixToolset.Core/Localizer.cs | 63 | ||||
| -rw-r--r-- | src/WixToolset.Core/ResolveContext.cs | 6 | ||||
| -rw-r--r-- | src/WixToolset.Core/Resolver.cs | 81 | ||||
| -rw-r--r-- | src/WixToolset.Core/WixVariableResolver.cs | 186 |
12 files changed, 505 insertions, 480 deletions
diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index c1c12ac4..cf4504b2 100644 --- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | |||
| @@ -96,7 +96,7 @@ namespace WixToolset.Core.Burn | |||
| 96 | 96 | ||
| 97 | public string IntermediateFolder { private get; set; } | 97 | public string IntermediateFolder { private get; set; } |
| 98 | 98 | ||
| 99 | public IBindVariableResolver WixVariableResolver { private get; set; } | 99 | public IVariableResolver VariableResolver { private get; set; } |
| 100 | 100 | ||
| 101 | public IEnumerable<FileTransfer> FileTransfers { get; private set; } | 101 | public IEnumerable<FileTransfer> FileTransfers { get; private set; } |
| 102 | 102 | ||
diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs index 3ded9a87..6f8da9ec 100644 --- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs | |||
| @@ -5,6 +5,7 @@ namespace WixToolset.Core.Bind | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Globalization; | 7 | using System.Globalization; |
| 8 | using System.Text; | ||
| 8 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 9 | using WixToolset.Extensibility; | 10 | using WixToolset.Extensibility; |
| 10 | using WixToolset.Extensibility.Services; | 11 | using WixToolset.Extensibility.Services; |
| @@ -46,7 +47,7 @@ namespace WixToolset.Core.Bind | |||
| 46 | // process properties first in case they refer to other binder variables | 47 | // process properties first in case they refer to other binder variables |
| 47 | if (delayedField.Row.Definition.Type == TupleDefinitionType.Property) | 48 | if (delayedField.Row.Definition.Type == TupleDefinitionType.Property) |
| 48 | { | 49 | { |
| 49 | var value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache); | 50 | var value = ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache); |
| 50 | 51 | ||
| 51 | // update the variable cache with the new value | 52 | // update the variable cache with the new value |
| 52 | var key = String.Concat("property.", propertyRow.AsString(0)); | 53 | var key = String.Concat("property.", propertyRow.AsString(0)); |
| @@ -102,7 +103,7 @@ namespace WixToolset.Core.Bind | |||
| 102 | { | 103 | { |
| 103 | try | 104 | try |
| 104 | { | 105 | { |
| 105 | var value = WixVariableResolver.ResolveDelayedVariables(delayedField.Row.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache); | 106 | var value = ResolveDelayedVariables(delayedField.Row.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache); |
| 106 | delayedField.Field.Set(value); | 107 | delayedField.Field.Set(value); |
| 107 | } | 108 | } |
| 108 | catch (WixException we) | 109 | catch (WixException we) |
| @@ -111,5 +112,75 @@ namespace WixToolset.Core.Bind | |||
| 111 | } | 112 | } |
| 112 | } | 113 | } |
| 113 | } | 114 | } |
| 115 | |||
| 116 | public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData) | ||
| 117 | { | ||
| 118 | var matches = Common.WixVariableRegex.Matches(value); | ||
| 119 | |||
| 120 | if (0 < matches.Count) | ||
| 121 | { | ||
| 122 | var sb = new StringBuilder(value); | ||
| 123 | |||
| 124 | // notice how this code walks backward through the list | ||
| 125 | // because it modifies the string as we go through it | ||
| 126 | for (int i = matches.Count - 1; 0 <= i; i--) | ||
| 127 | { | ||
| 128 | string variableNamespace = matches[i].Groups["namespace"].Value; | ||
| 129 | string variableId = matches[i].Groups["fullname"].Value; | ||
| 130 | string variableDefaultValue = null; | ||
| 131 | string variableScope = null; | ||
| 132 | |||
| 133 | // get the default value if one was specified | ||
| 134 | if (matches[i].Groups["value"].Success) | ||
| 135 | { | ||
| 136 | variableDefaultValue = matches[i].Groups["value"].Value; | ||
| 137 | } | ||
| 138 | |||
| 139 | // get the scope if one was specified | ||
| 140 | if (matches[i].Groups["scope"].Success) | ||
| 141 | { | ||
| 142 | variableScope = matches[i].Groups["scope"].Value; | ||
| 143 | if ("bind" == variableNamespace) | ||
| 144 | { | ||
| 145 | variableId = matches[i].Groups["name"].Value; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | // check for an escape sequence of !! indicating the match is not a variable expression | ||
| 150 | if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1]) | ||
| 151 | { | ||
| 152 | sb.Remove(matches[i].Index - 1, 1); | ||
| 153 | } | ||
| 154 | else | ||
| 155 | { | ||
| 156 | string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", variableId, variableScope).ToLower(CultureInfo.InvariantCulture); | ||
| 157 | string resolvedValue = variableDefaultValue; | ||
| 158 | |||
| 159 | if (resolutionData.ContainsKey(key)) | ||
| 160 | { | ||
| 161 | resolvedValue = resolutionData[key]; | ||
| 162 | } | ||
| 163 | |||
| 164 | if ("bind" == variableNamespace) | ||
| 165 | { | ||
| 166 | // insert the resolved value if it was found or display an error | ||
| 167 | if (null != resolvedValue) | ||
| 168 | { | ||
| 169 | sb.Remove(matches[i].Index, matches[i].Length); | ||
| 170 | sb.Insert(matches[i].Index, resolvedValue); | ||
| 171 | } | ||
| 172 | else | ||
| 173 | { | ||
| 174 | throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value)); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | value = sb.ToString(); | ||
| 181 | } | ||
| 182 | |||
| 183 | return value; | ||
| 184 | } | ||
| 114 | } | 185 | } |
| 115 | } | 186 | } |
diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 824eb9a5..744f022c 100644 --- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | |||
| @@ -18,7 +18,7 @@ namespace WixToolset.Core.Bind | |||
| 18 | 18 | ||
| 19 | public bool BuildingPatch { private get; set; } | 19 | public bool BuildingPatch { private get; set; } |
| 20 | 20 | ||
| 21 | public IBindVariableResolver BindVariableResolver { private get; set; } | 21 | public IVariableResolver VariableResolver { private get; set; } |
| 22 | 22 | ||
| 23 | public IEnumerable<BindPath> BindPaths { private get; set; } | 23 | public IEnumerable<BindPath> BindPaths { private get; set; } |
| 24 | 24 | ||
| @@ -62,7 +62,7 @@ namespace WixToolset.Core.Bind | |||
| 62 | var original = field.AsString(); | 62 | var original = field.AsString(); |
| 63 | if (!String.IsNullOrEmpty(original)) | 63 | if (!String.IsNullOrEmpty(original)) |
| 64 | { | 64 | { |
| 65 | var resolution = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, original, false); | 65 | var resolution = this.VariableResolver.ResolveVariables(row.SourceLineNumbers, original, false); |
| 66 | if (resolution.UpdatedValue) | 66 | if (resolution.UpdatedValue) |
| 67 | { | 67 | { |
| 68 | field.Set(resolution.Value); | 68 | field.Set(resolution.Value); |
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs index fb258179..e11cd15a 100644 --- a/src/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs | |||
| @@ -186,7 +186,9 @@ namespace WixToolset.Core.CommandLine | |||
| 186 | 186 | ||
| 187 | private void BindPhase(Intermediate output) | 187 | private void BindPhase(Intermediate output) |
| 188 | { | 188 | { |
| 189 | var localizations = this.LoadLocalizationFiles().ToList(); | 189 | var localizations = new List<Localization>(output.Localizations); |
| 190 | |||
| 191 | localizations.AddRange(this.LoadLocalizationFiles()); | ||
| 190 | 192 | ||
| 191 | // If there was an error loading localization files, then bail. | 193 | // If there was an error loading localization files, then bail. |
| 192 | if (this.Messaging.EncounteredError) | 194 | if (this.Messaging.EncounteredError) |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 8819721b..c71a8ba4 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
| @@ -2446,7 +2446,7 @@ namespace WixToolset.Core | |||
| 2446 | { | 2446 | { |
| 2447 | if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath)) | 2447 | if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath)) |
| 2448 | { | 2448 | { |
| 2449 | this.componentIdPlaceholdersResolver.AddVariable(componentIdPlaceholder, keyPath, false); | 2449 | this.componentIdPlaceholdersResolver.AddVariable(sourceLineNumbers, componentIdPlaceholder, keyPath, false); |
| 2450 | 2450 | ||
| 2451 | id = new Identifier(keyPath, AccessModifier.Private); | 2451 | id = new Identifier(keyPath, AccessModifier.Private); |
| 2452 | } | 2452 | } |
diff --git a/src/WixToolset.Core/Librarian.cs b/src/WixToolset.Core/Librarian.cs index f4191e86..c42356ac 100644 --- a/src/WixToolset.Core/Librarian.cs +++ b/src/WixToolset.Core/Librarian.cs | |||
| @@ -54,39 +54,46 @@ namespace WixToolset.Core | |||
| 54 | extension.PreCombine(this.Context); | 54 | extension.PreCombine(this.Context); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList(); | 57 | Intermediate library = null; |
| 58 | 58 | try | |
| 59 | var embedFilePaths = this.ResolveFilePathsToEmbed(sections); | 59 | { |
| 60 | var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList(); | ||
| 60 | 61 | ||
| 61 | var localizationsByCulture = this.CollateLocalizations(this.Context.Localizations); | 62 | var collate = new CollateLocalizationsCommand(this.Context.Messaging, this.Context.Localizations); |
| 63 | var localizationsByCulture = collate.Execute(); | ||
| 62 | 64 | ||
| 63 | if (this.Context.Messaging.EncounteredError) | 65 | if (this.Context.Messaging.EncounteredError) |
| 64 | { | 66 | { |
| 65 | return null; | 67 | return null; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | foreach (var section in sections) | 70 | var embedFilePaths = this.ResolveFilePathsToEmbed(sections); |
| 69 | { | ||
| 70 | section.LibraryId = this.Context.LibraryId; | ||
| 71 | } | ||
| 72 | 71 | ||
| 73 | var library = new Intermediate(this.Context.LibraryId, sections, localizationsByCulture, embedFilePaths); | 72 | foreach (var section in sections) |
| 73 | { | ||
| 74 | section.LibraryId = this.Context.LibraryId; | ||
| 75 | } | ||
| 74 | 76 | ||
| 75 | this.Validate(library); | 77 | library = new Intermediate(this.Context.LibraryId, sections, localizationsByCulture, embedFilePaths); |
| 76 | 78 | ||
| 77 | foreach (var extension in this.Context.Extensions) | 79 | this.Validate(library); |
| 80 | } | ||
| 81 | finally | ||
| 78 | { | 82 | { |
| 79 | extension.PostCombine(library); | 83 | foreach (var extension in this.Context.Extensions) |
| 84 | { | ||
| 85 | extension.PostCombine(library); | ||
| 86 | } | ||
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | return library; | 89 | return this.Context.Messaging.EncounteredError ? null : library; |
| 83 | } | 90 | } |
| 84 | 91 | ||
| 85 | /// <summary> | 92 | /// <summary> |
| 86 | /// Validate that a library contains one entry section and no duplicate symbols. | 93 | /// Validate that a library contains one entry section and no duplicate symbols. |
| 87 | /// </summary> | 94 | /// </summary> |
| 88 | /// <param name="library">Library to validate.</param> | 95 | /// <param name="library">Library to validate.</param> |
| 89 | private Intermediate Validate(Intermediate library) | 96 | private void Validate(Intermediate library) |
| 90 | { | 97 | { |
| 91 | FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, library.Sections); | 98 | FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, library.Sections); |
| 92 | find.Execute(); | 99 | find.Execute(); |
| @@ -100,34 +107,6 @@ namespace WixToolset.Core | |||
| 100 | // ReportDuplicateResolvedSymbolErrorsCommand reportDupes = new ReportDuplicateResolvedSymbolErrorsCommand(find.SymbolsWithDuplicates, resolve.ResolvedSections); | 107 | // ReportDuplicateResolvedSymbolErrorsCommand reportDupes = new ReportDuplicateResolvedSymbolErrorsCommand(find.SymbolsWithDuplicates, resolve.ResolvedSections); |
| 101 | // reportDupes.Execute(); | 108 | // reportDupes.Execute(); |
| 102 | // } | 109 | // } |
| 103 | |||
| 104 | return (this.Context.Messaging.EncounteredError ? null : library); | ||
| 105 | } | ||
| 106 | |||
| 107 | private Dictionary<string, Localization> CollateLocalizations(IEnumerable<Localization> localizations) | ||
| 108 | { | ||
| 109 | var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); | ||
| 110 | |||
| 111 | foreach (var localization in localizations) | ||
| 112 | { | ||
| 113 | if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture)) | ||
| 114 | { | ||
| 115 | try | ||
| 116 | { | ||
| 117 | existingCulture.Merge(localization); | ||
| 118 | } | ||
| 119 | catch (WixException e) | ||
| 120 | { | ||
| 121 | this.Context.Messaging.Write(e.Error); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | else | ||
| 125 | { | ||
| 126 | localizationsByCulture.Add(localization.Culture, localization); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | return localizationsByCulture; | ||
| 131 | } | 110 | } |
| 132 | 111 | ||
| 133 | private List<string> ResolveFilePathsToEmbed(IEnumerable<IntermediateSection> sections) | 112 | private List<string> ResolveFilePathsToEmbed(IEnumerable<IntermediateSection> sections) |
diff --git a/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs b/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs new file mode 100644 index 00000000..ffa66210 --- /dev/null +++ b/src/WixToolset.Core/Link/CollateLocalizationsCommand.cs | |||
| @@ -0,0 +1,71 @@ | |||
| 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.Link | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Linq; | ||
| 8 | using WixToolset.Data; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | internal class CollateLocalizationsCommand | ||
| 12 | { | ||
| 13 | public CollateLocalizationsCommand(IMessaging messaging, IEnumerable<Localization> localizations) | ||
| 14 | { | ||
| 15 | this.Messaging = messaging; | ||
| 16 | this.Localizations = localizations; | ||
| 17 | } | ||
| 18 | |||
| 19 | private IMessaging Messaging { get; } | ||
| 20 | |||
| 21 | private IEnumerable<Localization> Localizations { get; } | ||
| 22 | |||
| 23 | public Dictionary<string, Localization> Execute() | ||
| 24 | { | ||
| 25 | var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); | ||
| 26 | |||
| 27 | foreach (var localization in this.Localizations) | ||
| 28 | { | ||
| 29 | if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture)) | ||
| 30 | { | ||
| 31 | var merged = this.Merge(existingCulture, localization); | ||
| 32 | localizationsByCulture[localization.Culture] = merged; | ||
| 33 | } | ||
| 34 | else | ||
| 35 | { | ||
| 36 | localizationsByCulture.Add(localization.Culture, localization); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | return localizationsByCulture; | ||
| 41 | } | ||
| 42 | |||
| 43 | private Localization Merge(Localization existingLocalization, Localization localization) | ||
| 44 | { | ||
| 45 | var variables = existingLocalization.Variables.ToDictionary(v => v.Id); | ||
| 46 | var controls = existingLocalization.LocalizedControls.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); | ||
| 47 | |||
| 48 | foreach (var newVariable in localization.Variables) | ||
| 49 | { | ||
| 50 | if (!variables.TryGetValue(newVariable.Id, out var existingVariable) || (existingVariable.Overridable && !newVariable.Overridable)) | ||
| 51 | { | ||
| 52 | variables[newVariable.Id] = newVariable; | ||
| 53 | } | ||
| 54 | else if (!newVariable.Overridable) | ||
| 55 | { | ||
| 56 | this.Messaging.Write(ErrorMessages.DuplicateLocalizationIdentifier(newVariable.SourceLineNumbers, newVariable.Id)); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | foreach (var localizedControl in localization.LocalizedControls) | ||
| 61 | { | ||
| 62 | if (!controls.ContainsKey(localizedControl.Key)) | ||
| 63 | { | ||
| 64 | controls.Add(localizedControl.Key, localizedControl.Value); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | return new Localization(existingLocalization.Codepage, existingLocalization.Culture, variables, controls); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs index 79ddd30a..db2514fb 100644 --- a/src/WixToolset.Core/Linker.cs +++ b/src/WixToolset.Core/Linker.cs | |||
| @@ -32,9 +32,6 @@ namespace WixToolset.Core | |||
| 32 | { | 32 | { |
| 33 | this.ServiceProvider = serviceProvider; | 33 | this.ServiceProvider = serviceProvider; |
| 34 | this.sectionIdOnRows = true; // TODO: what is the correct value for this? | 34 | this.sectionIdOnRows = true; // TODO: what is the correct value for this? |
| 35 | |||
| 36 | //this.extensionData = new List<IExtensionData>(); | ||
| 37 | //this.inspectorExtensions = new List<InspectorExtension>(); | ||
| 38 | } | 35 | } |
| 39 | 36 | ||
| 40 | private IServiceProvider ServiceProvider { get; } | 37 | private IServiceProvider ServiceProvider { get; } |
| @@ -78,40 +75,49 @@ namespace WixToolset.Core | |||
| 78 | this.Context.Extensions = extensionManager.Create<ILinkerExtension>(); | 75 | this.Context.Extensions = extensionManager.Create<ILinkerExtension>(); |
| 79 | this.Context.ExtensionData = extensionManager.Create<IExtensionData>(); | 76 | this.Context.ExtensionData = extensionManager.Create<IExtensionData>(); |
| 80 | this.Context.ExpectedOutputType = this.OutputType; | 77 | this.Context.ExpectedOutputType = this.OutputType; |
| 81 | this.Context.Intermediates = this.Intermediates.Union(this.Libraries).ToList(); | 78 | this.Context.Intermediates = this.Intermediates.Concat(this.Libraries).ToList(); |
| 82 | this.Context.TupleDefinitionCreator = creator; | 79 | this.Context.TupleDefinitionCreator = creator; |
| 83 | 80 | ||
| 84 | var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList(); | 81 | foreach (var extension in this.Context.Extensions) |
| 82 | { | ||
| 83 | extension.PreLink(this.Context); | ||
| 84 | } | ||
| 85 | 85 | ||
| 86 | // Add sections from the extensions with data. | 86 | Intermediate intermediate = null; |
| 87 | foreach (var data in this.Context.ExtensionData) | 87 | try |
| 88 | { | 88 | { |
| 89 | var library = data.GetLibrary(this.Context.TupleDefinitionCreator); | 89 | var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList(); |
| 90 | var localizations = this.Context.Intermediates.SelectMany(i => i.Localizations).ToList(); | ||
| 90 | 91 | ||
| 91 | if (library != null) | 92 | // Add sections from the extensions with data. |
| 93 | foreach (var data in this.Context.ExtensionData) | ||
| 92 | { | 94 | { |
| 93 | sections.AddRange(library.Sections); | 95 | var library = data.GetLibrary(this.Context.TupleDefinitionCreator); |
| 96 | |||
| 97 | if (library != null) | ||
| 98 | { | ||
| 99 | sections.AddRange(library.Sections); | ||
| 100 | } | ||
| 94 | } | 101 | } |
| 95 | } | ||
| 96 | 102 | ||
| 97 | #if MOVE_TO_BACKEND | 103 | #if MOVE_TO_BACKEND |
| 98 | bool containsModuleSubstitution = false; | 104 | bool containsModuleSubstitution = false; |
| 99 | bool containsModuleConfiguration = false; | 105 | bool containsModuleConfiguration = false; |
| 100 | #endif | 106 | #endif |
| 101 | 107 | ||
| 102 | //this.activeOutput = null; | 108 | //this.activeOutput = null; |
| 103 | 109 | ||
| 104 | //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection(); | 110 | //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection(); |
| 105 | //IntermediateTuple customRows = new List<IntermediateTuple>(); | 111 | //IntermediateTuple customRows = new List<IntermediateTuple>(); |
| 106 | 112 | ||
| 107 | #if MOVE_TO_BACKEND | 113 | #if MOVE_TO_BACKEND |
| 108 | StringCollection generatedShortFileNameIdentifiers = new StringCollection(); | 114 | StringCollection generatedShortFileNameIdentifiers = new StringCollection(); |
| 109 | Hashtable generatedShortFileNames = new Hashtable(); | 115 | Hashtable generatedShortFileNames = new Hashtable(); |
| 110 | #endif | 116 | #endif |
| 111 | 117 | ||
| 112 | Hashtable multipleFeatureComponents = new Hashtable(); | 118 | Hashtable multipleFeatureComponents = new Hashtable(); |
| 113 | 119 | ||
| 114 | var wixVariables = new Dictionary<string, WixVariableTuple>(); | 120 | var wixVariables = new Dictionary<string, WixVariableTuple>(); |
| 115 | 121 | ||
| 116 | #if MOVE_TO_BACKEND | 122 | #if MOVE_TO_BACKEND |
| 117 | // verify that modularization types match for foreign key relationships | 123 | // verify that modularization types match for foreign key relationships |
| @@ -143,114 +149,117 @@ namespace WixToolset.Core | |||
| 143 | } | 149 | } |
| 144 | #endif | 150 | #endif |
| 145 | 151 | ||
| 146 | // First find the entry section and while processing all sections load all the symbols from all of the sections. | 152 | // First find the entry section and while processing all sections load all the symbols from all of the sections. |
| 147 | // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols); | 153 | // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols); |
| 148 | var find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, sections); | 154 | var find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, sections); |
| 149 | find.ExpectedOutputType = this.Context.ExpectedOutputType; | 155 | find.ExpectedOutputType = this.Context.ExpectedOutputType; |
| 150 | find.Execute(); | 156 | find.Execute(); |
| 151 | 157 | ||
| 152 | // Must have found the entry section by now. | 158 | // Must have found the entry section by now. |
| 153 | if (null == find.EntrySection) | 159 | if (null == find.EntrySection) |
| 154 | { | 160 | { |
| 155 | throw new WixException(ErrorMessages.MissingEntrySection(this.Context.ExpectedOutputType.ToString())); | 161 | throw new WixException(ErrorMessages.MissingEntrySection(this.Context.ExpectedOutputType.ToString())); |
| 156 | } | 162 | } |
| 157 | 163 | ||
| 158 | // Add the missing standard action symbols. | 164 | // Add the missing standard action symbols. |
| 159 | this.LoadStandardActionSymbols(find.EntrySection, find.Symbols); | 165 | this.LoadStandardActionSymbols(find.EntrySection, find.Symbols); |
| 160 | 166 | ||
| 161 | // Resolve the symbol references to find the set of sections we care about for linking. | 167 | // Resolve the symbol references to find the set of sections we care about for linking. |
| 162 | // Of course, we start with the entry section (that's how it got its name after all). | 168 | // Of course, we start with the entry section (that's how it got its name after all). |
| 163 | var resolve = new ResolveReferencesCommand(this.Context.Messaging, find.EntrySection, find.Symbols); | 169 | var resolve = new ResolveReferencesCommand(this.Context.Messaging, find.EntrySection, find.Symbols); |
| 164 | resolve.BuildingMergeModule = (SectionType.Module == find.EntrySection.Type); | 170 | resolve.BuildingMergeModule = (SectionType.Module == find.EntrySection.Type); |
| 165 | 171 | ||
| 166 | resolve.Execute(); | 172 | resolve.Execute(); |
| 167 | 173 | ||
| 168 | if (this.Context.Messaging.EncounteredError) | 174 | if (this.Context.Messaging.EncounteredError) |
| 169 | { | 175 | { |
| 170 | return null; | 176 | return null; |
| 171 | } | 177 | } |
| 172 | 178 | ||
| 173 | // Reset the sections to only those that were resolved then flatten the complex | 179 | // Reset the sections to only those that were resolved then flatten the complex |
| 174 | // references that particpate in groups. | 180 | // references that particpate in groups. |
| 175 | sections = resolve.ResolvedSections.ToList(); | 181 | sections = resolve.ResolvedSections.ToList(); |
| 176 | 182 | ||
| 177 | this.FlattenSectionsComplexReferences(sections); | 183 | // TODO: consider filtering "localizations" down to only those localizations from |
| 184 | // intermediates in the sections. | ||
| 178 | 185 | ||
| 179 | if (this.Context.Messaging.EncounteredError) | 186 | this.FlattenSectionsComplexReferences(sections); |
| 180 | { | ||
| 181 | return null; | ||
| 182 | } | ||
| 183 | 187 | ||
| 184 | // The hard part in linking is processing the complex references. | 188 | if (this.Context.Messaging.EncounteredError) |
| 185 | var referencedComponents = new HashSet<string>(); | 189 | { |
| 186 | var componentsToFeatures = new ConnectToFeatureCollection(); | 190 | return null; |
| 187 | var featuresToFeatures = new ConnectToFeatureCollection(); | 191 | } |
| 188 | var modulesToFeatures = new ConnectToFeatureCollection(); | ||
| 189 | this.ProcessComplexReferences(find.EntrySection, sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures); | ||
| 190 | 192 | ||
| 191 | if (this.Context.Messaging.EncounteredError) | 193 | // The hard part in linking is processing the complex references. |
| 192 | { | 194 | var referencedComponents = new HashSet<string>(); |
| 193 | return null; | 195 | var componentsToFeatures = new ConnectToFeatureCollection(); |
| 194 | } | 196 | var featuresToFeatures = new ConnectToFeatureCollection(); |
| 197 | var modulesToFeatures = new ConnectToFeatureCollection(); | ||
| 198 | this.ProcessComplexReferences(find.EntrySection, sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures); | ||
| 195 | 199 | ||
| 196 | // Display an error message for Components that were not referenced by a Feature. | 200 | if (this.Context.Messaging.EncounteredError) |
| 197 | foreach (var symbol in resolve.ReferencedSymbols.Where(s => s.Row.Definition.Type == TupleDefinitionType.Component)) | ||
| 198 | { | ||
| 199 | if (!referencedComponents.Contains(symbol.Name)) | ||
| 200 | { | 201 | { |
| 201 | this.OnMessage(ErrorMessages.OrphanedComponent(symbol.Row.SourceLineNumbers, symbol.Row.Id.Id)); | 202 | return null; |
| 202 | } | 203 | } |
| 203 | } | ||
| 204 | 204 | ||
| 205 | // Report duplicates that would ultimately end up being primary key collisions. | 205 | // Display an error message for Components that were not referenced by a Feature. |
| 206 | var reportDupes = new ReportConflictingSymbolsCommand(this.Context.Messaging, find.PossiblyConflictingSymbols, resolve.ResolvedSections); | 206 | foreach (var symbol in resolve.ReferencedSymbols.Where(s => s.Row.Definition.Type == TupleDefinitionType.Component)) |
| 207 | reportDupes.Execute(); | 207 | { |
| 208 | if (!referencedComponents.Contains(symbol.Name)) | ||
| 209 | { | ||
| 210 | this.OnMessage(ErrorMessages.OrphanedComponent(symbol.Row.SourceLineNumbers, symbol.Row.Id.Id)); | ||
| 211 | } | ||
| 212 | } | ||
| 208 | 213 | ||
| 209 | if (this.Context.Messaging.EncounteredError) | 214 | // Report duplicates that would ultimately end up being primary key collisions. |
| 210 | { | 215 | var reportDupes = new ReportConflictingSymbolsCommand(this.Context.Messaging, find.PossiblyConflictingSymbols, resolve.ResolvedSections); |
| 211 | return null; | 216 | reportDupes.Execute(); |
| 212 | } | ||
| 213 | 217 | ||
| 214 | // resolve the feature to feature connects | 218 | if (this.Context.Messaging.EncounteredError) |
| 215 | this.ResolveFeatureToFeatureConnects(featuresToFeatures, find.Symbols); | 219 | { |
| 220 | return null; | ||
| 221 | } | ||
| 216 | 222 | ||
| 217 | // start generating OutputTables and OutputRows for all the sections in the output | 223 | // resolve the feature to feature connects |
| 218 | var ensureTableRows = new List<IntermediateTuple>(); | 224 | this.ResolveFeatureToFeatureConnects(featuresToFeatures, find.Symbols); |
| 219 | 225 | ||
| 220 | // Create the section to hold the linked content. | 226 | // start generating OutputTables and OutputRows for all the sections in the output |
| 221 | var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage); | 227 | var ensureTableRows = new List<IntermediateTuple>(); |
| 222 | 228 | ||
| 223 | var sectionCount = 0; | 229 | // Create the section to hold the linked content. |
| 230 | var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage); | ||
| 224 | 231 | ||
| 225 | foreach (var section in sections) | 232 | var sectionCount = 0; |
| 226 | { | ||
| 227 | sectionCount++; | ||
| 228 | 233 | ||
| 229 | var sectionId = section.Id; | 234 | foreach (var section in sections) |
| 230 | if (null == sectionId && this.sectionIdOnRows) | ||
| 231 | { | 235 | { |
| 232 | sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture); | 236 | sectionCount++; |
| 233 | } | ||
| 234 | 237 | ||
| 235 | foreach (var tuple in section.Tuples) | 238 | var sectionId = section.Id; |
| 236 | { | 239 | if (null == sectionId && this.sectionIdOnRows) |
| 237 | var copyTuple = true; // by default, copy tuples. | 240 | { |
| 241 | sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture); | ||
| 242 | } | ||
| 238 | 243 | ||
| 239 | // handle special tables | 244 | foreach (var tuple in section.Tuples) |
| 240 | switch (tuple.Definition.Type) | ||
| 241 | { | 245 | { |
| 246 | var copyTuple = true; // by default, copy tuples. | ||
| 247 | |||
| 248 | // handle special tables | ||
| 249 | switch (tuple.Definition.Type) | ||
| 250 | { | ||
| 242 | #if MOVE_TO_BACKEND | 251 | #if MOVE_TO_BACKEND |
| 243 | case "AppSearch": | 252 | case "AppSearch": |
| 244 | this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]); | 253 | this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]); |
| 245 | break; | 254 | break; |
| 246 | #endif | 255 | #endif |
| 247 | 256 | ||
| 248 | case TupleDefinitionType.Class: | 257 | case TupleDefinitionType.Class: |
| 249 | if (SectionType.Product == resolvedSection.Type) | 258 | if (SectionType.Product == resolvedSection.Type) |
| 250 | { | 259 | { |
| 251 | this.ResolveFeatures(tuple, 2, 11, componentsToFeatures, multipleFeatureComponents); | 260 | this.ResolveFeatures(tuple, 2, 11, componentsToFeatures, multipleFeatureComponents); |
| 252 | } | 261 | } |
| 253 | break; | 262 | break; |
| 254 | 263 | ||
| 255 | #if MOVE_TO_BACKEND | 264 | #if MOVE_TO_BACKEND |
| 256 | case "CustomAction": | 265 | case "CustomAction": |
| @@ -304,12 +313,12 @@ namespace WixToolset.Core | |||
| 304 | } | 313 | } |
| 305 | break; | 314 | break; |
| 306 | #endif | 315 | #endif |
| 307 | case TupleDefinitionType.Extension: | 316 | case TupleDefinitionType.Extension: |
| 308 | if (SectionType.Product == resolvedSection.Type) | 317 | if (SectionType.Product == resolvedSection.Type) |
| 309 | { | 318 | { |
| 310 | this.ResolveFeatures(tuple, 1, 4, componentsToFeatures, multipleFeatureComponents); | 319 | this.ResolveFeatures(tuple, 1, 4, componentsToFeatures, multipleFeatureComponents); |
| 311 | } | 320 | } |
| 312 | break; | 321 | break; |
| 313 | 322 | ||
| 314 | #if MOVE_TO_BACKEND | 323 | #if MOVE_TO_BACKEND |
| 315 | case TupleDefinitionType.ModuleSubstitution: | 324 | case TupleDefinitionType.ModuleSubstitution: |
| @@ -321,12 +330,12 @@ namespace WixToolset.Core | |||
| 321 | break; | 330 | break; |
| 322 | #endif | 331 | #endif |
| 323 | 332 | ||
| 324 | case TupleDefinitionType.MsiAssembly: | 333 | case TupleDefinitionType.MsiAssembly: |
| 325 | if (SectionType.Product == resolvedSection.Type) | 334 | if (SectionType.Product == resolvedSection.Type) |
| 326 | { | 335 | { |
| 327 | this.ResolveFeatures(tuple, 0, 1, componentsToFeatures, multipleFeatureComponents); | 336 | this.ResolveFeatures(tuple, 0, 1, componentsToFeatures, multipleFeatureComponents); |
| 328 | } | 337 | } |
| 329 | break; | 338 | break; |
| 330 | 339 | ||
| 331 | #if MOVE_TO_BACKEND | 340 | #if MOVE_TO_BACKEND |
| 332 | case "ProgId": | 341 | case "ProgId": |
| @@ -348,26 +357,26 @@ namespace WixToolset.Core | |||
| 348 | break; | 357 | break; |
| 349 | #endif | 358 | #endif |
| 350 | 359 | ||
| 351 | case TupleDefinitionType.PublishComponent: | 360 | case TupleDefinitionType.PublishComponent: |
| 352 | if (SectionType.Product == resolvedSection.Type) | 361 | if (SectionType.Product == resolvedSection.Type) |
| 353 | { | 362 | { |
| 354 | this.ResolveFeatures(tuple, 2, 4, componentsToFeatures, multipleFeatureComponents); | 363 | this.ResolveFeatures(tuple, 2, 4, componentsToFeatures, multipleFeatureComponents); |
| 355 | } | 364 | } |
| 356 | break; | 365 | break; |
| 357 | 366 | ||
| 358 | case TupleDefinitionType.Shortcut: | 367 | case TupleDefinitionType.Shortcut: |
| 359 | if (SectionType.Product == resolvedSection.Type) | 368 | if (SectionType.Product == resolvedSection.Type) |
| 360 | { | 369 | { |
| 361 | this.ResolveFeatures(tuple, 3, 4, componentsToFeatures, multipleFeatureComponents); | 370 | this.ResolveFeatures(tuple, 3, 4, componentsToFeatures, multipleFeatureComponents); |
| 362 | } | 371 | } |
| 363 | break; | 372 | break; |
| 364 | 373 | ||
| 365 | case TupleDefinitionType.TypeLib: | 374 | case TupleDefinitionType.TypeLib: |
| 366 | if (SectionType.Product == resolvedSection.Type) | 375 | if (SectionType.Product == resolvedSection.Type) |
| 367 | { | 376 | { |
| 368 | this.ResolveFeatures(tuple, 2, 6, componentsToFeatures, multipleFeatureComponents); | 377 | this.ResolveFeatures(tuple, 2, 6, componentsToFeatures, multipleFeatureComponents); |
| 369 | } | 378 | } |
| 370 | break; | 379 | break; |
| 371 | 380 | ||
| 372 | #if SOLVE_CUSTOM_TABLE | 381 | #if SOLVE_CUSTOM_TABLE |
| 373 | case "WixCustomTable": | 382 | case "WixCustomTable": |
| @@ -385,9 +394,9 @@ namespace WixToolset.Core | |||
| 385 | break; | 394 | break; |
| 386 | #endif | 395 | #endif |
| 387 | 396 | ||
| 388 | case TupleDefinitionType.WixEnsureTable: | 397 | case TupleDefinitionType.WixEnsureTable: |
| 389 | ensureTableRows.Add(tuple); | 398 | ensureTableRows.Add(tuple); |
| 390 | break; | 399 | break; |
| 391 | 400 | ||
| 392 | 401 | ||
| 393 | #if MOVE_TO_BACKEND | 402 | #if MOVE_TO_BACKEND |
| @@ -410,66 +419,66 @@ namespace WixToolset.Core | |||
| 410 | break; | 419 | break; |
| 411 | #endif | 420 | #endif |
| 412 | 421 | ||
| 413 | case TupleDefinitionType.WixMerge: | 422 | case TupleDefinitionType.WixMerge: |
| 414 | if (SectionType.Product == resolvedSection.Type) | 423 | if (SectionType.Product == resolvedSection.Type) |
| 415 | { | 424 | { |
| 416 | this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null); | 425 | this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null); |
| 417 | } | 426 | } |
| 418 | break; | 427 | break; |
| 419 | |||
| 420 | case TupleDefinitionType.WixComplexReference: | ||
| 421 | copyTuple = false; | ||
| 422 | break; | ||
| 423 | 428 | ||
| 424 | case TupleDefinitionType.WixSimpleReference: | 429 | case TupleDefinitionType.WixComplexReference: |
| 425 | copyTuple = false; | 430 | copyTuple = false; |
| 426 | break; | 431 | break; |
| 427 | 432 | ||
| 428 | case TupleDefinitionType.WixVariable: | 433 | case TupleDefinitionType.WixSimpleReference: |
| 429 | // check for colliding values and collect the wix variable rows | 434 | copyTuple = false; |
| 430 | { | 435 | break; |
| 431 | var row = (WixVariableTuple)tuple; | ||
| 432 | 436 | ||
| 433 | if (wixVariables.TryGetValue(row.WixVariable, out var collidingRow)) | 437 | case TupleDefinitionType.WixVariable: |
| 438 | // check for colliding values and collect the wix variable rows | ||
| 434 | { | 439 | { |
| 435 | if (collidingRow.Overridable && !row.Overridable) | 440 | var row = (WixVariableTuple)tuple; |
| 441 | |||
| 442 | if (wixVariables.TryGetValue(row.WixVariable, out var collidingRow)) | ||
| 436 | { | 443 | { |
| 437 | wixVariables[row.WixVariable] = row; | 444 | if (collidingRow.Overridable && !row.Overridable) |
| 445 | { | ||
| 446 | wixVariables[row.WixVariable] = row; | ||
| 447 | } | ||
| 448 | else if (!row.Overridable || (collidingRow.Overridable && row.Overridable)) | ||
| 449 | { | ||
| 450 | this.OnMessage(ErrorMessages.WixVariableCollision(row.SourceLineNumbers, row.WixVariable)); | ||
| 451 | } | ||
| 438 | } | 452 | } |
| 439 | else if (!row.Overridable || (collidingRow.Overridable && row.Overridable)) | 453 | else |
| 440 | { | 454 | { |
| 441 | this.OnMessage(ErrorMessages.WixVariableCollision(row.SourceLineNumbers, row.WixVariable)); | 455 | wixVariables.Add(row.WixVariable, row); |
| 442 | } | 456 | } |
| 443 | } | 457 | } |
| 444 | else | ||
| 445 | { | ||
| 446 | wixVariables.Add(row.WixVariable, row); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | 458 | ||
| 450 | copyTuple = false; | 459 | copyTuple = false; |
| 451 | break; | 460 | break; |
| 452 | } | 461 | } |
| 453 | 462 | ||
| 454 | if (copyTuple) | 463 | if (copyTuple) |
| 455 | { | 464 | { |
| 456 | resolvedSection.Tuples.Add(tuple); | 465 | resolvedSection.Tuples.Add(tuple); |
| 466 | } | ||
| 457 | } | 467 | } |
| 458 | } | 468 | } |
| 459 | } | ||
| 460 | 469 | ||
| 461 | // copy the module to feature connections into the output | 470 | // copy the module to feature connections into the output |
| 462 | foreach (ConnectToFeature connectToFeature in modulesToFeatures) | 471 | foreach (ConnectToFeature connectToFeature in modulesToFeatures) |
| 463 | { | ||
| 464 | foreach (var feature in connectToFeature.ConnectFeatures) | ||
| 465 | { | 472 | { |
| 466 | var row = new WixFeatureModulesTuple(); | 473 | foreach (var feature in connectToFeature.ConnectFeatures) |
| 467 | row.Feature_ = feature; | 474 | { |
| 468 | row.WixMerge_ = connectToFeature.ChildId; | 475 | var row = new WixFeatureModulesTuple(); |
| 476 | row.Feature_ = feature; | ||
| 477 | row.WixMerge_ = connectToFeature.ChildId; | ||
| 469 | 478 | ||
| 470 | resolvedSection.Tuples.Add(row); | 479 | resolvedSection.Tuples.Add(row); |
| 480 | } | ||
| 471 | } | 481 | } |
| 472 | } | ||
| 473 | 482 | ||
| 474 | #if MOVE_TO_BACKEND | 483 | #if MOVE_TO_BACKEND |
| 475 | // ensure the creation of tables that need to exist | 484 | // ensure the creation of tables that need to exist |
| @@ -612,24 +621,24 @@ namespace WixToolset.Core | |||
| 612 | } | 621 | } |
| 613 | #endif | 622 | #endif |
| 614 | 623 | ||
| 615 | //correct the section Id in FeatureComponents table | 624 | //correct the section Id in FeatureComponents table |
| 616 | if (this.sectionIdOnRows) | 625 | if (this.sectionIdOnRows) |
| 617 | { | 626 | { |
| 618 | //var componentSectionIds = new Dictionary<string, string>(); | 627 | //var componentSectionIds = new Dictionary<string, string>(); |
| 619 | 628 | ||
| 620 | //foreach (var componentTuple in entrySection.Tuples.OfType<ComponentTuple>()) | 629 | //foreach (var componentTuple in entrySection.Tuples.OfType<ComponentTuple>()) |
| 621 | //{ | 630 | //{ |
| 622 | // componentSectionIds.Add(componentTuple.Id.Id, componentTuple.SectionId); | 631 | // componentSectionIds.Add(componentTuple.Id.Id, componentTuple.SectionId); |
| 623 | //} | 632 | //} |
| 624 | 633 | ||
| 625 | //foreach (var featureComponentTuple in entrySection.Tuples.OfType<FeatureComponentsTuple>()) | 634 | //foreach (var featureComponentTuple in entrySection.Tuples.OfType<FeatureComponentsTuple>()) |
| 626 | //{ | 635 | //{ |
| 627 | // if (componentSectionIds.TryGetValue(featureComponentTuple.Component_, out var componentSectionId)) | 636 | // if (componentSectionIds.TryGetValue(featureComponentTuple.Component_, out var componentSectionId)) |
| 628 | // { | 637 | // { |
| 629 | // featureComponentTuple.SectionId = componentSectionId; | 638 | // featureComponentTuple.SectionId = componentSectionId; |
| 630 | // } | 639 | // } |
| 631 | //} | 640 | //} |
| 632 | } | 641 | } |
| 633 | 642 | ||
| 634 | #if MOVE_TO_BACKEND | 643 | #if MOVE_TO_BACKEND |
| 635 | // add the ModuleSubstitution table to the ModuleIgnoreTable | 644 | // add the ModuleSubstitution table to the ModuleIgnoreTable |
| @@ -698,27 +707,38 @@ namespace WixToolset.Core | |||
| 698 | } | 707 | } |
| 699 | #endif | 708 | #endif |
| 700 | 709 | ||
| 701 | // copy the wix variable rows to the output after all overriding has been accounted for. | 710 | // copy the wix variable rows to the output after all overriding has been accounted for. |
| 702 | foreach (var row in wixVariables.Values) | 711 | foreach (var row in wixVariables.Values) |
| 703 | { | 712 | { |
| 704 | resolvedSection.Tuples.Add(row); | 713 | resolvedSection.Tuples.Add(row); |
| 705 | } | 714 | } |
| 706 | 715 | ||
| 707 | // Bundles have groups of data that must be flattened in a way different from other types. | 716 | // Bundles have groups of data that must be flattened in a way different from other types. |
| 708 | this.FlattenBundleTables(resolvedSection); | 717 | this.FlattenBundleTables(resolvedSection); |
| 709 | 718 | ||
| 710 | if (this.Context.Messaging.EncounteredError) | 719 | if (this.Context.Messaging.EncounteredError) |
| 711 | { | 720 | { |
| 712 | return null; | 721 | return null; |
| 713 | } | 722 | } |
| 714 | 723 | ||
| 715 | var output = new Intermediate(resolvedSection.Id, new[] { resolvedSection }, null, null); | 724 | var collate = new CollateLocalizationsCommand(this.Context.Messaging, localizations); |
| 725 | var localizationsByCulture = collate.Execute(); | ||
| 726 | |||
| 727 | intermediate = new Intermediate(resolvedSection.Id, new[] { resolvedSection }, localizationsByCulture, null); | ||
| 716 | 728 | ||
| 717 | #if MOVE_TO_BACKEND | 729 | #if MOVE_TO_BACKEND |
| 718 | this.CheckOutputConsistency(output); | 730 | this.CheckOutputConsistency(output); |
| 719 | #endif | 731 | #endif |
| 732 | } | ||
| 733 | finally | ||
| 734 | { | ||
| 735 | foreach (var extension in this.Context.Extensions) | ||
| 736 | { | ||
| 737 | extension.PostLink(intermediate); | ||
| 738 | } | ||
| 739 | } | ||
| 720 | 740 | ||
| 721 | return this.Context.Messaging.EncounteredError ? null : output; | 741 | return this.Context.Messaging.EncounteredError ? null : intermediate; |
| 722 | } | 742 | } |
| 723 | 743 | ||
| 724 | #if SOLVE_CUSTOM_TABLE | 744 | #if SOLVE_CUSTOM_TABLE |
diff --git a/src/WixToolset.Core/Localizer.cs b/src/WixToolset.Core/Localizer.cs index 2e7b19b6..38d8864e 100644 --- a/src/WixToolset.Core/Localizer.cs +++ b/src/WixToolset.Core/Localizer.cs | |||
| @@ -14,72 +14,11 @@ namespace WixToolset.Core | |||
| 14 | /// <summary> | 14 | /// <summary> |
| 15 | /// Parses localization files and localizes database values. | 15 | /// Parses localization files and localizes database values. |
| 16 | /// </summary> | 16 | /// </summary> |
| 17 | public sealed class Localizer : ILocalizer | 17 | public sealed class Localizer |
| 18 | { | 18 | { |
| 19 | public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl"; | 19 | public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl"; |
| 20 | private static string XmlElementName = "WixLocalization"; | 20 | private static string XmlElementName = "WixLocalization"; |
| 21 | 21 | ||
| 22 | private Dictionary<string, BindVariable> variables; | ||
| 23 | private Dictionary<string, LocalizedControl> localizedControls; | ||
| 24 | |||
| 25 | /// <summary> | ||
| 26 | /// Instantiate a new Localizer. | ||
| 27 | /// </summary> | ||
| 28 | public Localizer(IMessaging messaging, IEnumerable<Localization> localizations) | ||
| 29 | { | ||
| 30 | this.Codepage = -1; | ||
| 31 | this.variables = new Dictionary<string, BindVariable>(); | ||
| 32 | this.localizedControls = new Dictionary<string, LocalizedControl>(); | ||
| 33 | |||
| 34 | foreach (var localization in localizations) | ||
| 35 | { | ||
| 36 | if (-1 == this.Codepage) | ||
| 37 | { | ||
| 38 | this.Codepage = localization.Codepage; | ||
| 39 | } | ||
| 40 | |||
| 41 | foreach (var variable in localization.Variables) | ||
| 42 | { | ||
| 43 | Localizer.AddWixVariable(messaging, this.variables, variable); | ||
| 44 | } | ||
| 45 | |||
| 46 | foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls) | ||
| 47 | { | ||
| 48 | if (!this.localizedControls.ContainsKey(localizedControl.Key)) | ||
| 49 | { | ||
| 50 | this.localizedControls.Add(localizedControl.Key, localizedControl.Value); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /// <summary> | ||
| 57 | /// Gets the codepage. | ||
| 58 | /// </summary> | ||
| 59 | /// <value>The codepage.</value> | ||
| 60 | public int Codepage { get; } | ||
| 61 | |||
| 62 | /// <summary> | ||
| 63 | /// Get a localized data value. | ||
| 64 | /// </summary> | ||
| 65 | /// <param name="id">The name of the localization variable.</param> | ||
| 66 | /// <returns>The localized data value or null if it wasn't found.</returns> | ||
| 67 | public string GetLocalizedValue(string id) | ||
| 68 | { | ||
| 69 | return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null; | ||
| 70 | } | ||
| 71 | |||
| 72 | /// <summary> | ||
| 73 | /// Get a localized control. | ||
| 74 | /// </summary> | ||
| 75 | /// <param name="dialog">The optional id of the control's dialog.</param> | ||
| 76 | /// <param name="control">The id of the control.</param> | ||
| 77 | /// <returns>The localized control or null if it wasn't found.</returns> | ||
| 78 | public LocalizedControl GetLocalizedControl(string dialog, string control) | ||
| 79 | { | ||
| 80 | return this.localizedControls.TryGetValue(LocalizedControl.GetKey(dialog, control), out var localizedControl) ? localizedControl : null; | ||
| 81 | } | ||
| 82 | |||
| 83 | /// <summary> | 22 | /// <summary> |
| 84 | /// Loads a localization file from a path on disk. | 23 | /// Loads a localization file from a path on disk. |
| 85 | /// </summary> | 24 | /// </summary> |
diff --git a/src/WixToolset.Core/ResolveContext.cs b/src/WixToolset.Core/ResolveContext.cs index 6d7b9df1..ca80d99c 100644 --- a/src/WixToolset.Core/ResolveContext.cs +++ b/src/WixToolset.Core/ResolveContext.cs | |||
| @@ -23,10 +23,14 @@ namespace WixToolset.Core | |||
| 23 | 23 | ||
| 24 | public IEnumerable<IResolverExtension> Extensions { get; set; } | 24 | public IEnumerable<IResolverExtension> Extensions { get; set; } |
| 25 | 25 | ||
| 26 | public IEnumerable<IExtensionData> ExtensionData { get; set; } | ||
| 27 | |||
| 26 | public string IntermediateFolder { get; set; } | 28 | public string IntermediateFolder { get; set; } |
| 27 | 29 | ||
| 28 | public Intermediate IntermediateRepresentation { get; set; } | 30 | public Intermediate IntermediateRepresentation { get; set; } |
| 29 | 31 | ||
| 30 | public IBindVariableResolver WixVariableResolver { get; set; } | 32 | public IEnumerable<Localization> Localizations { get; set; } |
| 33 | |||
| 34 | public IVariableResolver VariableResolver { get; set; } | ||
| 31 | } | 35 | } |
| 32 | } | 36 | } |
diff --git a/src/WixToolset.Core/Resolver.cs b/src/WixToolset.Core/Resolver.cs index 1b72e3d0..c2d5cc87 100644 --- a/src/WixToolset.Core/Resolver.cs +++ b/src/WixToolset.Core/Resolver.cs | |||
| @@ -25,48 +25,42 @@ namespace WixToolset.Core | |||
| 25 | 25 | ||
| 26 | public IEnumerable<BindPath> BindPaths { get; set; } | 26 | public IEnumerable<BindPath> BindPaths { get; set; } |
| 27 | 27 | ||
| 28 | public Intermediate IntermediateRepresentation { get; set; } | ||
| 29 | |||
| 30 | public string IntermediateFolder { get; set; } | 28 | public string IntermediateFolder { get; set; } |
| 31 | 29 | ||
| 32 | public IEnumerable<Localization> Localizations { get; set; } | 30 | public Intermediate IntermediateRepresentation { get; set; } |
| 33 | 31 | ||
| 34 | private IMessaging Messaging { get; set; } | 32 | public IEnumerable<Localization> Localizations { get; set; } |
| 35 | 33 | ||
| 36 | public ResolveResult Execute() | 34 | public ResolveResult Execute() |
| 37 | { | 35 | { |
| 38 | this.Messaging = this.ServiceProvider.GetService<IMessaging>(); | 36 | var extensionManager = this.ServiceProvider.GetService<IExtensionManager>(); |
| 39 | |||
| 40 | var localizer = new Localizer(this.Messaging, this.Localizations); | ||
| 41 | |||
| 42 | var variableResolver = new WixVariableResolver(this.Messaging, localizer); | ||
| 43 | this.PopulateVariableResolver(variableResolver); | ||
| 44 | 37 | ||
| 45 | var context = this.ServiceProvider.GetService<IResolveContext>(); | 38 | var context = this.ServiceProvider.GetService<IResolveContext>(); |
| 46 | context.Messaging = this.Messaging; | 39 | context.Messaging = this.ServiceProvider.GetService<IMessaging>(); |
| 47 | context.BindPaths = this.BindPaths; | 40 | context.BindPaths = this.BindPaths; |
| 48 | context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().Create<IResolverExtension>(); | 41 | context.Extensions = extensionManager.Create<IResolverExtension>(); |
| 42 | context.ExtensionData = extensionManager.Create<IExtensionData>(); | ||
| 49 | context.IntermediateFolder = this.IntermediateFolder; | 43 | context.IntermediateFolder = this.IntermediateFolder; |
| 50 | context.IntermediateRepresentation = this.IntermediateRepresentation; | 44 | context.IntermediateRepresentation = this.IntermediateRepresentation; |
| 51 | context.WixVariableResolver = variableResolver; | 45 | context.Localizations = this.Localizations; |
| 46 | context.VariableResolver = new WixVariableResolver(context.Messaging); | ||
| 52 | 47 | ||
| 53 | // Preresolve. | ||
| 54 | // | ||
| 55 | foreach (IResolverExtension extension in context.Extensions) | 48 | foreach (IResolverExtension extension in context.Extensions) |
| 56 | { | 49 | { |
| 57 | extension.PreResolve(context); | 50 | extension.PreResolve(context); |
| 58 | } | 51 | } |
| 59 | 52 | ||
| 60 | // Resolve. | 53 | ResolveResult resolveResult = null; |
| 61 | // | 54 | try |
| 62 | this.LocalizeUI(context); | 55 | { |
| 56 | PopulateVariableResolver(context); | ||
| 63 | 57 | ||
| 64 | var resolveResult = this.Resolve(localizer.Codepage, context); | 58 | this.LocalizeUI(context); |
| 65 | 59 | ||
| 66 | if (resolveResult != null) | 60 | resolveResult = this.Resolve(context); |
| 61 | } | ||
| 62 | finally | ||
| 67 | { | 63 | { |
| 68 | // Postresolve. | ||
| 69 | // | ||
| 70 | foreach (IResolverExtension extension in context.Extensions) | 64 | foreach (IResolverExtension extension in context.Extensions) |
| 71 | { | 65 | { |
| 72 | extension.PostResolve(resolveResult); | 66 | extension.PostResolve(resolveResult); |
| @@ -76,7 +70,7 @@ namespace WixToolset.Core | |||
| 76 | return resolveResult; | 70 | return resolveResult; |
| 77 | } | 71 | } |
| 78 | 72 | ||
| 79 | private ResolveResult Resolve(int codepage, IResolveContext context) | 73 | private ResolveResult Resolve(IResolveContext context) |
| 80 | { | 74 | { |
| 81 | var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch); | 75 | var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch); |
| 82 | 76 | ||
| @@ -87,7 +81,7 @@ namespace WixToolset.Core | |||
| 87 | var command = new ResolveFieldsCommand(); | 81 | var command = new ResolveFieldsCommand(); |
| 88 | command.Messaging = context.Messaging; | 82 | command.Messaging = context.Messaging; |
| 89 | command.BuildingPatch = buildingPatch; | 83 | command.BuildingPatch = buildingPatch; |
| 90 | command.BindVariableResolver = context.WixVariableResolver; | 84 | command.VariableResolver = context.VariableResolver; |
| 91 | command.BindPaths = context.BindPaths; | 85 | command.BindPaths = context.BindPaths; |
| 92 | command.Extensions = context.Extensions; | 86 | command.Extensions = context.Extensions; |
| 93 | command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | 87 | command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; |
| @@ -122,7 +116,7 @@ namespace WixToolset.Core | |||
| 122 | 116 | ||
| 123 | return new ResolveResult | 117 | return new ResolveResult |
| 124 | { | 118 | { |
| 125 | Codepage = codepage, | 119 | Codepage = context.VariableResolver.Codepage, |
| 126 | ExpectedEmbeddedFiles = expectedEmbeddedFiles, | 120 | ExpectedEmbeddedFiles = expectedEmbeddedFiles, |
| 127 | DelayedFields = delayedFields, | 121 | DelayedFields = delayedFields, |
| 128 | IntermediateRepresentation = context.IntermediateRepresentation | 122 | IntermediateRepresentation = context.IntermediateRepresentation |
| @@ -140,7 +134,7 @@ namespace WixToolset.Core | |||
| 140 | { | 134 | { |
| 141 | string dialog = row.Dialog; | 135 | string dialog = row.Dialog; |
| 142 | 136 | ||
| 143 | if (context.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl)) | 137 | if (context.VariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl)) |
| 144 | { | 138 | { |
| 145 | if (CompilerConstants.IntegerNotSet != localizedControl.X) | 139 | if (CompilerConstants.IntegerNotSet != localizedControl.X) |
| 146 | { | 140 | { |
| @@ -176,7 +170,7 @@ namespace WixToolset.Core | |||
| 176 | string dialog = row.Dialog_; | 170 | string dialog = row.Dialog_; |
| 177 | string control = row.Control; | 171 | string control = row.Control; |
| 178 | 172 | ||
| 179 | if (context.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl)) | 173 | if (context.VariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl)) |
| 180 | { | 174 | { |
| 181 | if (CompilerConstants.IntegerNotSet != localizedControl.X) | 175 | if (CompilerConstants.IntegerNotSet != localizedControl.X) |
| 182 | { | 176 | { |
| @@ -209,21 +203,34 @@ namespace WixToolset.Core | |||
| 209 | } | 203 | } |
| 210 | } | 204 | } |
| 211 | 205 | ||
| 212 | private void PopulateVariableResolver(WixVariableResolver resolver) | 206 | private static void PopulateVariableResolver(IResolveContext context) |
| 213 | { | 207 | { |
| 214 | // Gather all the wix variables. | 208 | var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>(); |
| 215 | var wixVariableTuples = this.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>(); | 209 | |
| 216 | foreach (var tuple in wixVariableTuples) | 210 | var localizations = context.Localizations.Concat(context.IntermediateRepresentation.Localizations).ToList(); |
| 211 | |||
| 212 | // Add localizations from the extensions with data. | ||
| 213 | foreach (var data in context.ExtensionData) | ||
| 217 | { | 214 | { |
| 218 | try | 215 | var library = data.GetLibrary(creator); |
| 219 | { | 216 | |
| 220 | resolver.AddVariable(tuple.WixVariable, tuple.Value, tuple.Overridable); | 217 | if (library?.Localizations != null) |
| 221 | } | ||
| 222 | catch (ArgumentException) | ||
| 223 | { | 218 | { |
| 224 | this.Messaging.Write(ErrorMessages.WixVariableCollision(tuple.SourceLineNumbers, tuple.WixVariable)); | 219 | localizations.AddRange(library.Localizations); |
| 225 | } | 220 | } |
| 226 | } | 221 | } |
| 222 | |||
| 223 | foreach (var localization in localizations) | ||
| 224 | { | ||
| 225 | context.VariableResolver.AddLocalization(localization); | ||
| 226 | } | ||
| 227 | |||
| 228 | // Gather all the wix variables. | ||
| 229 | var wixVariableTuples = context.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>(); | ||
| 230 | foreach (var tuple in wixVariableTuples) | ||
| 231 | { | ||
| 232 | context.VariableResolver.AddVariable(tuple.SourceLineNumbers, tuple.WixVariable, tuple.Value, tuple.Overridable); | ||
| 233 | } | ||
| 227 | } | 234 | } |
| 228 | } | 235 | } |
| 229 | } | 236 | } |
diff --git a/src/WixToolset.Core/WixVariableResolver.cs b/src/WixToolset.Core/WixVariableResolver.cs index 7339840e..7fe77077 100644 --- a/src/WixToolset.Core/WixVariableResolver.cs +++ b/src/WixToolset.Core/WixVariableResolver.cs | |||
| @@ -4,72 +4,82 @@ namespace WixToolset.Core | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Diagnostics.CodeAnalysis; | ||
| 8 | using System.Globalization; | ||
| 9 | using System.Text; | 7 | using System.Text; |
| 10 | using System.Text.RegularExpressions; | ||
| 11 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 9 | using WixToolset.Data.Bind; | ||
| 12 | using WixToolset.Extensibility.Services; | 10 | using WixToolset.Extensibility.Services; |
| 13 | 11 | ||
| 14 | /// <summary> | 12 | /// <summary> |
| 15 | /// WiX variable resolver. | 13 | /// WiX variable resolver. |
| 16 | /// </summary> | 14 | /// </summary> |
| 17 | internal sealed class WixVariableResolver : IBindVariableResolver | 15 | internal sealed class WixVariableResolver : IVariableResolver |
| 18 | { | 16 | { |
| 19 | private Dictionary<string, string> wixVariables; | 17 | private Dictionary<string, BindVariable> locVariables; |
| 18 | private Dictionary<string, BindVariable> wixVariables; | ||
| 19 | private Dictionary<string, LocalizedControl> localizedControls; | ||
| 20 | 20 | ||
| 21 | /// <summary> | 21 | /// <summary> |
| 22 | /// Instantiate a new WixVariableResolver. | 22 | /// Instantiate a new WixVariableResolver. |
| 23 | /// </summary> | 23 | /// </summary> |
| 24 | public WixVariableResolver(IMessaging messaging, Localizer localizer = null) | 24 | public WixVariableResolver(IMessaging messaging) |
| 25 | { | 25 | { |
| 26 | this.wixVariables = new Dictionary<string, string>(); | 26 | this.locVariables = new Dictionary<string, BindVariable>(); |
| 27 | this.wixVariables = new Dictionary<string, BindVariable>(); | ||
| 28 | this.Codepage = -1; | ||
| 27 | this.Messaging = messaging; | 29 | this.Messaging = messaging; |
| 28 | this.Localizer = localizer; | ||
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | private IMessaging Messaging { get; } | 32 | private IMessaging Messaging { get; } |
| 32 | 33 | ||
| 33 | private Localizer Localizer { get; } | 34 | public int Codepage { get; private set; } |
| 34 | 35 | ||
| 35 | /// <summary> | ||
| 36 | /// Gets the count of variables added to the resolver. | ||
| 37 | /// </summary> | ||
| 38 | public int VariableCount => this.wixVariables.Count; | 36 | public int VariableCount => this.wixVariables.Count; |
| 39 | 37 | ||
| 40 | /// <summary> | 38 | public void AddLocalization(Localization localization) |
| 41 | /// Add a variable. | ||
| 42 | /// </summary> | ||
| 43 | /// <param name="name">The name of the variable.</param> | ||
| 44 | /// <param name="value">The value of the variable.</param> | ||
| 45 | /// <param name="overridable">Indicates whether the variable can be overridden by an existing variable.</param> | ||
| 46 | public void AddVariable(string name, string value, bool overridable) | ||
| 47 | { | 39 | { |
| 48 | try | 40 | if (-1 == this.Codepage) |
| 49 | { | 41 | { |
| 50 | this.wixVariables.Add(name, value); | 42 | this.Codepage = localization.Codepage; |
| 51 | } | 43 | } |
| 52 | catch (ArgumentException) | 44 | |
| 45 | foreach (var variable in localization.Variables) | ||
| 53 | { | 46 | { |
| 54 | if (!overridable) | 47 | if (!TryAddWixVariable(this.locVariables, variable)) |
| 55 | { | 48 | { |
| 56 | throw; | 49 | this.Messaging.Write(ErrorMessages.DuplicateLocalizationIdentifier(variable.SourceLineNumbers, variable.Id)); |
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls) | ||
| 54 | { | ||
| 55 | if (!this.localizedControls.ContainsKey(localizedControl.Key)) | ||
| 56 | { | ||
| 57 | this.localizedControls.Add(localizedControl.Key, localizedControl.Value); | ||
| 57 | } | 58 | } |
| 58 | } | 59 | } |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | /// <summary> | 62 | public void AddVariable(SourceLineNumber sourceLineNumber, string name, string value, bool overridable) |
| 62 | /// Resolve the wix variables in a value. | 63 | { |
| 63 | /// </summary> | 64 | var bindVariable = new BindVariable { Id = name, Value = value, Overridable = overridable, SourceLineNumbers = sourceLineNumber }; |
| 64 | /// <param name="sourceLineNumbers">The source line information for the value.</param> | 65 | |
| 65 | /// <param name="value">The value to resolve.</param> | 66 | if (!TryAddWixVariable(this.wixVariables, bindVariable)) |
| 66 | /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param> | 67 | { |
| 67 | /// <returns>The resolved value.</returns> | 68 | this.Messaging.Write(ErrorMessages.WixVariableCollision(sourceLineNumber, name)); |
| 68 | public BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly) | 69 | } |
| 70 | } | ||
| 71 | |||
| 72 | public VariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly) | ||
| 69 | { | 73 | { |
| 70 | return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true); | 74 | return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true); |
| 71 | } | 75 | } |
| 72 | 76 | ||
| 77 | public bool TryGetLocalizedControl(string dialog, string control, out LocalizedControl localizedControl) | ||
| 78 | { | ||
| 79 | var key = LocalizedControl.GetKey(dialog, control); | ||
| 80 | return this.localizedControls.TryGetValue(key, out localizedControl); | ||
| 81 | } | ||
| 82 | |||
| 73 | /// <summary> | 83 | /// <summary> |
| 74 | /// Resolve the wix variables in a value. | 84 | /// Resolve the wix variables in a value. |
| 75 | /// </summary> | 85 | /// </summary> |
| @@ -80,23 +90,23 @@ namespace WixToolset.Core | |||
| 80 | /// <param name="isDefault">true if the resolved value was the default.</param> | 90 | /// <param name="isDefault">true if the resolved value was the default.</param> |
| 81 | /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param> | 91 | /// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param> |
| 82 | /// <returns>The resolved value.</returns> | 92 | /// <returns>The resolved value.</returns> |
| 83 | internal BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown) | 93 | internal VariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown) |
| 84 | { | 94 | { |
| 85 | MatchCollection matches = Common.WixVariableRegex.Matches(value); | 95 | var matches = Common.WixVariableRegex.Matches(value); |
| 86 | 96 | ||
| 87 | // the value is the default unless its substituted further down | 97 | // the value is the default unless its substituted further down |
| 88 | var result = new BindVariableResolution { IsDefault = true, Value = value }; | 98 | var result = new VariableResolution { IsDefault = true, Value = value }; |
| 89 | 99 | ||
| 90 | if (0 < matches.Count) | 100 | if (0 < matches.Count) |
| 91 | { | 101 | { |
| 92 | StringBuilder sb = new StringBuilder(value); | 102 | var sb = new StringBuilder(value); |
| 93 | 103 | ||
| 94 | // notice how this code walks backward through the list | 104 | // notice how this code walks backward through the list |
| 95 | // because it modifies the string as we through it | 105 | // because it modifies the string as we through it |
| 96 | for (int i = matches.Count - 1; 0 <= i; i--) | 106 | for (int i = matches.Count - 1; 0 <= i; i--) |
| 97 | { | 107 | { |
| 98 | string variableNamespace = matches[i].Groups["namespace"].Value; | 108 | var variableNamespace = matches[i].Groups["namespace"].Value; |
| 99 | string variableId = matches[i].Groups["fullname"].Value; | 109 | var variableId = matches[i].Groups["fullname"].Value; |
| 100 | string variableDefaultValue = null; | 110 | string variableDefaultValue = null; |
| 101 | 111 | ||
| 102 | // get the default value if one was specified | 112 | // get the default value if one was specified |
| @@ -142,7 +152,10 @@ namespace WixToolset.Core | |||
| 142 | this.Messaging.Write(WarningMessages.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId)); | 152 | this.Messaging.Write(WarningMessages.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId)); |
| 143 | } | 153 | } |
| 144 | 154 | ||
| 145 | resolvedValue = this.Localizer?.GetLocalizedValue(variableId); | 155 | if (this.locVariables.TryGetValue(variableId, out var bindVariable)) |
| 156 | { | ||
| 157 | resolvedValue = bindVariable.Value; | ||
| 158 | } | ||
| 146 | } | 159 | } |
| 147 | else if (!localizationOnly && "wix" == variableNamespace) | 160 | else if (!localizationOnly && "wix" == variableNamespace) |
| 148 | { | 161 | { |
| @@ -153,9 +166,9 @@ namespace WixToolset.Core | |||
| 153 | } | 166 | } |
| 154 | else | 167 | else |
| 155 | { | 168 | { |
| 156 | if (this.wixVariables.TryGetValue(variableId, out resolvedValue)) | 169 | if (this.wixVariables.TryGetValue(variableId, out var bindVariable)) |
| 157 | { | 170 | { |
| 158 | resolvedValue = resolvedValue ?? String.Empty; | 171 | resolvedValue = bindVariable.Value ?? String.Empty; |
| 159 | result.IsDefault = false; | 172 | result.IsDefault = false; |
| 160 | } | 173 | } |
| 161 | else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified | 174 | else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified |
| @@ -198,96 +211,15 @@ namespace WixToolset.Core | |||
| 198 | return result; | 211 | return result; |
| 199 | } | 212 | } |
| 200 | 213 | ||
| 201 | /// <summary> | 214 | private static bool TryAddWixVariable(IDictionary<string, BindVariable> variables, BindVariable variable) |
| 202 | /// Try to find localization information for dialog and (optional) control. | ||
| 203 | /// </summary> | ||
| 204 | /// <param name="dialog">Dialog identifier.</param> | ||
| 205 | /// <param name="control">Optional control identifier.</param> | ||
| 206 | /// <param name="localizedControl">Found localization information.</param> | ||
| 207 | /// <returns>True if localized control was found, otherwise false.</returns> | ||
| 208 | public bool TryGetLocalizedControl(string dialog, string control, out LocalizedControl localizedControl) | ||
| 209 | { | ||
| 210 | localizedControl = this.Localizer?.GetLocalizedControl(dialog, control); | ||
| 211 | return localizedControl != null; | ||
| 212 | } | ||
| 213 | |||
| 214 | /// <summary> | ||
| 215 | /// Resolve the delay variables in a value. | ||
| 216 | /// </summary> | ||
| 217 | /// <param name="sourceLineNumbers">The source line information for the value.</param> | ||
| 218 | /// <param name="value">The value to resolve.</param> | ||
| 219 | /// <param name="resolutionData"></param> | ||
| 220 | /// <returns>The resolved value.</returns> | ||
| 221 | [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sourceLineNumbers")] | ||
| 222 | [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "This string is not round tripped, and not used for any security decisions")] | ||
| 223 | public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData) | ||
| 224 | { | 215 | { |
| 225 | MatchCollection matches = Common.WixVariableRegex.Matches(value); | 216 | if (!variables.TryGetValue(variable.Id, out var existingWixVariableRow) || (existingWixVariableRow.Overridable && !variable.Overridable)) |
| 226 | |||
| 227 | if (0 < matches.Count) | ||
| 228 | { | 217 | { |
| 229 | StringBuilder sb = new StringBuilder(value); | 218 | variables[variable.Id] = variable; |
| 230 | 219 | return true; | |
| 231 | // notice how this code walks backward through the list | ||
| 232 | // because it modifies the string as we go through it | ||
| 233 | for (int i = matches.Count - 1; 0 <= i; i--) | ||
| 234 | { | ||
| 235 | string variableNamespace = matches[i].Groups["namespace"].Value; | ||
| 236 | string variableId = matches[i].Groups["fullname"].Value; | ||
| 237 | string variableDefaultValue = null; | ||
| 238 | string variableScope = null; | ||
| 239 | |||
| 240 | // get the default value if one was specified | ||
| 241 | if (matches[i].Groups["value"].Success) | ||
| 242 | { | ||
| 243 | variableDefaultValue = matches[i].Groups["value"].Value; | ||
| 244 | } | ||
| 245 | |||
| 246 | // get the scope if one was specified | ||
| 247 | if (matches[i].Groups["scope"].Success) | ||
| 248 | { | ||
| 249 | variableScope = matches[i].Groups["scope"].Value; | ||
| 250 | if ("bind" == variableNamespace) | ||
| 251 | { | ||
| 252 | variableId = matches[i].Groups["name"].Value; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | // check for an escape sequence of !! indicating the match is not a variable expression | ||
| 257 | if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1]) | ||
| 258 | { | ||
| 259 | sb.Remove(matches[i].Index - 1, 1); | ||
| 260 | } | ||
| 261 | else | ||
| 262 | { | ||
| 263 | string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", variableId, variableScope).ToLower(CultureInfo.InvariantCulture); | ||
| 264 | string resolvedValue = variableDefaultValue; | ||
| 265 | |||
| 266 | if (resolutionData.ContainsKey(key)) | ||
| 267 | { | ||
| 268 | resolvedValue = resolutionData[key]; | ||
| 269 | } | ||
| 270 | |||
| 271 | if ("bind" == variableNamespace) | ||
| 272 | { | ||
| 273 | // insert the resolved value if it was found or display an error | ||
| 274 | if (null != resolvedValue) | ||
| 275 | { | ||
| 276 | sb.Remove(matches[i].Index, matches[i].Length); | ||
| 277 | sb.Insert(matches[i].Index, resolvedValue); | ||
| 278 | } | ||
| 279 | else | ||
| 280 | { | ||
| 281 | throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value)); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | value = sb.ToString(); | ||
| 288 | } | 220 | } |
| 289 | 221 | ||
| 290 | return value; | 222 | return variable.Overridable; |
| 291 | } | 223 | } |
| 292 | } | 224 | } |
| 293 | } | 225 | } |
