aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Arnson <bob@firegiant.com>2023-09-10 22:34:56 -0400
committerBob Arnson <github@bobs.org>2024-01-08 14:04:21 -0500
commitf676ceeaedbba4125d3e0c691959afcba182d008 (patch)
treecdf12f0b9c2be3b0eb0b1346d5e07ff6142432d3
parent44a13ff738267fc7e1147e46cd86df6cdbba7bb3 (diff)
downloadwix-f676ceeaedbba4125d3e0c691959afcba182d008.tar.gz
wix-f676ceeaedbba4125d3e0c691959afcba182d008.tar.bz2
wix-f676ceeaedbba4125d3e0c691959afcba182d008.zip
Support naked files.
Implements https://github.com/wixtoolset/issues/issues/7696. `File` elements can appear where `Component` elements do in WiX v4. The compiler generates an appropriate per-file component. Naked files under `Directory`, `DirectoryRef`, `Fragment`, `StandardDirectory`, or `Package` elements are included in a package via the [default-feature feature](https://github.com/wixtoolset/issues/issues/7581). Naked files appearing under `ComponentGroup`, `Feature`, `FeatureRef`, and `FeatureGroup` generate the component and the reference to the parent element. Components and naked Files default to being installed to INSTALLFOLDER (including a default INSTALLFOLDER if one isn't otherwise authored).
-rw-r--r--src/api/wix/WixToolset.Data/ErrorMessages.cs5
-rw-r--r--src/wix/WixToolset.Core/Compiler.cs354
-rw-r--r--src/wix/WixToolset.Core/Compiler_Module.cs3
-rw-r--r--src/wix/WixToolset.Core/Compiler_Package.cs3
-rw-r--r--src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs4
-rw-r--r--src/wix/WixToolset.Core/Link/SymbolWithSection.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/ComponentFixture.cs29
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/NakedFileFixture.cs247
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Component/MissingDirectoryWithSubdirectory.wxs10
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/BadAttributes.wxs11
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/ComponentGroup.wxs14
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Condition.wxs10
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Directory.wxs13
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/DirectoryRef.wxs17
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Feature.wxs10
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureGroup.wxs22
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureRef.wxs20
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Fragment.wxs24
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Module.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Package.wxs10
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithDefaultInstallFolder.wxs10
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithoutDefaultFeature.wxs17
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/StandardDirectory.wxs11
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroup.wxs8
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroupPackage.wxs5
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/test.txt1
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/SingleFile/PackageComponents.wxs5
27 files changed, 751 insertions, 120 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs
index e7c88613..79b835cd 100644
--- a/src/api/wix/WixToolset.Data/ErrorMessages.cs
+++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs
@@ -2256,6 +2256,11 @@ namespace WixToolset.Data
2256 return Message(sourceLineNumbers, Ids.IllegalInnerText, "The {0} element contains inner text which is obsolete. Use the {1} attribute instead.", elementName, attributeName); 2256 return Message(sourceLineNumbers, Ids.IllegalInnerText, "The {0} element contains inner text which is obsolete. Use the {1} attribute instead.", elementName, attributeName);
2257 } 2257 }
2258 2258
2259 public static Message IllegalAttributeWhenNested(SourceLineNumber sourceLineNumbers, string attributeName)
2260 {
2261 return Message(sourceLineNumbers, Ids.IllegalAttributeWhenNested, "The File element contains an attribute '{0}' that cannot be used in a File element that is a child of a Component element.", attributeName);
2262 }
2263
2259 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) 2264 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
2260 { 2265 {
2261 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); 2266 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
diff --git a/src/wix/WixToolset.Core/Compiler.cs b/src/wix/WixToolset.Core/Compiler.cs
index 7088cfba..bafe2c19 100644
--- a/src/wix/WixToolset.Core/Compiler.cs
+++ b/src/wix/WixToolset.Core/Compiler.cs
@@ -2252,9 +2252,11 @@ namespace WixToolset.Core
2252 2252
2253 if (String.IsNullOrEmpty(directoryId)) 2253 if (String.IsNullOrEmpty(directoryId))
2254 { 2254 {
2255 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Directory")); 2255 directoryId = "INSTALLFOLDER";
2256 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
2256 } 2257 }
2257 else if (!String.IsNullOrEmpty(subdirectory)) 2258
2259 if (!String.IsNullOrEmpty(subdirectory))
2258 { 2260 {
2259 directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, directoryId, subdirectory); 2261 directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, directoryId, subdirectory);
2260 } 2262 }
@@ -2427,6 +2429,7 @@ namespace WixToolset.Core
2427 keyBit = ComponentKeyPathType.File; 2429 keyBit = ComponentKeyPathType.File;
2428 keyPossible = possibleKeyPath.Id; 2430 keyPossible = possibleKeyPath.Id;
2429 break; 2431 break;
2432
2430 case PossibleKeyPathType.Directory: 2433 case PossibleKeyPathType.Directory:
2431 keyBit = ComponentKeyPathType.Directory; 2434 keyBit = ComponentKeyPathType.Directory;
2432 keyPossible = String.Empty; 2435 keyPossible = String.Empty;
@@ -2581,8 +2584,8 @@ namespace WixToolset.Core
2581 /// Parses a component group element. 2584 /// Parses a component group element.
2582 /// </summary> 2585 /// </summary>
2583 /// <param name="node">Element to parse.</param> 2586 /// <param name="node">Element to parse.</param>
2584 /// <param name="parentType"></param> 2587 /// <param name="parentType">Type of complex reference parent. Will be Unknown if there is no parent.</param>
2585 /// <param name="parentId"></param> 2588 /// <param name="parentId">Optional identifier for primary parent.</param>
2586 private void ParseComponentGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId) 2589 private void ParseComponentGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
2587 { 2590 {
2588 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 2591 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
@@ -2649,6 +2652,9 @@ namespace WixToolset.Core
2649 case "Component": 2652 case "Component":
2650 this.ParseComponentElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null, CompilerConstants.IntegerNotSet, directoryId, source); 2653 this.ParseComponentElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null, CompilerConstants.IntegerNotSet, directoryId, source);
2651 break; 2654 break;
2655 case "File":
2656 this.ParseNakedFileElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, directoryId, source);
2657 break;
2652 default: 2658 default:
2653 this.Core.UnexpectedElement(node, child); 2659 this.Core.UnexpectedElement(node, child);
2654 break; 2660 break;
@@ -3872,6 +3878,9 @@ namespace WixToolset.Core
3872 case "Directory": 3878 case "Directory":
3873 this.ParseDirectoryElement(child, id.Id, diskId, fileSource); 3879 this.ParseDirectoryElement(child, id.Id, diskId, fileSource);
3874 break; 3880 break;
3881 case "File":
3882 this.ParseNakedFileElement(child, ComplexReferenceParentType.Unknown, null, id.Id, fileSource);
3883 break;
3875 case "Merge": 3884 case "Merge":
3876 this.ParseMergeElement(child, id.Id, diskId); 3885 this.ParseMergeElement(child, id.Id, diskId);
3877 break; 3886 break;
@@ -3984,6 +3993,9 @@ namespace WixToolset.Core
3984 case "Directory": 3993 case "Directory":
3985 this.ParseDirectoryElement(child, id, diskId, fileSource); 3994 this.ParseDirectoryElement(child, id, diskId, fileSource);
3986 break; 3995 break;
3996 case "File":
3997 this.ParseNakedFileElement(child, ComplexReferenceParentType.Unknown, null, id, fileSource);
3998 break;
3987 case "Merge": 3999 case "Merge":
3988 this.ParseMergeElement(child, id, diskId); 4000 this.ParseMergeElement(child, id, diskId);
3989 break; 4001 break;
@@ -4421,6 +4433,9 @@ namespace WixToolset.Core
4421 case "FeatureRef": 4433 case "FeatureRef":
4422 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id.Id); 4434 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id.Id);
4423 break; 4435 break;
4436 case "File":
4437 this.ParseNakedFileElement(child, ComplexReferenceParentType.Feature, id.Id, null, null);
4438 break;
4424 case "Level": 4439 case "Level":
4425 this.ParseLevelElement(child, id.Id); 4440 this.ParseLevelElement(child, id.Id);
4426 break; 4441 break;
@@ -4561,6 +4576,9 @@ namespace WixToolset.Core
4561 case "FeatureRef": 4576 case "FeatureRef":
4562 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id); 4577 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id);
4563 break; 4578 break;
4579 case "File":
4580 this.ParseNakedFileElement(child, ComplexReferenceParentType.Feature, id, null, null);
4581 break;
4564 case "MergeRef": 4582 case "MergeRef":
4565 this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id); 4583 this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id);
4566 break; 4584 break;
@@ -4646,6 +4664,9 @@ namespace WixToolset.Core
4646 case "FeatureRef": 4664 case "FeatureRef":
4647 this.ParseFeatureRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id); 4665 this.ParseFeatureRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id);
4648 break; 4666 break;
4667 case "File":
4668 this.ParseNakedFileElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null, null);
4669 break;
4649 case "MergeRef": 4670 case "MergeRef":
4650 this.ParseMergeRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id); 4671 this.ParseMergeRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id);
4651 break; 4672 break;
@@ -5029,9 +5050,8 @@ namespace WixToolset.Core
5029 } 5050 }
5030 } 5051 }
5031 5052
5032
5033 /// <summary> 5053 /// <summary>
5034 /// Parses a file element. 5054 /// Parses a File element's attributes.
5035 /// </summary> 5055 /// </summary>
5036 /// <param name="node">File element to parse.</param> 5056 /// <param name="node">File element to parse.</param>
5037 /// <param name="componentId">Parent's component id.</param> 5057 /// <param name="componentId">Parent's component id.</param>
@@ -5039,10 +5059,12 @@ namespace WixToolset.Core
5039 /// <param name="diskId">Disk id inherited from parent component.</param> 5059 /// <param name="diskId">Disk id inherited from parent component.</param>
5040 /// <param name="sourcePath">Default source path of parent directory.</param> 5060 /// <param name="sourcePath">Default source path of parent directory.</param>
5041 /// <param name="possibleKeyPath">This will be set with the possible keyPath for the parent component.</param> 5061 /// <param name="possibleKeyPath">This will be set with the possible keyPath for the parent component.</param>
5042 /// <param name="win64Component">true if the component is 64-bit.</param> 5062 /// <param name="componentGuid">Component GUID (including `*`).</param>
5043 /// <param name="componentGuid"></param> 5063 /// <param name="isNakedFile">Whether the File element being parsed is outside a Component element.</param>
5064 /// <param name="fileSymbol">Outgoing file symbol containing parsed attributes.</param>
5065 /// <param name="assemblySymbol">Outgoing assembly symbol containing parsed attributes.</param>
5044 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns> 5066 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
5045 private YesNoType ParseFileElement(XElement node, string componentId, string directoryId, int diskId, string sourcePath, out string possibleKeyPath, bool win64Component, string componentGuid) 5067 private YesNoType ParseFileElementAttributes(XElement node, string componentId, string directoryId, int diskId, string sourcePath, out string possibleKeyPath, string componentGuid, bool isNakedFile, out FileSymbol fileSymbol, out AssemblySymbol assemblySymbol)
5046 { 5068 {
5047 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 5069 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5048 Identifier id = null; 5070 Identifier id = null;
@@ -5083,12 +5105,25 @@ namespace WixToolset.Core
5083 var source = sourcePath; // assume we'll use the parents as the source for this file 5105 var source = sourcePath; // assume we'll use the parents as the source for this file
5084 var sourceSet = false; 5106 var sourceSet = false;
5085 5107
5108 fileSymbol = null;
5109 assemblySymbol = null;
5110
5086 foreach (var attrib in node.Attributes()) 5111 foreach (var attrib in node.Attributes())
5087 { 5112 {
5088 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) 5113 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5089 { 5114 {
5090 switch (attrib.Name.LocalName) 5115 switch (attrib.Name.LocalName)
5091 { 5116 {
5117 case "Bitness":
5118 case "Condition":
5119 case "Directory":
5120 case "Subdirectory":
5121 // Naked files handle their attributes in ParseNakedFileElement.
5122 if (!isNakedFile)
5123 {
5124 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, attrib.Name.LocalName));
5125 }
5126 break;
5092 case "Id": 5127 case "Id":
5093 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); 5128 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
5094 break; 5129 break;
@@ -5329,70 +5364,6 @@ namespace WixToolset.Core
5329 } 5364 }
5330 } 5365 }
5331 5366
5332 foreach (var child in node.Elements())
5333 {
5334 if (CompilerCore.WixNamespace == child.Name.Namespace)
5335 {
5336 switch (child.Name.LocalName)
5337 {
5338 case "AppId":
5339 this.ParseAppIdElement(child, componentId, YesNoType.NotSet, id.Id, null, null);
5340 break;
5341 case "AssemblyName":
5342 this.ParseAssemblyName(child, componentId);
5343 break;
5344 case "Class":
5345 this.ParseClassElement(child, componentId, YesNoType.NotSet, id.Id, null, null, null);
5346 break;
5347 case "CopyFile":
5348 this.ParseCopyFileElement(child, componentId, id.Id);
5349 break;
5350 case "IgnoreRange":
5351 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
5352 break;
5353 case "ODBCDriver":
5354 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, SymbolDefinitionType.ODBCDriver);
5355 break;
5356 case "ODBCTranslator":
5357 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, SymbolDefinitionType.ODBCTranslator);
5358 break;
5359 case "Permission":
5360 this.ParsePermissionElement(child, id.Id, "File");
5361 break;
5362 case "PermissionEx":
5363 this.ParsePermissionExElement(child, id.Id, "File");
5364 break;
5365 case "ProtectRange":
5366 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
5367 break;
5368 case "Shortcut":
5369 this.ParseShortcutElement(child, componentId, node.Name.LocalName, id.Id, keyPath);
5370 break;
5371 case "SymbolPath":
5372 if (null != symbols)
5373 {
5374 symbols += ";" + this.ParseSymbolPathElement(child);
5375 }
5376 else
5377 {
5378 symbols = this.ParseSymbolPathElement(child);
5379 }
5380 break;
5381 case "TypeLib":
5382 this.ParseTypeLibElement(child, componentId, id.Id, win64Component);
5383 break;
5384 default:
5385 this.Core.UnexpectedElement(node, child);
5386 break;
5387 }
5388 }
5389 else
5390 {
5391 var context = new Dictionary<string, string>() { { "FileId", id?.Id }, { "ComponentId", componentId }, { "DirectoryId", directoryId }, { "Win64", win64Component.ToString() } };
5392 this.Core.ParseExtensionElement(node, child, context);
5393 }
5394 }
5395
5396 if (!this.Core.EncounteredError) 5367 if (!this.Core.EncounteredError)
5397 { 5368 {
5398 var patchAttributes = PatchAttributeType.None; 5369 var patchAttributes = PatchAttributeType.None;
@@ -5427,7 +5398,7 @@ namespace WixToolset.Core
5427 attributes |= compressed.HasValue && compressed == true ? FileSymbolAttributes.Compressed : 0; 5398 attributes |= compressed.HasValue && compressed == true ? FileSymbolAttributes.Compressed : 0;
5428 attributes |= compressed.HasValue && compressed == false ? FileSymbolAttributes.Uncompressed : 0; 5399 attributes |= compressed.HasValue && compressed == false ? FileSymbolAttributes.Uncompressed : 0;
5429 5400
5430 this.Core.AddSymbol(new FileSymbol(sourceLineNumbers, id) 5401 fileSymbol = new FileSymbol(sourceLineNumbers, id)
5431 { 5402 {
5432 ComponentRef = componentId, 5403 ComponentRef = componentId,
5433 Name = name, 5404 Name = name,
@@ -5454,11 +5425,11 @@ namespace WixToolset.Core
5454 IgnoreLengths = ignoreLengths, 5425 IgnoreLengths = ignoreLengths,
5455 RetainOffsets = protectOffsets, 5426 RetainOffsets = protectOffsets,
5456 SymbolPaths = symbols, 5427 SymbolPaths = symbols,
5457 }); 5428 };
5458 5429
5459 if (AssemblyType.NotAnAssembly != assemblyType) 5430 if (AssemblyType.NotAnAssembly != assemblyType)
5460 { 5431 {
5461 this.Core.AddSymbol(new AssemblySymbol(sourceLineNumbers, id) 5432 assemblySymbol = new AssemblySymbol(sourceLineNumbers, id)
5462 { 5433 {
5463 ComponentRef = componentId, 5434 ComponentRef = componentId,
5464 FeatureRef = Guid.Empty.ToString("B"), 5435 FeatureRef = Guid.Empty.ToString("B"),
@@ -5466,7 +5437,7 @@ namespace WixToolset.Core
5466 ApplicationFileRef = assemblyApplication, 5437 ApplicationFileRef = assemblyApplication,
5467 Type = assemblyType, 5438 Type = assemblyType,
5468 ProcessorArchitecture = procArch, 5439 ProcessorArchitecture = procArch,
5469 }); 5440 };
5470 } 5441 }
5471 } 5442 }
5472 5443
@@ -5485,6 +5456,227 @@ namespace WixToolset.Core
5485 return keyPath; 5456 return keyPath;
5486 } 5457 }
5487 5458
5459 /// <param name="node">File element to parse.</param>
5460 /// <param name="fileSymbol">The partially-parsed file symbol.</param>
5461 /// <param name="keyPath">Whether the file is the keypath of its component.</param>
5462 /// <param name="win64Component">true if the component is 64-bit.</param>
5463 private void ParseFileElementChildren(XElement node, FileSymbol fileSymbol, YesNoType keyPath, bool win64Component)
5464 {
5465 var directoryId = fileSymbol.DirectoryRef;
5466 var componentId = fileSymbol.ComponentRef;
5467 var id = fileSymbol.Id;
5468 var ignoreOffsets = fileSymbol.IgnoreOffsets;
5469 var ignoreLengths = fileSymbol.IgnoreLengths;
5470 var protectOffsets = fileSymbol.RetainOffsets;
5471 var protectLengths = fileSymbol.RetainLengths;
5472 var symbols = fileSymbol.SymbolPaths;
5473
5474 foreach (var child in node.Elements())
5475 {
5476 if (CompilerCore.WixNamespace == child.Name.Namespace)
5477 {
5478 switch (child.Name.LocalName)
5479 {
5480 case "AppId":
5481 this.ParseAppIdElement(child, componentId, YesNoType.NotSet, id.Id, null, null);
5482 break;
5483 case "AssemblyName":
5484 this.ParseAssemblyName(child, componentId);
5485 break;
5486 case "Class":
5487 this.ParseClassElement(child, componentId, YesNoType.NotSet, id.Id, null, null, null);
5488 break;
5489 case "CopyFile":
5490 this.ParseCopyFileElement(child, componentId, id.Id);
5491 break;
5492 case "IgnoreRange":
5493 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
5494 break;
5495 case "ODBCDriver":
5496 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, SymbolDefinitionType.ODBCDriver);
5497 break;
5498 case "ODBCTranslator":
5499 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, SymbolDefinitionType.ODBCTranslator);
5500 break;
5501 case "Permission":
5502 this.ParsePermissionElement(child, id.Id, "File");
5503 break;
5504 case "PermissionEx":
5505 this.ParsePermissionExElement(child, id.Id, "File");
5506 break;
5507 case "ProtectRange":
5508 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
5509 break;
5510 case "Shortcut":
5511 this.ParseShortcutElement(child, componentId, node.Name.LocalName, id.Id, keyPath);
5512 break;
5513 case "SymbolPath":
5514 if (null != symbols)
5515 {
5516 symbols += ";" + this.ParseSymbolPathElement(child);
5517 }
5518 else
5519 {
5520 symbols = this.ParseSymbolPathElement(child);
5521 }
5522 break;
5523 case "TypeLib":
5524 this.ParseTypeLibElement(child, componentId, id.Id, win64Component);
5525 break;
5526 default:
5527 this.Core.UnexpectedElement(node, child);
5528 break;
5529 }
5530 }
5531 else
5532 {
5533 var context = new Dictionary<string, string>() { { "FileId", id?.Id }, { "ComponentId", componentId }, { "DirectoryId", directoryId }, { "Win64", win64Component.ToString() } };
5534 this.Core.ParseExtensionElement(node, child, context);
5535 }
5536 }
5537
5538 fileSymbol.IgnoreOffsets = ignoreOffsets;
5539 fileSymbol.IgnoreLengths = ignoreLengths;
5540 fileSymbol.RetainOffsets = protectOffsets;
5541 fileSymbol.RetainLengths = protectLengths;
5542 fileSymbol.SymbolPaths = symbols;
5543 }
5544
5545
5546 /// <summary>
5547 /// Parses a File element.
5548 /// </summary>
5549 /// <param name="node">File element to parse.</param>
5550 /// <param name="componentId">Parent's component id.</param>
5551 /// <param name="directoryId">Ancestor's directory id.</param>
5552 /// <param name="diskId">Disk id inherited from parent component.</param>
5553 /// <param name="sourcePath">Default source path of parent directory.</param>
5554 /// <param name="possibleKeyPath">This will be set with the possible keyPath for the parent component.</param>
5555 /// <param name="win64Component">true if the component is 64-bit.</param>
5556 /// <param name="componentGuid">Component GUID (including `*`).</param>
5557 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
5558 private YesNoType ParseFileElement(XElement node, string componentId, string directoryId, int diskId, string sourcePath, out string possibleKeyPath, bool win64Component, string componentGuid)
5559 {
5560 var keyPath = this.ParseFileElementAttributes(node, componentId, directoryId, diskId, sourcePath, out possibleKeyPath, componentGuid, isNakedFile: false, out var fileSymbol, out var assemblySymbol);
5561
5562 if (!this.Core.EncounteredError)
5563 {
5564 this.Core.AddSymbol(fileSymbol);
5565
5566 if (assemblySymbol != null)
5567 {
5568 this.Core.AddSymbol(assemblySymbol);
5569 }
5570
5571 this.ParseFileElementChildren(node, fileSymbol, keyPath, win64Component);
5572 }
5573
5574 return keyPath;
5575 }
5576
5577 /// <summary>
5578 /// Parses a file element outside a component.
5579 /// </summary>
5580 /// <param name="node">File element to parse.</param>
5581 /// <param name="parentType">Type of complex reference parent. Will be Unknown if there is no parent.</param>
5582 /// <param name="parentId">Optional identifier for primary parent.</param>
5583 /// <param name="directoryId">Ancestor's directory id.</param>
5584 /// <param name="sourcePath">Default source path of parent directory.</param>
5585 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
5586 private void ParseNakedFileElement(XElement node, ComplexReferenceParentType parentType, string parentId, string directoryId, string sourcePath)
5587 {
5588 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5589 var win64 = this.Context.IsCurrentPlatform64Bit;
5590 string condition = null;
5591 string subdirectory = null;
5592
5593 var keyPath = this.ParseFileElementAttributes(node, "@WixTemporaryComponentId", directoryId, diskId: CompilerConstants.IntegerNotSet, sourcePath, out var _, componentGuid: "*", isNakedFile: true, out var fileSymbol, out var assemblySymbol);
5594
5595 if (!this.Core.EncounteredError)
5596 {
5597 // Naked files have additional attributes to handle common component attributes.
5598 foreach (var attrib in node.Attributes())
5599 {
5600 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5601 {
5602 switch (attrib.Name.LocalName)
5603 {
5604 case "Bitness":
5605 var bitnessValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
5606 switch (bitnessValue)
5607 {
5608 case "always32":
5609 win64 = false;
5610 break;
5611 case "always64":
5612 win64 = true;
5613 break;
5614 case "default":
5615 case "":
5616 break;
5617 default:
5618 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, bitnessValue, "default", "always32", "always64"));
5619 break;
5620 }
5621 break;
5622 case "Condition":
5623 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
5624 break;
5625 case "Directory":
5626 directoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
5627 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
5628 break;
5629 case "Subdirectory":
5630 subdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
5631 break;
5632 }
5633 }
5634 }
5635
5636 if (String.IsNullOrEmpty(directoryId))
5637 {
5638 directoryId = "INSTALLFOLDER";
5639 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
5640 }
5641
5642 directoryId = this.HandleSubdirectory(sourceLineNumbers, node, directoryId, subdirectory, "Directory", "Subdirectory");
5643
5644 this.Core.AddSymbol(new ComponentSymbol(sourceLineNumbers, fileSymbol.Id)
5645 {
5646 ComponentId = "*",
5647 DirectoryRef = directoryId,
5648 Location = ComponentLocation.LocalOnly,
5649 Condition = condition,
5650 KeyPath = fileSymbol.Id.Id,
5651 KeyPathType = ComponentKeyPathType.File,
5652 DisableRegistryReflection = false,
5653 NeverOverwrite = false,
5654 Permanent = false,
5655 SharedDllRefCount = false,
5656 Shared = false,
5657 Transitive = false,
5658 UninstallWhenSuperseded = false,
5659 Win64 = win64,
5660 });
5661
5662 fileSymbol.ComponentRef = fileSymbol.Id.Id;
5663 this.Core.AddSymbol(fileSymbol);
5664
5665 if (assemblySymbol != null)
5666 {
5667 this.Core.AddSymbol(assemblySymbol);
5668 }
5669
5670 this.ParseFileElementChildren(node, fileSymbol, keyPath, win64);
5671
5672 if (ComplexReferenceParentType.Unknown != parentType && null != parentId) // if parent was provided, add a complex reference to that.
5673 {
5674 // If the naked file's component is defined directly under a feature, then mark the complex reference primary.
5675 this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Component, fileSymbol.Id.Id, ComplexReferenceParentType.Feature == parentType);
5676 }
5677 }
5678 }
5679
5488 /// <summary> 5680 /// <summary>
5489 /// Parses a file search element. 5681 /// Parses a file search element.
5490 /// </summary> 5682 /// </summary>
@@ -5802,6 +5994,9 @@ namespace WixToolset.Core
5802 case "FeatureRef": 5994 case "FeatureRef":
5803 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Unknown, null); 5995 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Unknown, null);
5804 break; 5996 break;
5997 case "File":
5998 this.ParseNakedFileElement(child, ComplexReferenceParentType.Unknown, null, null, null);
5999 break;
5805 case "Icon": 6000 case "Icon":
5806 this.ParseIconElement(child); 6001 this.ParseIconElement(child);
5807 break; 6002 break;
@@ -7149,6 +7344,9 @@ namespace WixToolset.Core
7149 case "Directory": 7344 case "Directory":
7150 this.ParseDirectoryElement(child, id, diskId: CompilerConstants.IntegerNotSet, fileSource: String.Empty); 7345 this.ParseDirectoryElement(child, id, diskId: CompilerConstants.IntegerNotSet, fileSource: String.Empty);
7151 break; 7346 break;
7347 case "File":
7348 this.ParseNakedFileElement(child, ComplexReferenceParentType.Unknown, null, id, null);
7349 break;
7152 case "Merge": 7350 case "Merge":
7153 this.ParseMergeElement(child, id, diskId: CompilerConstants.IntegerNotSet); 7351 this.ParseMergeElement(child, id, diskId: CompilerConstants.IntegerNotSet);
7154 break; 7352 break;
@@ -7300,7 +7498,7 @@ namespace WixToolset.Core
7300 /// Parses a merge reference element. 7498 /// Parses a merge reference element.
7301 /// </summary> 7499 /// </summary>
7302 /// <param name="node">Element to parse.</param> 7500 /// <param name="node">Element to parse.</param>
7303 /// <param name="parentType">Parents complex reference type.</param> 7501 /// <param name="parentType">Parent's complex reference type.</param>
7304 /// <param name="parentId">Identifier for parent feature or feature group.</param> 7502 /// <param name="parentId">Identifier for parent feature or feature group.</param>
7305 private void ParseMergeRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) 7503 private void ParseMergeRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
7306 { 7504 {
diff --git a/src/wix/WixToolset.Core/Compiler_Module.cs b/src/wix/WixToolset.Core/Compiler_Module.cs
index 092f3473..19f57773 100644
--- a/src/wix/WixToolset.Core/Compiler_Module.cs
+++ b/src/wix/WixToolset.Core/Compiler_Module.cs
@@ -175,6 +175,9 @@ namespace WixToolset.Core
175 case "Exclusion": 175 case "Exclusion":
176 this.ParseExclusionElement(child); 176 this.ParseExclusionElement(child);
177 break; 177 break;
178 case "File":
179 this.ParseNakedFileElement(child, ComplexReferenceParentType.Module, this.activeName, null, null);
180 break;
178 case "Icon": 181 case "Icon":
179 this.ParseIconElement(child); 182 this.ParseIconElement(child);
180 break; 183 break;
diff --git a/src/wix/WixToolset.Core/Compiler_Package.cs b/src/wix/WixToolset.Core/Compiler_Package.cs
index 31b8e81c..220a2a76 100644
--- a/src/wix/WixToolset.Core/Compiler_Package.cs
+++ b/src/wix/WixToolset.Core/Compiler_Package.cs
@@ -303,6 +303,9 @@ namespace WixToolset.Core
303 case "FeatureGroupRef": 303 case "FeatureGroupRef":
304 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode); 304 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode);
305 break; 305 break;
306 case "File":
307 this.ParseNakedFileElement(child, ComplexReferenceParentType.Product, productCode, null, null);
308 break;
306 case "Icon": 309 case "Icon":
307 this.ParseIconElement(child); 310 this.ParseIconElement(child);
308 break; 311 break;
diff --git a/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs
index 8d9f7cab..28ad9351 100644
--- a/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs
@@ -32,6 +32,7 @@ namespace WixToolset.Core.Link
32 return; 32 return;
33 } 33 }
34 34
35 // If a directory with id INSTALLFOLDER hasn't been authored, provide a default one.
35 if (!this.Find.SymbolsByName.ContainsKey(WixStandardInstallFolderReference)) 36 if (!this.Find.SymbolsByName.ContainsKey(WixStandardInstallFolderReference))
36 { 37 {
37 var sourceLineNumber = new SourceLineNumber("DefaultInstallFolder"); 38 var sourceLineNumber = new SourceLineNumber("DefaultInstallFolder");
@@ -51,6 +52,9 @@ namespace WixToolset.Core.Link
51 ); 52 );
52 } 53 }
53 54
55 // If an upgrade hasn't been authored and the upgrade strategy is MajorUpgrade,
56 // conjure a default major upgrade with the stdlib localization string for the
57 // downgrade error message.
54 var symbols = this.Sections.SelectMany(section => section.Symbols); 58 var symbols = this.Sections.SelectMany(section => section.Symbols);
55 var upgradeSymbols = symbols.OfType<UpgradeSymbol>(); 59 var upgradeSymbols = symbols.OfType<UpgradeSymbol>();
56 if (!upgradeSymbols.Any()) 60 if (!upgradeSymbols.Any())
diff --git a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
index 5bdf8360..c00cce40 100644
--- a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
+++ b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
@@ -4,6 +4,7 @@ namespace WixToolset.Core.Link
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics;
7 using System.Linq; 8 using System.Linq;
8 using WixToolset.Data; 9 using WixToolset.Data;
9 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
@@ -11,6 +12,7 @@ namespace WixToolset.Core.Link
11 /// <summary> 12 /// <summary>
12 /// Symbol with section representing a single unique symbol. 13 /// Symbol with section representing a single unique symbol.
13 /// </summary> 14 /// </summary>
15 [DebuggerDisplay("{Symbol.DebuggerDisplay}")]
14 internal class SymbolWithSection 16 internal class SymbolWithSection
15 { 17 {
16 private List<WixSimpleReferenceSymbol> directReferences; 18 private List<WixSimpleReferenceSymbol> directReferences;
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ComponentFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ComponentFixture.cs
index 9348afa5..71b80783 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/ComponentFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/ComponentFixture.cs
@@ -41,34 +41,5 @@ namespace WixToolsetTest.CoreIntegration
41 }, errors.Select(e => e.Id).ToArray()); 41 }, errors.Select(e => e.Id).ToArray());
42 } 42 }
43 } 43 }
44
45 [Fact]
46 public void CannotBuildMissingDirectoryAttributeWithSubdirectory()
47 {
48 var folder = TestData.Get(@"TestData");
49
50 using (var fs = new DisposableFileSystem())
51 {
52 var baseFolder = fs.GetFolder();
53 var intermediateFolder = Path.Combine(baseFolder, "obj");
54 var msiPath = Path.Combine(baseFolder, "bin", "test.msi");
55
56 var result = WixRunner.Execute(new[]
57 {
58 "build",
59 Path.Combine(folder, "Component", "MissingDirectoryWithSubdirectory.wxs"),
60 Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"),
61 "-bindpath", Path.Combine(folder, "SingleFile", "data"),
62 "-intermediateFolder", intermediateFolder,
63 "-o", msiPath
64 });
65
66 var errors = result.Messages.Select(m => m.ToString()).ToArray();
67 WixAssert.CompareLineByLine(new[]
68 {
69 "The Component/@Directory attribute was not found; it is required."
70 }, errors);
71 }
72 }
73 } 44 }
74} 45}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/NakedFileFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/NakedFileFixture.cs
new file mode 100644
index 00000000..6d874ff0
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/NakedFileFixture.cs
@@ -0,0 +1,247 @@
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
3namespace WixToolsetTest.CoreIntegration
4{
5 using System.Data;
6 using System.IO;
7 using System.Linq;
8 using WixInternal.Core.TestPackage;
9 using WixInternal.TestSupport;
10 using WixToolset.Data.WindowsInstaller;
11 using Xunit;
12
13 public class NakedFileFixture
14 {
15 [Fact]
16 public void CanBuildNakedFilesInComponentGroup()
17 {
18 var rows = BuildAndQueryComponentAndFileTables("ComponentGroup.wxs");
19
20 AssertFileComponentIds(2, rows);
21 }
22
23 [Fact]
24 public void CanBuildNakedFilesInFeature()
25 {
26 var rows = BuildAndQueryComponentAndFileTables("Feature.wxs");
27
28 AssertFileComponentIds(2, rows);
29 }
30
31 [Fact]
32 public void CanBuildNakedFilesInDirectory()
33 {
34 var rows = BuildAndQueryComponentAndFileTables("Directory.wxs");
35
36 AssertFileComponentIds(2, rows);
37 }
38
39 [Fact]
40 public void CanBuildNakedFilesInDirectoryRef()
41 {
42 var rows = BuildAndQueryComponentAndFileTables("DirectoryRef.wxs");
43
44 AssertFileComponentIds(2, rows);
45 }
46
47 [Fact]
48 public void CanBuildNakedFilesInFeatureRef()
49 {
50 var rows = BuildAndQueryComponentAndFileTables("FeatureRef.wxs");
51
52 AssertFileComponentIds(2, rows);
53 }
54
55 [Fact]
56 public void CanBuildNakedFilesInFeatureGroup()
57 {
58 var rows = BuildAndQueryComponentAndFileTables("FeatureGroup.wxs");
59
60 AssertFileComponentIds(2, rows);
61 }
62
63 [Fact]
64 public void CanBuildNakedFilesInFragments()
65 {
66 var rows = BuildAndQueryComponentAndFileTables("Fragment.wxs");
67
68 AssertFileComponentIds(2, rows);
69 }
70
71 [Fact]
72 public void CanBuildNakedFilesInStandardDirectory()
73 {
74 var rows = BuildAndQueryComponentAndFileTables("StandardDirectory.wxs");
75
76 AssertFileComponentIds(2, rows);
77 }
78
79 [Fact]
80 public void CanBuildNakedFilesInModule()
81 {
82 var rows = BuildAndQueryComponentAndFileTables("Module.wxs", isPackage: false);
83
84 AssertFileComponentIds(2, rows);
85 }
86
87 [Fact]
88 public void CanBuildNakedFilesWithConditions()
89 {
90 var rows = BuildAndQueryComponentAndFileTables("Condition.wxs");
91 var componentRows = rows.Where(row => row.StartsWith("Component:")).ToArray();
92
93 // Coincidentally, the files' ids are the same as the component conditions.
94 foreach (var componentRow in componentRows)
95 {
96 var columns = componentRow.Split(':', '\t');
97 Assert.Equal(columns[1], columns[5]);
98 }
99 }
100
101 [Fact]
102 public void CanBuildNakedFilesUnderPackage()
103 {
104 var rows = BuildAndQueryComponentAndFileTables("Package.wxs");
105 AssertFileComponentIds(4, rows);
106 }
107
108 [Fact]
109 public void CanBuildNakedFilesUnderPackageWithDefaultInstallFolder()
110 {
111 var rows = BuildAndQueryComponentAndFileTables("PackageWithDefaultInstallFolder.wxs");
112 AssertFileComponentIds(4, rows);
113 }
114
115 [Fact]
116 public void NakedFilesUnderPackageWithAuthoredFeatureAreOrphaned()
117 {
118 var messages = BuildAndQueryComponentAndFileTables("PackageWithoutDefaultFeature.wxs", isPackage: true, 267);
119 Assert.Equal(new[]
120 {
121 "267",
122 "267",
123 }, messages);
124 }
125
126 [Fact]
127 public void IllegalAttributesWhenNonNakedFailTheBuild()
128 {
129 var messages = BuildAndQueryComponentAndFileTables("BadAttributes.wxs", isPackage: true, 62);
130 Assert.Equal(new[]
131 {
132 "62",
133 "62",
134 "62",
135 "62",
136 }, messages);
137 }
138
139 [Fact]
140 public void CanBuildNakedFileFromWixlibComponentGroup()
141 {
142 var rows = BuildPackageWithWixlib("WixlibComponentGroup.wxs", "WixlibComponentGroupPackage.wxs");
143
144 AssertFileComponentIds(2, rows);
145 }
146
147 private static string[] BuildPackageWithWixlib(string wixlibSourcePath, string msiSourcePath)
148 {
149 var folder = TestData.Get("TestData", "NakedFile");
150
151 using (var fs = new DisposableFileSystem())
152 {
153 var baseFolder = fs.GetFolder();
154 var intermediateFolder = Path.Combine(baseFolder, "obj");
155 var binFolder = Path.Combine(baseFolder, "bin");
156 var wixlibPath = Path.Combine(binFolder, Path.ChangeExtension(wixlibSourcePath, ".wixlib"));
157
158 var result = WixRunner.Execute(new[]
159 {
160 "build",
161 Path.Combine(folder, wixlibSourcePath),
162 "-intermediateFolder", intermediateFolder,
163 "-bindpath", folder,
164 "-o", wixlibPath,
165 });
166
167 result.AssertSuccess();
168
169 var msiPath = Path.Combine(binFolder, "test.msi");
170
171 result = WixRunner.Execute(new[]
172 {
173 "build",
174 Path.Combine(folder, msiSourcePath),
175 wixlibPath,
176 "-intermediateFolder", intermediateFolder,
177 "-bindpath", folder,
178 "-o", msiPath,
179 });
180 result.AssertSuccess();
181
182 return Query.QueryDatabase(msiPath, new[] { "Component", "File" })
183 .OrderBy(s => s)
184 .ToArray();
185 }
186 }
187
188 private static string[] BuildAndQueryComponentAndFileTables(string file, bool isPackage = true, int? exitCode = null)
189 {
190 var folder = TestData.Get("TestData", "NakedFile");
191
192 using (var fs = new DisposableFileSystem())
193 {
194 var baseFolder = fs.GetFolder();
195 var intermediateFolder = Path.Combine(baseFolder, "obj");
196 var binFolder = Path.Combine(baseFolder, "bin");
197 var msiPath = Path.Combine(binFolder, isPackage ? "test.msi" : "test.msm");
198
199 var result = WixRunner.Execute(new[]
200 {
201 "build",
202 Path.Combine(folder, file),
203 "-intermediateFolder", intermediateFolder,
204 "-bindpath", folder,
205 "-o", msiPath,
206 });
207
208 if (exitCode.HasValue)
209 {
210 Assert.Equal(exitCode.Value, result.ExitCode);
211
212 return result.Messages.Select(m => m.Id.ToString()).ToArray();
213 }
214 else
215 {
216 result.AssertSuccess();
217
218 return Query.QueryDatabase(msiPath, new[] { "Component", "File" })
219 .OrderBy(s => s)
220 .ToArray();
221 }
222 }
223 }
224
225 private static void AssertFileComponentIds(int fileCount, string[] rows)
226 {
227 var componentRows = rows.Where(row => row.StartsWith("Component:")).ToArray();
228 var fileRows = rows.Where(row => row.StartsWith("File:")).ToArray();
229
230 Assert.Equal(fileCount, componentRows.Length);
231 Assert.Equal(componentRows.Length, fileRows.Length);
232
233 // Component id == Component keypath == File id
234 foreach (var componentRow in componentRows)
235 {
236 var columns = componentRow.Split(':', '\t');
237 Assert.Equal(columns[1], columns[6]);
238 }
239
240 foreach (var fileRow in fileRows)
241 {
242 var columns = fileRow.Split(':', '\t');
243 Assert.Equal(columns[1], columns[2]);
244 }
245 }
246 }
247}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Component/MissingDirectoryWithSubdirectory.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Component/MissingDirectoryWithSubdirectory.wxs
deleted file mode 100644
index cefa9abc..00000000
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Component/MissingDirectoryWithSubdirectory.wxs
+++ /dev/null
@@ -1,10 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Fragment>
4 <ComponentGroup Id="ProductComponents">
5 <Component Subdirectory="fails\without\Directory\attribute">
6 <File Source="test.txt" />
7 </Component>
8 </ComponentGroup>
9 </Fragment>
10</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/BadAttributes.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/BadAttributes.wxs
new file mode 100644
index 00000000..269db3c0
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/BadAttributes.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <Component Directory="ProgramFilesFolder" Subdirectory="MsiPackage">
7 <File Source="test.txt" Bitness="always64" Condition="BLAHBLAHBLAH" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" />
8 </Component>
9 </Feature>
10 </Package>
11</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/ComponentGroup.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/ComponentGroup.wxs
new file mode 100644
index 00000000..69a539a9
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/ComponentGroup.wxs
@@ -0,0 +1,14 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <ComponentGroupRef Id="Files" />
7 </Feature>
8
9 <ComponentGroup Id="Files" Directory="ProgramFilesFolder" Subdirectory="MsiPackage">
10 <File Source="test.txt" />
11 <File Source="test.txt" Name="test2.txt" />
12 </ComponentGroup>
13 </Package>
14</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Condition.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Condition.wxs
new file mode 100644
index 00000000..3b5dae22
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Condition.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <File Id="FILE1" Condition="FILE1" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
7 <File Id="FILE2" Condition="FILE2" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
8 </Feature>
9 </Package>
10</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Directory.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Directory.wxs
new file mode 100644
index 00000000..85cdb029
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Directory.wxs
@@ -0,0 +1,13 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <StandardDirectory Id="ProgramFilesFolder">
6 <Directory Id="INSTALLFOLDER" Name="MsiPackage">
7 <!-- Relies on default-feature feature to include naked files in package. -->
8 <File Id="test.txt" Source="test.txt" />
9 <File Id="test2.txt" Source="test.txt" Name="test2.txt" />
10 </Directory>
11 </StandardDirectory>
12 </Package>
13</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/DirectoryRef.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/DirectoryRef.wxs
new file mode 100644
index 00000000..6de50ac4
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/DirectoryRef.wxs
@@ -0,0 +1,17 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <DirectoryRef Id="INSTALLFOLDER">
6 <!-- Relies on default-feature feature to include naked files in package. -->
7 <File Id="test.txt" Source="test.txt" />
8 <File Id="test2.txt" Source="test.txt" Name="test2.txt" />
9 </DirectoryRef>
10 </Package>
11
12 <Fragment>
13 <StandardDirectory Id="ProgramFilesFolder">
14 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
15 </StandardDirectory>
16 </Fragment>
17</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Feature.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Feature.wxs
new file mode 100644
index 00000000..d4c2daa9
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Feature.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
7 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
8 </Feature>
9 </Package>
10</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureGroup.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureGroup.wxs
new file mode 100644
index 00000000..94fdd0de
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureGroup.wxs
@@ -0,0 +1,22 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <FeatureGroupRef Id="ProductFeatureGroup" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15
16 <Fragment>
17 <FeatureGroup Id="ProductFeatureGroup">
18 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
19 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
20 </FeatureGroup>
21 </Fragment>
22</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureRef.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureRef.wxs
new file mode 100644
index 00000000..c92db44b
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/FeatureRef.wxs
@@ -0,0 +1,20 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <FeatureRef Id="ProductFeature">
6 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
7 <File Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
8 </FeatureRef>
9 </Package>
10
11 <Fragment>
12 <StandardDirectory Id="ProgramFilesFolder">
13 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
14 </StandardDirectory>
15 </Fragment>
16
17 <Fragment>
18 <Feature Id="ProductFeature" />
19 </Fragment>
20</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Fragment.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Fragment.wxs
new file mode 100644
index 00000000..6393bbc4
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Fragment.wxs
@@ -0,0 +1,24 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <FeatureGroupRef Id="FeatureGroup1" />
6 <PropertyRef Id="Property2" />
7 </Package>
8
9 <Fragment>
10 <StandardDirectory Id="ProgramFilesFolder">
11 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
12 </StandardDirectory>
13 </Fragment>
14
15 <Fragment>
16 <FeatureGroup Id="FeatureGroup1" />
17 <File Id="test.txt" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
18 </Fragment>
19
20 <Fragment>
21 <Property Id="Property2" Hidden="yes" />
22 <File Id="test2.txt" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
23 </Fragment>
24</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Module.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Module.wxs
new file mode 100644
index 00000000..c94f3720
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Module.wxs
@@ -0,0 +1,6 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Module Id="MergeModule" Guid="e535b765-1019-4a4f-b3ea-ae28870e6d73" Language="1033" Version="1.0.0.0">
3 <File Id="FILE1" Condition="FILE1" Directory="ProgramFilesFolder" Subdirectory="MergeModule" Source="test.txt" />
4 <File Id="FILE2" Condition="FILE2" Directory="ProgramFilesFolder" Subdirectory="MergeModule" Source="test.txt" Name="test2.txt" />
5 </Module>
6</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Package.wxs
new file mode 100644
index 00000000..e5dd94e0
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/Package.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <File Directory="INSTALLFOLDER" Source="test.txt" />
6 <File Directory="INSTALLFOLDER" Source="test.txt" Name="test2.txt" />
7 <File Directory="INSTALLFOLDER" Source="test.txt" Name="test3.txt" />
8 <File Directory="INSTALLFOLDER" Source="test.txt" Name="test4.txt" />
9 </Package>
10</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithDefaultInstallFolder.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithDefaultInstallFolder.wxs
new file mode 100644
index 00000000..824f3501
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithDefaultInstallFolder.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <File Source="test.txt" />
6 <File Source="test.txt" Name="test2.txt" />
7 <File Source="test.txt" Name="test3.txt" />
8 <File Source="test.txt" Name="test4.txt" />
9 </Package>
10</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithoutDefaultFeature.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithoutDefaultFeature.wxs
new file mode 100644
index 00000000..0dbf2045
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/PackageWithoutDefaultFeature.wxs
@@ -0,0 +1,17 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <File Source="test.txt" Name="test3.txt" />
6 <File Source="test.txt" Name="test4.txt" />
7
8 <Feature Id="ProductFeature">
9 <ComponentGroupRef Id="Files" />
10 </Feature>
11
12 <ComponentGroup Id="Files" Directory="ProgramFilesFolder" Subdirectory="MsiPackage">
13 <File Source="test.txt" />
14 <File Source="test.txt" Name="test2.txt" />
15 </ComponentGroup>
16 </Package>
17</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/StandardDirectory.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/StandardDirectory.wxs
new file mode 100644
index 00000000..5b6bdf2f
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/StandardDirectory.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <StandardDirectory Id="ProgramFilesFolder">
6 <!-- Relies on default-feature feature to include naked files in package. -->
7 <File Id="test.txt" Source="test.txt" />
8 <File Id="test2.txt" Source="test.txt" Name="test2.txt" />
9 </StandardDirectory>
10 </Package>
11</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroup.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroup.wxs
new file mode 100644
index 00000000..90ce0dc9
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroup.wxs
@@ -0,0 +1,8 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment>
3 <ComponentGroup Id="WixlibFiles">
4 <File Id="test.txt" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" />
5 <File Id="test2.txt" Directory="ProgramFilesFolder" Subdirectory="MsiPackage" Source="test.txt" Name="test2.txt" />
6 </ComponentGroup>
7 </Fragment>
8</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroupPackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroupPackage.wxs
new file mode 100644
index 00000000..69b92038
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/WixlibComponentGroupPackage.wxs
@@ -0,0 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <ComponentGroupRef Id="WixlibFiles" />
4 </Package>
5</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/test.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/test.txt
new file mode 100644
index 00000000..d32727e0
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/NakedFile/test.txt
@@ -0,0 +1 @@
This is test.txt.
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SingleFile/PackageComponents.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SingleFile/PackageComponents.wxs
index b8e9f59c..488e230f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SingleFile/PackageComponents.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SingleFile/PackageComponents.wxs
@@ -2,9 +2,8 @@
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Fragment> 3 <Fragment>
4 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> 4 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
5 <Component> 5 <File Source="test.txt" />
6 <File Source="test.txt" /> 6
7 </Component>
8 <Component Id="Shared.dll" Shared="yes"> 7 <Component Id="Shared.dll" Shared="yes">
9 <File Name="Shared.dll" Source="test.txt" /> 8 <File Name="Shared.dll" Source="test.txt" />
10 </Component> 9 </Component>