Skip to content

Commit 0932858

Browse files
committed
Support naked files.
Implements wixtoolset/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](wixtoolset/issues#7581). Naked files appearing under `ComponentGroup`, `Feature`, `FeatureRef`, and `FeatureGroup` generate the component and the reference to the parent element.
1 parent 1c249dd commit 0932858

21 files changed

+707
-79
lines changed

src/api/wix/WixToolset.Data/ErrorMessages.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,11 @@ public static Message IllegalInnerText(SourceLineNumber sourceLineNumbers, strin
22562256
return Message(sourceLineNumbers, Ids.IllegalInnerText, "The {0} element contains inner text which is obsolete. Use the {1} attribute instead.", elementName, attributeName);
22572257
}
22582258

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+
22592264
private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
22602265
{
22612266
return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);

src/wix/WixToolset.Core/Compiler.cs

Lines changed: 266 additions & 76 deletions
Large diffs are not rendered by default.

src/wix/WixToolset.Core/Compiler_Module.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ private void ParseModuleElement(XElement node)
175175
case "Exclusion":
176176
this.ParseExclusionElement(child);
177177
break;
178+
case "File":
179+
this.ParseNakedFileElement(child, ComplexReferenceParentType.Module, this.activeName, null, null);
180+
break;
178181
case "Icon":
179182
this.ParseIconElement(child);
180183
break;

src/wix/WixToolset.Core/Compiler_Package.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ private void ParsePackageElement(XElement node)
287287
case "FeatureGroupRef":
288288
this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode);
289289
break;
290+
case "File":
291+
this.ParseNakedFileElement(child, ComplexReferenceParentType.Product, productCode, null, null);
292+
break;
290293
case "Icon":
291294
this.ParseIconElement(child);
292295
break;
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2+
3+
namespace 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 Xunit;
11+
12+
public class NakedFileFixture
13+
{
14+
[Fact]
15+
public void CanBuildNakedFilesInComponentGroup()
16+
{
17+
var rows = BuildAndQueryComponentAndFileTables("ComponentGroup.wxs");
18+
19+
AssertFileComponentIds(2, rows);
20+
}
21+
22+
[Fact]
23+
public void CanBuildNakedFilesInFeature()
24+
{
25+
var rows = BuildAndQueryComponentAndFileTables("Feature.wxs");
26+
27+
AssertFileComponentIds(2, rows);
28+
}
29+
30+
[Fact]
31+
public void CanBuildNakedFilesInDirectory()
32+
{
33+
var rows = BuildAndQueryComponentAndFileTables("Directory.wxs");
34+
35+
AssertFileComponentIds(2, rows);
36+
}
37+
38+
[Fact]
39+
public void CanBuildNakedFilesInDirectoryRef()
40+
{
41+
var rows = BuildAndQueryComponentAndFileTables("DirectoryRef.wxs");
42+
43+
AssertFileComponentIds(2, rows);
44+
}
45+
46+
[Fact]
47+
public void CanBuildNakedFilesInFeatureRef()
48+
{
49+
var rows = BuildAndQueryComponentAndFileTables("FeatureRef.wxs");
50+
51+
AssertFileComponentIds(2, rows);
52+
}
53+
54+
[Fact]
55+
public void CanBuildNakedFilesInFeatureGroup()
56+
{
57+
var rows = BuildAndQueryComponentAndFileTables("FeatureGroup.wxs");
58+
59+
AssertFileComponentIds(2, rows);
60+
}
61+
62+
[Fact]
63+
public void CanBuildNakedFilesInFragments()
64+
{
65+
var rows = BuildAndQueryComponentAndFileTables("Fragment.wxs");
66+
67+
AssertFileComponentIds(2, rows);
68+
}
69+
70+
[Fact]
71+
public void CanBuildNakedFilesInStandardDirectory()
72+
{
73+
var rows = BuildAndQueryComponentAndFileTables("StandardDirectory.wxs");
74+
75+
AssertFileComponentIds(2, rows);
76+
}
77+
78+
[Fact]
79+
public void CanBuildNakedFilesInModule()
80+
{
81+
var rows = BuildAndQueryComponentAndFileTables("Module.wxs", isPackage: false);
82+
83+
AssertFileComponentIds(2, rows);
84+
}
85+
86+
[Fact]
87+
public void CanBuildNakedFilesWithConditions()
88+
{
89+
var rows = BuildAndQueryComponentAndFileTables("Condition.wxs");
90+
var componentRows = rows.Where(row => row.StartsWith("Component:")).ToArray();
91+
92+
// Coincidentally, the files' ids are the same as the component conditions.
93+
foreach (var componentRow in componentRows)
94+
{
95+
var columns = componentRow.Split(':', '\t');
96+
Assert.Equal(columns[1], columns[5]);
97+
}
98+
}
99+
100+
[Fact]
101+
public void NakedFilesUnderPackageWithAuthoredFeatureAreOrphaned()
102+
{
103+
var messages = BuildAndQueryComponentAndFileTables("PackageWithoutDefaultFeature.wxs", isPackage: true, 267);
104+
Assert.Equal(new[]
105+
{
106+
"267",
107+
"267",
108+
}, messages);
109+
}
110+
111+
[Fact]
112+
public void IllegalAttributesWhenNonNakedFailTheBuild()
113+
{
114+
var messages = BuildAndQueryComponentAndFileTables("BadAttributes.wxs", isPackage: true, 62);
115+
Assert.Equal(new[]
116+
{
117+
"62",
118+
"62",
119+
"62",
120+
"62",
121+
}, messages);
122+
}
123+
124+
[Fact]
125+
public void CanBuildNakedFileFromWixlibComponentGroup()
126+
{
127+
var rows = BuildPackageWithWixlib("WixlibComponentGroup.wxs", "WixlibComponentGroupPackage.wxs");
128+
129+
AssertFileComponentIds(2, rows);
130+
}
131+
132+
private static string[] BuildPackageWithWixlib(string wixlibSourcePath, string msiSourcePath)
133+
{
134+
var folder = TestData.Get("TestData", "NakedFile");
135+
136+
using (var fs = new DisposableFileSystem())
137+
{
138+
var baseFolder = fs.GetFolder();
139+
var intermediateFolder = Path.Combine(baseFolder, "obj");
140+
var binFolder = Path.Combine(baseFolder, "bin");
141+
var wixlibPath = Path.Combine(binFolder, Path.ChangeExtension(wixlibSourcePath, ".wixlib"));
142+
143+
var result = WixRunner.Execute(new[]
144+
{
145+
"build",
146+
Path.Combine(folder, wixlibSourcePath),
147+
"-intermediateFolder", intermediateFolder,
148+
"-bindpath", folder,
149+
"-o", wixlibPath,
150+
});
151+
152+
result.AssertSuccess();
153+
154+
var msiPath = Path.Combine(binFolder, "test.msi");
155+
156+
result = WixRunner.Execute(new[]
157+
{
158+
"build",
159+
Path.Combine(folder, msiSourcePath),
160+
wixlibPath,
161+
"-intermediateFolder", intermediateFolder,
162+
"-bindpath", folder,
163+
"-o", msiPath,
164+
});
165+
result.AssertSuccess();
166+
167+
return Query.QueryDatabase(msiPath, new[] { "Component", "File" })
168+
.OrderBy(s => s)
169+
.ToArray();
170+
}
171+
}
172+
173+
private static string[] BuildAndQueryComponentAndFileTables(string file, bool isPackage = true, int? exitCode = null)
174+
{
175+
var folder = TestData.Get("TestData", "NakedFile");
176+
177+
using (var fs = new DisposableFileSystem())
178+
{
179+
var baseFolder = fs.GetFolder();
180+
var intermediateFolder = Path.Combine(baseFolder, "obj");
181+
var binFolder = Path.Combine(baseFolder, "bin");
182+
var msiPath = Path.Combine(binFolder, isPackage ? "test.msi" : "test.msm");
183+
184+
var result = WixRunner.Execute(new[]
185+
{
186+
"build",
187+
Path.Combine(folder, file),
188+
"-intermediateFolder", intermediateFolder,
189+
"-bindpath", folder,
190+
"-o", msiPath,
191+
});
192+
193+
if (exitCode.HasValue)
194+
{
195+
Assert.Equal(exitCode.Value, result.ExitCode);
196+
197+
return result.Messages.Select(m => m.Id.ToString()).ToArray();
198+
}
199+
else
200+
{
201+
result.AssertSuccess();
202+
203+
return Query.QueryDatabase(msiPath, new[] { "Component", "File" })
204+
.OrderBy(s => s)
205+
.ToArray();
206+
}
207+
}
208+
}
209+
210+
private static void AssertFileComponentIds(int fileCount, string[] rows)
211+
{
212+
var componentRows = rows.Where(row => row.StartsWith("Component:")).ToArray();
213+
var fileRows = rows.Where(row => row.StartsWith("File:")).ToArray();
214+
215+
Assert.Equal(fileCount, componentRows.Length);
216+
Assert.Equal(componentRows.Length, fileRows.Length);
217+
218+
// Component id == Component keypath == File id
219+
foreach (var componentRow in componentRows)
220+
{
221+
var columns = componentRow.Split(':', '\t');
222+
Assert.Equal(columns[1], columns[6]);
223+
}
224+
225+
foreach (var fileRow in fileRows)
226+
{
227+
var columns = fileRow.Split(':', '\t');
228+
Assert.Equal(columns[1], columns[2]);
229+
}
230+
}
231+
}
232+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>

0 commit comments

Comments
 (0)