aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Compiler.cs')
-rw-r--r--src/WixToolset.Core/Compiler.cs168
1 files changed, 86 insertions, 82 deletions
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())