diff options
author | Rob Mensching <rob@firegiant.com> | 2020-07-10 23:41:25 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2020-07-14 10:53:45 -0700 |
commit | f4b7d3f7c0a49e70fe5d25a309387786589488a6 (patch) | |
tree | 74162de1ece12ecd88c7c429f8f0ed155ca6b653 | |
parent | 28f41d1afec40d79459402fbed21f6c237768adb (diff) | |
download | wix-f4b7d3f7c0a49e70fe5d25a309387786589488a6.tar.gz wix-f4b7d3f7c0a49e70fe5d25a309387786589488a6.tar.bz2 wix-f4b7d3f7c0a49e70fe5d25a309387786589488a6.zip |
Major performance improvements to inline directory syntax parsing
-rw-r--r-- | src/WixToolset.Core/Common.cs | 21 | ||||
-rw-r--r-- | src/WixToolset.Core/Compiler.cs | 168 | ||||
-rw-r--r-- | src/WixToolset.Core/CompilerCore.cs | 37 | ||||
-rw-r--r-- | src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | 216 |
4 files changed, 220 insertions, 222 deletions
diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs index 19c77368..7a321d29 100644 --- a/src/WixToolset.Core/Common.cs +++ b/src/WixToolset.Core/Common.cs | |||
@@ -329,21 +329,22 @@ namespace WixToolset.Core | |||
329 | /// <returns>The generated identifier.</returns> | 329 | /// <returns>The generated identifier.</returns> |
330 | public static string GenerateIdentifier(string prefix, params string[] args) | 330 | public static string GenerateIdentifier(string prefix, params string[] args) |
331 | { | 331 | { |
332 | string stringData = String.Join("|", args); | 332 | string base64; |
333 | byte[] data = Encoding.UTF8.GetBytes(stringData); | ||
334 | 333 | ||
335 | // hash the data | 334 | using (var sha1 = new SHA1CryptoServiceProvider()) |
336 | byte[] hash; | ||
337 | using (SHA1 sha1 = new SHA1CryptoServiceProvider()) | ||
338 | { | 335 | { |
339 | hash = sha1.ComputeHash(data); | 336 | var combined = String.Join("|", args); |
337 | var data = Encoding.UTF8.GetBytes(combined); | ||
338 | var hash = sha1.ComputeHash(data); | ||
339 | base64 = Convert.ToBase64String(hash); | ||
340 | } | 340 | } |
341 | 341 | ||
342 | // Build up the identifier. | 342 | var identifier = new StringBuilder(32); |
343 | StringBuilder identifier = new StringBuilder(35, 35); | ||
344 | identifier.Append(prefix); | 343 | identifier.Append(prefix); |
345 | identifier.Append(Convert.ToBase64String(hash).TrimEnd('=')); | 344 | identifier.Append(base64); |
346 | identifier.Replace('+', '.').Replace('/', '_'); | 345 | identifier.Length -= 1; // removes the trailing '=' from base64 |
346 | identifier.Replace('+', '.'); | ||
347 | identifier.Replace('/', '_'); | ||
347 | 348 | ||
348 | return identifier.ToString(); | 349 | return identifier.ToString(); |
349 | } | 350 | } |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 3fa06f9c..c504e96f 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
@@ -9,6 +9,7 @@ namespace WixToolset.Core | |||
9 | using System.Globalization; | 9 | using System.Globalization; |
10 | using System.IO; | 10 | using System.IO; |
11 | using System.Linq; | 11 | using System.Linq; |
12 | using System.Text; | ||
12 | using System.Xml.Linq; | 13 | using System.Xml.Linq; |
13 | using WixToolset.Data; | 14 | using WixToolset.Data; |
14 | using WixToolset.Data.Symbols; | 15 | using WixToolset.Data.Symbols; |
@@ -25,8 +26,10 @@ namespace WixToolset.Core | |||
25 | private const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB | 26 | private const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB |
26 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | 27 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) |
27 | 28 | ||
28 | private const string DefaultComponentIdPlaceholderPrefix = "WixComponentIdPlaceholder"; | 29 | private const char ComponentIdPlaceholderStart = (char)167; |
29 | private const string DefaultComponentIdPlaceholderWixVariablePrefix = "!(wix."; | 30 | private const char ComponentIdPlaceholderEnd = (char)167; |
31 | private Dictionary<string, string> componentIdPlaceholders; | ||
32 | |||
30 | // If these are true you know you are building a module or product | 33 | // If these are true you know you are building a module or product |
31 | // but if they are false you cannot not be sure they will not end | 34 | // but if they are false you cannot not be sure they will not end |
32 | // up a product or module. Use these flags carefully. | 35 | // up a product or module. Use these flags carefully. |
@@ -36,9 +39,6 @@ namespace WixToolset.Core | |||
36 | private string activeName; | 39 | private string activeName; |
37 | private string activeLanguage; | 40 | private string activeLanguage; |
38 | 41 | ||
39 | // TODO: Implement this differently to not require the VariableResolver. | ||
40 | private VariableResolver componentIdPlaceholdersResolver; | ||
41 | |||
42 | /// <summary> | 42 | /// <summary> |
43 | /// Type of RadioButton element in a group. | 43 | /// Type of RadioButton element in a group. |
44 | /// </summary> | 44 | /// </summary> |
@@ -129,7 +129,7 @@ namespace WixToolset.Core | |||
129 | 129 | ||
130 | this.Core = new CompilerCore(target, this.Messaging, parseHelper, extensionsByNamespace); | 130 | this.Core = new CompilerCore(target, this.Messaging, parseHelper, extensionsByNamespace); |
131 | this.Core.ShowPedanticMessages = this.ShowPedanticMessages; | 131 | this.Core.ShowPedanticMessages = this.ShowPedanticMessages; |
132 | this.componentIdPlaceholdersResolver = new VariableResolver(this.ServiceProvider); | 132 | this.componentIdPlaceholders = new Dictionary<string, string>(); |
133 | 133 | ||
134 | // parse the document | 134 | // parse the document |
135 | var source = this.Context.Source; | 135 | var source = this.Context.Source; |
@@ -247,7 +247,7 @@ namespace WixToolset.Core | |||
247 | 247 | ||
248 | private void ResolveComponentIdPlaceholders(Intermediate target) | 248 | private void ResolveComponentIdPlaceholders(Intermediate target) |
249 | { | 249 | { |
250 | if (0 < this.componentIdPlaceholdersResolver.VariableCount) | 250 | if (0 < this.componentIdPlaceholders.Count) |
251 | { | 251 | { |
252 | foreach (var section in target.Sections) | 252 | foreach (var section in target.Sections) |
253 | { | 253 | { |
@@ -255,15 +255,40 @@ namespace WixToolset.Core | |||
255 | { | 255 | { |
256 | foreach (var field in symbol.Fields) | 256 | foreach (var field in symbol.Fields) |
257 | { | 257 | { |
258 | if (field?.Type == IntermediateFieldType.String) | 258 | if (field != null && field.Type == IntermediateFieldType.String) |
259 | { | 259 | { |
260 | var data = field.AsString(); | 260 | var data = field.AsString(); |
261 | if (!String.IsNullOrEmpty(data)) | 261 | if (!String.IsNullOrEmpty(data)) |
262 | { | 262 | { |
263 | var resolved = this.componentIdPlaceholdersResolver.ResolveVariables(symbol.SourceLineNumbers, data, errorOnUnknown: false); | 263 | var changed = false; |
264 | if (resolved.UpdatedValue) | 264 | var start = data.IndexOf(ComponentIdPlaceholderStart); |
265 | while (start != -1) | ||
266 | { | ||
267 | var end = data.IndexOf(ComponentIdPlaceholderEnd, start + 1); | ||
268 | if (end == -1) | ||
269 | { | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | var placeholderId = data.Substring(start, end - start + 1); | ||
274 | if (this.componentIdPlaceholders.TryGetValue(placeholderId, out var value)) | ||
275 | { | ||
276 | var sb = new StringBuilder(data); | ||
277 | sb.Remove(start, end - start + 1); | ||
278 | sb.Insert(start, value); | ||
279 | |||
280 | data = sb.ToString(); | ||
281 | changed = true; | ||
282 | |||
283 | end = start + value.Length; | ||
284 | } | ||
285 | |||
286 | start = data.IndexOf(ComponentIdPlaceholderStart, end); | ||
287 | } | ||
288 | |||
289 | if (changed) | ||
265 | { | 290 | { |
266 | field.Set(resolved.Value); | 291 | field.Overwrite(data); |
267 | } | 292 | } |
268 | } | 293 | } |
269 | } | 294 | } |
@@ -2096,9 +2121,8 @@ namespace WixToolset.Core | |||
2096 | var encounteredODBCDataSource = false; | 2121 | var encounteredODBCDataSource = false; |
2097 | var files = 0; | 2122 | var files = 0; |
2098 | var guid = "*"; | 2123 | var guid = "*"; |
2099 | var componentIdPlaceholder = Compiler.DefaultComponentIdPlaceholderPrefix + this.componentIdPlaceholdersResolver.VariableCount; // placeholder id for defaulting Component/@Id to keypath id. | 2124 | Identifier id = null; |
2100 | var componentIdPlaceholderWixVariable = Compiler.DefaultComponentIdPlaceholderWixVariablePrefix + componentIdPlaceholder + ")"; | 2125 | string componentIdPlaceholder = null; |
2101 | var id = new Identifier(AccessModifier.Private, componentIdPlaceholderWixVariable); | ||
2102 | var keyFound = false; | 2126 | var keyFound = false; |
2103 | string keyPath = null; | 2127 | string keyPath = null; |
2104 | 2128 | ||
@@ -2253,6 +2277,13 @@ namespace WixToolset.Core | |||
2253 | win64 = true; | 2277 | win64 = true; |
2254 | } | 2278 | } |
2255 | 2279 | ||
2280 | if (id == null) | ||
2281 | { | ||
2282 | // Placeholder id for defaulting Component/@Id to keypath id. | ||
2283 | componentIdPlaceholder = String.Concat(Compiler.ComponentIdPlaceholderStart, this.componentIdPlaceholders.Count, Compiler.ComponentIdPlaceholderEnd); | ||
2284 | id = new Identifier(AccessModifier.Private, componentIdPlaceholder); | ||
2285 | } | ||
2286 | |||
2256 | if (null == directoryId) | 2287 | if (null == directoryId) |
2257 | { | 2288 | { |
2258 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Directory")); | 2289 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Directory")); |
@@ -2466,14 +2497,14 @@ namespace WixToolset.Core | |||
2466 | this.Core.Write(ErrorMessages.ImplicitComponentKeyPath(sourceLineNumbers, id.Id)); | 2497 | this.Core.Write(ErrorMessages.ImplicitComponentKeyPath(sourceLineNumbers, id.Id)); |
2467 | } | 2498 | } |
2468 | 2499 | ||
2469 | // if there isn't an @Id attribute value, replace the placeholder with the id of the keypath. | 2500 | // If there isn't an @Id attribute value, replace the placeholder with the id of the keypath. |
2470 | // either an explicit KeyPath="yes" attribute must be specified or requirements for | 2501 | // either an explicit KeyPath="yes" attribute must be specified or requirements for |
2471 | // generatable guid must be met. | 2502 | // generatable guid must be met. |
2472 | if (componentIdPlaceholderWixVariable == id.Id) | 2503 | if (componentIdPlaceholder == id.Id) |
2473 | { | 2504 | { |
2474 | if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath)) | 2505 | if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath)) |
2475 | { | 2506 | { |
2476 | this.componentIdPlaceholdersResolver.AddVariable(sourceLineNumbers, componentIdPlaceholder, keyPath, false); | 2507 | this.componentIdPlaceholders.Add(componentIdPlaceholder, keyPath); |
2477 | 2508 | ||
2478 | id = new Identifier(AccessModifier.Private, keyPath); | 2509 | id = new Identifier(AccessModifier.Private, keyPath); |
2479 | } | 2510 | } |
@@ -2483,12 +2514,6 @@ namespace WixToolset.Core | |||
2483 | } | 2514 | } |
2484 | } | 2515 | } |
2485 | 2516 | ||
2486 | // If an id was not determined by now, we have to error. | ||
2487 | if (null == id) | ||
2488 | { | ||
2489 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
2490 | } | ||
2491 | |||
2492 | // finally add the Component table row | 2517 | // finally add the Component table row |
2493 | if (!this.Core.EncounteredError) | 2518 | if (!this.Core.EncounteredError) |
2494 | { | 2519 | { |
@@ -4120,9 +4145,9 @@ namespace WixToolset.Core | |||
4120 | Identifier id = null; | 4145 | Identifier id = null; |
4121 | string componentGuidGenerationSeed = null; | 4146 | string componentGuidGenerationSeed = null; |
4122 | var fileSourceAttribSet = false; | 4147 | var fileSourceAttribSet = false; |
4123 | var nameHasValue = false; | 4148 | XAttribute nameAttribute = null; |
4124 | var name = "."; // default to parent directory. | 4149 | var name = "."; // default to parent directory. |
4125 | string[] inlineSyntax = null; | 4150 | string inlineSyntax = null; |
4126 | string shortName = null; | 4151 | string shortName = null; |
4127 | string sourceName = null; | 4152 | string sourceName = null; |
4128 | string shortSourceName = null; | 4153 | string shortSourceName = null; |
@@ -4148,15 +4173,8 @@ namespace WixToolset.Core | |||
4148 | fileSourceAttribSet = true; | 4173 | fileSourceAttribSet = true; |
4149 | break; | 4174 | break; |
4150 | case "Name": | 4175 | case "Name": |
4151 | nameHasValue = true; | 4176 | name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); |
4152 | if (attrib.Value == ".") | 4177 | nameAttribute = attrib; |
4153 | { | ||
4154 | name = attrib.Value; | ||
4155 | } | ||
4156 | else | ||
4157 | { | ||
4158 | inlineSyntax = this.Core.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attrib); | ||
4159 | } | ||
4160 | break; | 4178 | break; |
4161 | case "ShortName": | 4179 | case "ShortName": |
4162 | shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); | 4180 | shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); |
@@ -4185,37 +4203,7 @@ namespace WixToolset.Core | |||
4185 | } | 4203 | } |
4186 | } | 4204 | } |
4187 | 4205 | ||
4188 | // Create the directory rows for the inline. | 4206 | if (nameAttribute == null) |
4189 | if (null != inlineSyntax) | ||
4190 | { | ||
4191 | // Special case the single entry in the inline syntax since it is the most common case | ||
4192 | // and needs no extra processing. It's just the name of the directory. | ||
4193 | if (1 == inlineSyntax.Length) | ||
4194 | { | ||
4195 | name = inlineSyntax[0]; | ||
4196 | } | ||
4197 | else | ||
4198 | { | ||
4199 | var pathStartsAt = 0; | ||
4200 | if (inlineSyntax[0].EndsWith(":")) | ||
4201 | { | ||
4202 | parentId = inlineSyntax[0].TrimEnd(':'); | ||
4203 | this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, parentId); | ||
4204 | |||
4205 | pathStartsAt = 1; | ||
4206 | } | ||
4207 | |||
4208 | for (var i = pathStartsAt; i < inlineSyntax.Length - 1; ++i) | ||
4209 | { | ||
4210 | var inlineId = this.Core.CreateDirectoryRow(sourceLineNumbers, null, parentId, inlineSyntax[i]); | ||
4211 | parentId = inlineId.Id; | ||
4212 | } | ||
4213 | |||
4214 | name = inlineSyntax[inlineSyntax.Length - 1]; | ||
4215 | } | ||
4216 | } | ||
4217 | |||
4218 | if (!nameHasValue) | ||
4219 | { | 4207 | { |
4220 | if (!String.IsNullOrEmpty(shortName)) | 4208 | if (!String.IsNullOrEmpty(shortName)) |
4221 | { | 4209 | { |
@@ -4264,39 +4252,55 @@ namespace WixToolset.Core | |||
4264 | } | 4252 | } |
4265 | } | 4253 | } |
4266 | 4254 | ||
4267 | // Update the file source path appropriately. | 4255 | // Create the directory rows for the inline. |
4268 | if (fileSourceAttribSet) | 4256 | if (nameAttribute != null) |
4269 | { | 4257 | { |
4270 | if (!fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) | 4258 | var lastSlash = name.LastIndexOf('\\'); |
4259 | if (lastSlash > 0) | ||
4271 | { | 4260 | { |
4272 | fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar); | 4261 | inlineSyntax = name; |
4262 | name = inlineSyntax.Substring(lastSlash + 1); | ||
4263 | |||
4264 | parentId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, nameAttribute, parentId, inlineSyntax.Substring(0, lastSlash)); | ||
4265 | |||
4266 | if (!this.Core.IsValidLongFilename(name, false, false)) | ||
4267 | { | ||
4268 | this.Messaging.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, nameAttribute.Name.LocalName, nameAttribute.Value, name)); | ||
4269 | } | ||
4273 | } | 4270 | } |
4274 | } | 4271 | } |
4275 | else // add the appropriate part of this directory element to the file source. | 4272 | |
4273 | if (null == id) | ||
4276 | { | 4274 | { |
4277 | string append = String.IsNullOrEmpty(sourceName) ? name : sourceName; | 4275 | id = this.Core.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); |
4278 | 4276 | ||
4279 | if (!String.IsNullOrEmpty(append)) | 4277 | if (!String.IsNullOrEmpty(inlineSyntax)) |
4280 | { | 4278 | { |
4281 | fileSource = String.Concat(fileSource, append, Path.DirectorySeparatorChar); | 4279 | this.Core.AddInlineDirectoryId(inlineSyntax, id.Id); |
4282 | } | 4280 | } |
4283 | } | 4281 | } |
4284 | 4282 | ||
4285 | if (null == id) | 4283 | if ("TARGETDIR".Equals(id.Id, StringComparison.Ordinal) && !("SourceDir".Equals(name, StringComparison.Ordinal) && shortName == null && shortSourceName == null && sourceName == null)) |
4286 | { | 4284 | { |
4287 | id = this.Core.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); | 4285 | this.Core.Write(ErrorMessages.IllegalTargetDirDefaultDir(sourceLineNumbers, name)); |
4288 | } | 4286 | } |
4289 | 4287 | ||
4290 | // Calculate the DefaultDir for the directory row. | 4288 | // Update the file source path appropriately. |
4291 | var defaultDir = String.IsNullOrEmpty(shortName) ? name : String.Concat(shortName, "|", name); | 4289 | if (fileSourceAttribSet) |
4292 | if (!String.IsNullOrEmpty(sourceName)) | ||
4293 | { | 4290 | { |
4294 | defaultDir = String.Concat(defaultDir, ":", String.IsNullOrEmpty(shortSourceName) ? sourceName : String.Concat(shortSourceName, "|", sourceName)); | 4291 | if (!fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) |
4292 | { | ||
4293 | fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar); | ||
4294 | } | ||
4295 | } | 4295 | } |
4296 | 4296 | else // add the appropriate part of this directory element to the file source. | |
4297 | if ("TARGETDIR".Equals(id.Id, StringComparison.Ordinal) && !"SourceDir".Equals(defaultDir, StringComparison.Ordinal)) | ||
4298 | { | 4297 | { |
4299 | this.Core.Write(ErrorMessages.IllegalTargetDirDefaultDir(sourceLineNumbers, defaultDir)); | 4298 | string append = String.IsNullOrEmpty(sourceName) ? name : sourceName; |
4299 | |||
4300 | if (!String.IsNullOrEmpty(append)) | ||
4301 | { | ||
4302 | fileSource = String.Concat(fileSource, append, Path.DirectorySeparatorChar); | ||
4303 | } | ||
4300 | } | 4304 | } |
4301 | 4305 | ||
4302 | foreach (var child in node.Elements()) | 4306 | foreach (var child in node.Elements()) |
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs index d88858ae..e2c1a3d6 100644 --- a/src/WixToolset.Core/CompilerCore.cs +++ b/src/WixToolset.Core/CompilerCore.cs | |||
@@ -120,7 +120,7 @@ namespace WixToolset.Core | |||
120 | private readonly IParseHelper parseHelper; | 120 | private readonly IParseHelper parseHelper; |
121 | private readonly Intermediate intermediate; | 121 | private readonly Intermediate intermediate; |
122 | private readonly IMessaging messaging; | 122 | private readonly IMessaging messaging; |
123 | private HashSet<string> activeSectionInlinedDirectoryIds; | 123 | private Dictionary<string, string> activeSectionCachedInlinedDirectoryIds; |
124 | private HashSet<string> activeSectionSimpleReferences; | 124 | private HashSet<string> activeSectionSimpleReferences; |
125 | 125 | ||
126 | /// <summary> | 126 | /// <summary> |
@@ -354,12 +354,13 @@ namespace WixToolset.Core | |||
354 | /// Creates directories using the inline directory syntax. | 354 | /// Creates directories using the inline directory syntax. |
355 | /// </summary> | 355 | /// </summary> |
356 | /// <param name="sourceLineNumbers">Source line information.</param> | 356 | /// <param name="sourceLineNumbers">Source line information.</param> |
357 | /// <param name="attribute">The attribute to parse.</param> | 357 | /// <param name="attribute">Attribute containing the inline syntax.</param> |
358 | /// <param name="parentId">Optional identifier of parent directory.</param> | 358 | /// <param name="parentId">Optional identifier of parent directory.</param> |
359 | /// <param name="inlineSyntax">Optional inline syntax to override attribute's value.</param> | ||
359 | /// <returns>Identifier of the leaf directory created.</returns> | 360 | /// <returns>Identifier of the leaf directory created.</returns> |
360 | public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId) | 361 | public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId, string inlineSyntax = null) |
361 | { | 362 | { |
362 | return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, parentId, attribute, this.activeSectionInlinedDirectoryIds); | 363 | return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, attribute, parentId, inlineSyntax, this.activeSectionCachedInlinedDirectoryIds); |
363 | } | 364 | } |
364 | 365 | ||
365 | /// <summary> | 366 | /// <summary> |
@@ -1001,6 +1002,16 @@ namespace WixToolset.Core | |||
1001 | } | 1002 | } |
1002 | 1003 | ||
1003 | /// <summary> | 1004 | /// <summary> |
1005 | /// Adds inline directory syntax generated identifier. | ||
1006 | /// </summary> | ||
1007 | /// <param name="inlineSyntax">Inline directory syntax the identifier was generated.</param> | ||
1008 | /// <param name="id">Generated identifier for inline syntax.</param> | ||
1009 | internal void AddInlineDirectoryId(string inlineSyntax, string id) | ||
1010 | { | ||
1011 | this.activeSectionCachedInlinedDirectoryIds.Add(inlineSyntax, id); | ||
1012 | } | ||
1013 | |||
1014 | /// <summary> | ||
1004 | /// Creates a new section and makes it the active section in the core. | 1015 | /// Creates a new section and makes it the active section in the core. |
1005 | /// </summary> | 1016 | /// </summary> |
1006 | /// <param name="id">Unique identifier for the section.</param> | 1017 | /// <param name="id">Unique identifier for the section.</param> |
@@ -1011,7 +1022,7 @@ namespace WixToolset.Core | |||
1011 | { | 1022 | { |
1012 | this.ActiveSection = this.CreateSection(id, type, codepage, compilationId); | 1023 | this.ActiveSection = this.CreateSection(id, type, codepage, compilationId); |
1013 | 1024 | ||
1014 | this.activeSectionInlinedDirectoryIds = new HashSet<string>(); | 1025 | this.activeSectionCachedInlinedDirectoryIds = new Dictionary<string, string>(); |
1015 | this.activeSectionSimpleReferences = new HashSet<string>(); | 1026 | this.activeSectionSimpleReferences = new HashSet<string>(); |
1016 | 1027 | ||
1017 | return this.ActiveSection; | 1028 | return this.ActiveSection; |
@@ -1060,9 +1071,9 @@ namespace WixToolset.Core | |||
1060 | /// <param name="sourceName">Optional source name for the directory.</param> | 1071 | /// <param name="sourceName">Optional source name for the directory.</param> |
1061 | /// <param name="shortSourceName">Optional short source name for the directory.</param> | 1072 | /// <param name="shortSourceName">Optional short source name for the directory.</param> |
1062 | /// <returns>Identifier for the newly created row.</returns> | 1073 | /// <returns>Identifier for the newly created row.</returns> |
1063 | internal Identifier CreateDirectoryRow(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) | 1074 | internal Identifier CreateDirectorySymbol(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) |
1064 | { | 1075 | { |
1065 | return this.parseHelper.CreateDirectorySymbol(this.ActiveSection, sourceLineNumbers, id, parentId, name, this.activeSectionInlinedDirectoryIds, shortName, sourceName, shortSourceName); | 1076 | return this.parseHelper.CreateDirectorySymbol(this.ActiveSection, sourceLineNumbers, id, parentId, name, shortName, sourceName, shortSourceName); |
1066 | } | 1077 | } |
1067 | 1078 | ||
1068 | public void CreateWixSearchSymbol(SourceLineNumber sourceLineNumbers, string elementName, Identifier id, string variable, string condition, string after) | 1079 | public void CreateWixSearchSymbol(SourceLineNumber sourceLineNumbers, string elementName, Identifier id, string variable, string condition, string after) |
@@ -1070,18 +1081,6 @@ namespace WixToolset.Core | |||
1070 | this.parseHelper.CreateWixSearchSymbol(this.ActiveSection, sourceLineNumbers, elementName, id, variable, condition, after, null); | 1081 | this.parseHelper.CreateWixSearchSymbol(this.ActiveSection, sourceLineNumbers, elementName, id, variable, condition, after, null); |
1071 | } | 1082 | } |
1072 | 1083 | ||
1073 | /// <summary> | ||
1074 | /// Gets the attribute value as inline directory syntax. | ||
1075 | /// </summary> | ||
1076 | /// <param name="sourceLineNumbers">Source line information.</param> | ||
1077 | /// <param name="attribute">Attribute containing the value to get.</param> | ||
1078 | /// <param name="resultUsedToCreateReference">Flag indicates whether the inline directory syntax should be processed to create a directory row or to create a directory reference.</param> | ||
1079 | /// <returns>Inline directory syntax split into array of strings or null if the syntax did not parse.</returns> | ||
1080 | internal string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false) | ||
1081 | { | ||
1082 | return this.parseHelper.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, resultUsedToCreateReference); | ||
1083 | } | ||
1084 | |||
1085 | internal WixActionSymbol ScheduleActionSymbol(SourceLineNumber sourceLineNumbers, AccessModifier access, SequenceTable sequence, string actionName, string condition = null, string beforeAction = null, string afterAction = null, bool overridable = false) | 1084 | internal WixActionSymbol ScheduleActionSymbol(SourceLineNumber sourceLineNumbers, AccessModifier access, SequenceTable sequence, string actionName, string condition = null, string beforeAction = null, string afterAction = null, bool overridable = false) |
1086 | { | 1085 | { |
1087 | return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable); | 1086 | return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable); |
diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs index d9087ce3..e75f5fe5 100644 --- a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs +++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | |||
@@ -19,6 +19,8 @@ namespace WixToolset.Core.ExtensibilityServices | |||
19 | 19 | ||
20 | internal class ParseHelper : IParseHelper | 20 | internal class ParseHelper : IParseHelper |
21 | { | 21 | { |
22 | private static readonly char[] InlineDirectorySeparators = new char[] { ':', '\\', '/' }; | ||
23 | |||
22 | public ParseHelper(IWixToolsetServiceProvider serviceProvider) | 24 | public ParseHelper(IWixToolsetServiceProvider serviceProvider) |
23 | { | 25 | { |
24 | this.ServiceProvider = serviceProvider; | 26 | this.ServiceProvider = serviceProvider; |
@@ -53,13 +55,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
53 | this.CreateWixGroupSymbol(section, sourceLineNumbers, parentType, parentId, childType, childId); | 55 | this.CreateWixGroupSymbol(section, sourceLineNumbers, parentType, parentId, childType, childId); |
54 | } | 56 | } |
55 | 57 | ||
56 | [Obsolete] | 58 | public Identifier CreateDirectorySymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) |
57 | public Identifier CreateDirectoryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, ISet<string> sectionInlinedDirectoryIds, string shortName = null, string sourceName = null, string shortSourceName = null) | ||
58 | { | ||
59 | return this.CreateDirectorySymbol(section, sourceLineNumbers, id, parentId, name, sectionInlinedDirectoryIds, shortName, sourceName, shortSourceName); | ||
60 | } | ||
61 | |||
62 | public Identifier CreateDirectorySymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, ISet<string> sectionInlinedDirectoryIds, string shortName = null, string sourceName = null, string shortSourceName = null) | ||
63 | { | 59 | { |
64 | // For anonymous directories, create the identifier. If this identifier already exists in the | 60 | // For anonymous directories, create the identifier. If this identifier already exists in the |
65 | // active section, bail so we don't add duplicate anonymous directory symbols (which are legal | 61 | // active section, bail so we don't add duplicate anonymous directory symbols (which are legal |
@@ -67,11 +63,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
67 | if (null == id) | 63 | if (null == id) |
68 | { | 64 | { |
69 | id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); | 65 | id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); |
70 | |||
71 | if (!sectionInlinedDirectoryIds.Add(id.Id)) | ||
72 | { | ||
73 | return id; | ||
74 | } | ||
75 | } | 66 | } |
76 | 67 | ||
77 | var symbol = section.AddSymbol(new DirectorySymbol(sourceLineNumbers, id) | 68 | var symbol = section.AddSymbol(new DirectorySymbol(sourceLineNumbers, id) |
@@ -86,48 +77,30 @@ namespace WixToolset.Core.ExtensibilityServices | |||
86 | return symbol.Id; | 77 | return symbol.Id; |
87 | } | 78 | } |
88 | 79 | ||
89 | public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, string parentId, XAttribute attribute, ISet<string> sectionInlinedDirectoryIds) | 80 | public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId, string inlineSyntax, IDictionary<string, string> sectionCachedInlinedDirectoryIds) |
90 | { | 81 | { |
91 | string id = null; | 82 | if (String.IsNullOrEmpty(inlineSyntax)) |
92 | var inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true); | ||
93 | |||
94 | if (null != inlineSyntax) | ||
95 | { | 83 | { |
96 | // Special case the single entry in the inline syntax since it is the most common case | 84 | inlineSyntax = attribute.Value; |
97 | // and needs no extra processing. It's just a reference to an existing directory. | 85 | } |
98 | if (1 == inlineSyntax.Length) | ||
99 | { | ||
100 | id = inlineSyntax[0]; | ||
101 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, id); | ||
102 | } | ||
103 | else // start creating symbols for the entries in the inline syntax | ||
104 | { | ||
105 | id = parentId; | ||
106 | |||
107 | var pathStartsAt = 0; | ||
108 | if (inlineSyntax[0].EndsWith(":", StringComparison.Ordinal)) | ||
109 | { | ||
110 | // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide? | ||
111 | //if (null != parentId) | ||
112 | //{ | ||
113 | // this.core.Write(WixErrors.Xxx(sourceLineNumbers)); | ||
114 | //} | ||
115 | |||
116 | id = inlineSyntax[0].TrimEnd(':'); | ||
117 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, id); | ||
118 | 86 | ||
119 | pathStartsAt = 1; | 87 | // If no separator is found, the string is a simple reference. |
120 | } | 88 | var separatorFound = inlineSyntax.IndexOfAny(InlineDirectorySeparators); |
89 | if (separatorFound == -1) | ||
90 | { | ||
91 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, inlineSyntax); | ||
92 | return inlineSyntax; | ||
93 | } | ||
121 | 94 | ||
122 | for (var i = pathStartsAt; i < inlineSyntax.Length; ++i) | 95 | // If a parent id was provided and the inline syntax does not start with a directory reference, prepend the parent id. |
123 | { | 96 | if (!String.IsNullOrEmpty(parentId) && inlineSyntax[separatorFound] != ':') |
124 | var inlineId = this.CreateDirectorySymbol(section, sourceLineNumbers, null, id, inlineSyntax[i], sectionInlinedDirectoryIds); | 97 | { |
125 | id = inlineId.Id; | 98 | inlineSyntax = String.Concat(parentId, ":", inlineSyntax); |
126 | } | ||
127 | } | ||
128 | } | 99 | } |
129 | 100 | ||
130 | return id; | 101 | inlineSyntax = inlineSyntax.TrimEnd('\\', '/'); |
102 | |||
103 | return this.ParseInlineSyntax(section, sourceLineNumbers, attribute, inlineSyntax, sectionCachedInlinedDirectoryIds); | ||
131 | } | 104 | } |
132 | 105 | ||
133 | public string CreateGuid(Guid namespaceGuid, string value) | 106 | public string CreateGuid(Guid namespaceGuid, string value) |
@@ -544,69 +517,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
544 | return Common.GetAttributeIdentifierValue(this.Messaging, sourceLineNumbers, attribute); | 517 | return Common.GetAttributeIdentifierValue(this.Messaging, sourceLineNumbers, attribute); |
545 | } | 518 | } |
546 | 519 | ||
547 | public string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false) | ||
548 | { | ||
549 | string[] result = null; | ||
550 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
551 | |||
552 | if (!String.IsNullOrEmpty(value)) | ||
553 | { | ||
554 | var pathStartsAt = 0; | ||
555 | result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); | ||
556 | if (result[0].EndsWith(":", StringComparison.Ordinal)) | ||
557 | { | ||
558 | var id = result[0].TrimEnd(':'); | ||
559 | if (1 == result.Length) | ||
560 | { | ||
561 | this.Messaging.Write(ErrorMessages.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); | ||
562 | return null; | ||
563 | } | ||
564 | else if (!this.IsValidIdentifier(id)) | ||
565 | { | ||
566 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id)); | ||
567 | return null; | ||
568 | } | ||
569 | |||
570 | pathStartsAt = 1; | ||
571 | } | ||
572 | else if (resultUsedToCreateReference && 1 == result.Length) | ||
573 | { | ||
574 | if (value.EndsWith("\\", StringComparison.Ordinal)) | ||
575 | { | ||
576 | if (!this.IsValidLongFilename(result[0], false, false)) | ||
577 | { | ||
578 | this.Messaging.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); | ||
579 | return null; | ||
580 | } | ||
581 | } | ||
582 | else if (!this.IsValidIdentifier(result[0])) | ||
583 | { | ||
584 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0])); | ||
585 | return null; | ||
586 | } | ||
587 | |||
588 | return result; // return early to avoid additional checks below. | ||
589 | } | ||
590 | |||
591 | // Check each part of the relative path to ensure that it is a valid directory name. | ||
592 | for (var i = pathStartsAt; i < result.Length; ++i) | ||
593 | { | ||
594 | if (!this.IsValidLongFilename(result[i], false, false)) | ||
595 | { | ||
596 | this.Messaging.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i])); | ||
597 | return null; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | if (1 < result.Length && !value.EndsWith("\\", StringComparison.Ordinal)) | ||
602 | { | ||
603 | this.Messaging.Write(WarningMessages.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | return result; | ||
608 | } | ||
609 | |||
610 | public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) | 520 | public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) |
611 | { | 521 | { |
612 | return Common.GetAttributeIntegerValue(this.Messaging, sourceLineNumbers, attribute, minimum, maximum); | 522 | return Common.GetAttributeIntegerValue(this.Messaging, sourceLineNumbers, attribute, minimum, maximum); |
@@ -1032,6 +942,90 @@ namespace WixToolset.Core.ExtensibilityServices | |||
1032 | this.Creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>(); | 942 | this.Creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>(); |
1033 | } | 943 | } |
1034 | 944 | ||
945 | private string ParseInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string inlineSyntax, IDictionary<string, string> sectionCachedInlinedDirectoryIds) | ||
946 | { | ||
947 | if (!sectionCachedInlinedDirectoryIds.TryGetValue(inlineSyntax, out var id)) | ||
948 | { | ||
949 | string parentId; | ||
950 | int nameIndex; | ||
951 | |||
952 | var separatorIndex = inlineSyntax.LastIndexOfAny(InlineDirectorySeparators); | ||
953 | if (separatorIndex == -1) | ||
954 | { | ||
955 | nameIndex = 0; | ||
956 | parentId = "TARGETDIR"; | ||
957 | } | ||
958 | else if (inlineSyntax[separatorIndex] == '\\' || inlineSyntax[separatorIndex] == '/') | ||
959 | { | ||
960 | nameIndex = separatorIndex + 1; | ||
961 | |||
962 | if (separatorIndex == 0) | ||
963 | { | ||
964 | parentId = "TARGETDIR"; | ||
965 | } | ||
966 | else if (inlineSyntax[separatorIndex - 1] == ':') | ||
967 | { | ||
968 | parentId = this.ParseParentReference(section, sourceLineNumbers, attribute, inlineSyntax, separatorIndex - 1); | ||
969 | } | ||
970 | else | ||
971 | { | ||
972 | var parentInlineDirectory = inlineSyntax.Substring(0, separatorIndex); | ||
973 | parentId = this.ParseInlineSyntax(section, sourceLineNumbers, attribute, parentInlineDirectory.TrimEnd('\\', '/'), sectionCachedInlinedDirectoryIds); | ||
974 | } | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | nameIndex = separatorIndex + 1; | ||
979 | parentId = this.ParseParentReference(section, sourceLineNumbers, attribute, inlineSyntax, separatorIndex); | ||
980 | } | ||
981 | |||
982 | if (nameIndex == inlineSyntax.Length) | ||
983 | { | ||
984 | id = parentId; | ||
985 | } | ||
986 | else | ||
987 | { | ||
988 | var name = nameIndex != -1 ? inlineSyntax.Substring(nameIndex) : null; | ||
989 | |||
990 | if (!this.IsValidLongFilename(name, false, false)) | ||
991 | { | ||
992 | this.Messaging.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, attribute.Value, name)); | ||
993 | return null; | ||
994 | } | ||
995 | |||
996 | var identifier = this.CreateDirectorySymbol(section, sourceLineNumbers, null, parentId, name); | ||
997 | |||
998 | id = identifier.Id; | ||
999 | } | ||
1000 | |||
1001 | sectionCachedInlinedDirectoryIds.Add(inlineSyntax, id); | ||
1002 | } | ||
1003 | |||
1004 | return id; | ||
1005 | } | ||
1006 | |||
1007 | private string ParseParentReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string reference, int colonIndex) | ||
1008 | { | ||
1009 | if (colonIndex == 0) | ||
1010 | { | ||
1011 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, attribute.Value, String.Empty)); | ||
1012 | return null; | ||
1013 | } | ||
1014 | else | ||
1015 | { | ||
1016 | var parentId = reference.Substring(0, colonIndex); | ||
1017 | |||
1018 | if (!Common.IsIdentifier(parentId)) | ||
1019 | { | ||
1020 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, attribute.Value, parentId)); | ||
1021 | return null; | ||
1022 | } | ||
1023 | |||
1024 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, parentId); | ||
1025 | return parentId; | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1035 | private static bool TryFindExtension(IEnumerable<ICompilerExtension> extensions, XNamespace ns, out ICompilerExtension extension) | 1029 | private static bool TryFindExtension(IEnumerable<ICompilerExtension> extensions, XNamespace ns, out ICompilerExtension extension) |
1036 | { | 1030 | { |
1037 | extension = null; | 1031 | extension = null; |