Fix wixlib, msm, msi incremental build
Simplify and improves the tracking of input and intermediate files to fix incremental build issues for wixlibs, merge modules and MSI databases.
Rob Mensching committed
Feb 23, 2022 at 15:15 UTC
bc101e0424d627ca79f79b30e9fccf61db722466
18 files changed
+311
-62
src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs
+2
-2
@@ -48,8 +48,8 @@ namespace WixToolset.Extensibility
48
/// <summary>
49
/// Called at the end of combining.
50
/// </summary>
51
- /// <param name="library">Combined library intermediate.</param>
52
- public virtual void PostCombine(Intermediate library)
51
+ /// <param name="result">Combined library result.</param>
52
+ public virtual void PostCombine(ILibraryResult result)
53
{
54
}
55
src/api/wix/WixToolset.Extensibility/Data/ILibraryResult.cs
new
+25
@@ -0,0 +1,25 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Extensibility.Data
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Threading;
8
+ using WixToolset.Data;
9
+
10
+ /// <summary>
11
+ /// Result of a library combine operation.
12
+ /// </summary>
13
+ public interface ILibraryResult
14
+ {
15
+ /// <summary>
16
+ /// Collection of files tracked when binding files into the library.
17
+ /// </summary>
18
+ IReadOnlyCollection<ITrackedFile> TrackedFiles { get; set; }
19
+
20
+ /// <summary>
21
+ /// Output of librarian.
22
+ /// </summary>
23
+ Intermediate Library { get; set; }
24
+ }
25
+}
src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs
+19
-2
@@ -5,13 +5,30 @@ namespace WixToolset.Extensibility
5
using WixToolset.Data;
6
using WixToolset.Extensibility.Data;
7
8
-#pragma warning disable 1591 // TODO: add documentation
8
+ /// <summary>
9
+ /// Interface all librarian extensions implement.
10
+ /// </summary>
11
public interface ILibrarianExtension
12
{
13
+ /// <summary>
14
+ /// Called at the beginning of combining.
15
+ /// </summary>
16
+ /// <param name="context">Librarian context.</param>
17
void PreCombine(ILibraryContext context);
18
19
+ /// <summary>
20
+ /// Resolves a path to a file path on disk.
21
+ /// </summary>
22
+ /// <param name="sourceLineNumber">Source line number for the path to resolve.</param>
23
+ /// <param name="symbolDefinition">Symbol related to the path to resolve.</param>
24
+ /// <param name="path">Path to resolve.</param>
25
+ /// <returns>Optional resolved file result.</returns>
26
IResolveFileResult ResolveFile(SourceLineNumber sourceLineNumber, IntermediateSymbolDefinition symbolDefinition, string path);
27
15
- void PostCombine(Intermediate library);
28
+ /// <summary>
29
+ /// Called at the end of combining.
30
+ /// </summary>
31
+ /// <param name="result">Combined library result.</param>
32
+ void PostCombine(ILibraryResult result);
33
}
34
}
src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj
+1
-9
@@ -1,19 +1,11 @@
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
<Project Sdk='WixToolset.Sdk'>
3
-<!--<Project>
4
- <Import Sdk="WixToolset.Sdk" Project='D:\src\wix4\build\wix\Debug\netcoreapp3.1\Sdk\Sdk.props' />-->
4
<PropertyGroup>
5
<OutputType>Module</OutputType>
6
</PropertyGroup>
7
9
- <!--<PropertyGroup>
10
- <WixBinDir>D:\src\wix4\build\wix\Debug\publish\WixToolset.Sdk\tools\net461\x86\</WixBinDir>
11
- <WixBinDir64>D:\src\wix4\build\wix\Debug\publish\WixToolset.Sdk\tools\net461\x64\</WixBinDir64>
12
- </PropertyGroup>-->
13
-
8
<ItemGroup>
9
<ProjectReference Include="..\CsprojWindowsFormsNetFx\CsprojWindowsFormsNetFx.csproj" />
10
</ItemGroup>
17
-
18
- <!--<Import Sdk="WixToolset.Sdk" Project='D:\src\wix4\build\wix\Debug\netcoreapp3.1\Sdk\Sdk.targets' />-->
11
</Project>
src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl
+1
-1
@@ -5,7 +5,7 @@ This file contains the declaration of all the localizable strings.
5
-->
6
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP">
7
8
- <String Id="PackageName">WixprojPackageVcxprojWindowsApp, eh?</String>
8
+ <String Id="PackageName">WixprojPackageVcxprojWindowsApp-jp</String>
9
<String Id="FeatureTitle">The First Feature in my Prooooduct, eh?</String>
10
11
</WixLocalization>
src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs
+5
-1
@@ -1,5 +1,7 @@
1
-<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
2
+ xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
3
<Package Name='!(loc.PackageName)' Manufacturer='WiX Toolset' Version='0.0.1' UpgradeCode='41a2c17e-1976-465b-bcde-eae03516ca68'>
4
+ <SoftwareTag Regid='wixtoolset.org' InstallDirectory='ApplicationFolder' />
5
6
<StandardDirectory Id='ProgramFiles6432Folder'>
7
<Directory Id='ApplicationFolder' Name='Test App'>
@@ -16,5 +18,7 @@
18
19
<MergeRef Id='WinFormsModule' />
20
</Feature>
21
+
22
+ <ui:WixUI Id="WixUI_Minimal" />
23
</Package>
24
</Wix>
src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj
+2
@@ -5,5 +5,7 @@
5
<ProjectReference Include="..\VcxprojWindowsApp\VcxprojWindowsApp.vcxproj" />
6
<ProjectReference Include="..\WixprojLibraryVcxprojDll\WixprojLibraryVcxprojDll.wixproj" />
7
<ProjectReference Include="..\WixprojModuleCsprojWinFormsNetFx\WixprojModuleCsprojWinFormsNetFx.wixproj" />
8
+
9
+ <PackageReference Include="Wixtoolset.UI.wixext" Version="4.0.0-preview.1-build.205" />
10
</ItemGroup>
11
</Project>
src/test/wix/WixE2E/WixE2EFixture.cs
+109
@@ -2,7 +2,12 @@
2
3
namespace WixE2E
4
{
5
+ using System;
6
using System.IO;
7
+ using System.Linq;
8
+ using System.Security.Cryptography;
9
+ using System.Text;
10
+ using System.Threading;
11
using WixBuildTools.TestSupport;
12
using Xunit;
13
@@ -52,6 +57,93 @@ namespace WixE2E
57
result.AssertSuccess();
58
}
59
60
+ [Fact]
61
+ public void CanIncrementalBuildPackageWithNativeWindowsAppWithNoEdits()
62
+ {
63
+ var projectDirectory = TestData.Get("TestData", "WixprojPackageVcxprojWindowsApp");
64
+ var projectPath = Path.Combine(projectDirectory, "WixprojPackageVcxprojWindowsApp.wixproj");
65
+ var projectBinPath = Path.Combine(projectDirectory, "bin");
66
+
67
+ CleanEverything();
68
+
69
+ var result = RestoreAndBuild(projectPath);
70
+ result.AssertSuccess();
71
+
72
+ var firstBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
73
+ var firstHashes = firstBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
74
+
75
+ // This should be an incremental build and not do any work.
76
+ //
77
+ result = RestoreAndBuild(projectPath);
78
+ result.AssertSuccess();
79
+
80
+ var secondBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
81
+ var secondHashes = secondBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
82
+
83
+ WixAssert.CompareLineByLine(firstHashes, secondHashes);
84
+ }
85
+
86
+ [Fact]
87
+ public void CanIncrementalBuildPackageWithNativeWindowsAppWithEdits()
88
+ {
89
+ var projectDirectory = TestData.Get("TestData", "WixprojPackageVcxprojWindowsApp");
90
+ var projectPath = Path.Combine(projectDirectory, "WixprojPackageVcxprojWindowsApp.wixproj");
91
+ var projectBinPath = Path.Combine(projectDirectory, "bin");
92
+
93
+ CleanEverything();
94
+
95
+ var result = RestoreAndBuild(projectPath);
96
+ result.AssertSuccess();
97
+
98
+ var firstBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
99
+ var firstRelativePaths = firstBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')}").ToArray();
100
+ var firstHashes = firstBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
101
+
102
+ var packageWxsPath = Path.Combine(projectDirectory, "Package.wxs");
103
+ File.SetLastWriteTime(packageWxsPath, DateTime.Now);
104
+
105
+ // This should be an incremental build that does work because a file was updated.
106
+ //
107
+ result = RestoreAndBuild(projectPath);
108
+ result.AssertSuccess();
109
+
110
+ var secondBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
111
+ var secondRelativePaths = secondBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')}").ToArray();
112
+ var secondHashes = secondBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
113
+
114
+ WixAssert.CompareLineByLine(firstRelativePaths, secondRelativePaths);
115
+ Assert.NotEqual(firstHashes, secondHashes);
116
+ }
117
+
118
+ [Fact(Skip = "Investigate if .NET Core WebApplications can be incrementally built")]
119
+ public void CanIncrementalBuildPackageWithNetCoreWebAppWithoutEdits()
120
+ {
121
+ var projectDirectory = TestData.Get("TestData", "WixprojPackageCsprojWebApplicationNetCore");
122
+ var projectPath = Path.Combine(projectDirectory, "WixprojPackageCsprojWebApplicationNetCore.wixproj");
123
+ var projectBinPath = Path.Combine(projectDirectory, "bin");
124
+
125
+ CleanEverything();
126
+
127
+ var result = RestoreAndBuild(projectPath);
128
+ result.AssertSuccess();
129
+
130
+ var firstBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
131
+ var firstHashes = firstBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
132
+
133
+ //var packageWxsPath = Path.Combine(projectDirectory, "Package.wxs");
134
+ //File.SetLastWriteTime(packageWxsPath, DateTime.Now);
135
+
136
+ // This should be an incremental build that does work because a file was updated.
137
+ //
138
+ result = RestoreAndBuild(projectPath);
139
+ result.AssertSuccess();
140
+
141
+ var secondBuiltFiles = Directory.GetFiles(projectBinPath, "*.*", SearchOption.AllDirectories).ToArray();
142
+ var secondHashes = secondBuiltFiles.Select(s => $"{s.Substring(projectBinPath.Length).TrimStart('\\')} with hash: {GetFileHash(s)}").ToArray();
143
+
144
+ WixAssert.CompareLineByLine(firstHashes, secondHashes);
145
+ }
146
+
147
private static void CleanEverything()
148
{
149
var rootFolder = TestData.Get("TestData");
@@ -71,6 +163,23 @@ namespace WixE2E
163
}
164
}
165
166
+ private static string GetFileHash(string path)
167
+ {
168
+ using (var sha2 = SHA256.Create())
169
+ {
170
+ var bytes = File.ReadAllBytes(path);
171
+ var hashBytes = sha2.ComputeHash(bytes);
172
+
173
+ var sb = new StringBuilder();
174
+ foreach (var hash in hashBytes)
175
+ {
176
+ sb.AppendFormat("{0:X}", hash);
177
+ }
178
+
179
+ return sb.ToString();
180
+ }
181
+ }
182
+
183
private static MsbuildRunnerResult RestoreAndBuild(string projectPath, bool x64 = true)
184
{
185
return MsbuildRunner.Execute(projectPath, new[] { "-Restore", "-v:m", "-bl" }, x64);
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+30
-4
@@ -244,6 +244,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
244
command.Execute();
245
246
fileFacades.AddRange(command.MergeModulesFileFacades);
247
+ trackedFiles.AddRange(command.TrackedFiles);
248
}
249
}
250
@@ -260,8 +261,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
261
262
if (softwareTags.Any())
263
{
263
- var command = new ProcessPackageSoftwareTagsCommand(section, softwareTags, this.IntermediateFolder);
264
+ var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, softwareTags, this.IntermediateFolder);
265
command.Execute();
266
+
267
+ trackedFiles.AddRange(command.TrackedFiles);
268
}
269
}
270
@@ -483,8 +486,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
486
{
487
this.Messaging.Write(VerboseMessages.MergingModules());
488
486
- var command = new MergeModulesCommand(this.Messaging, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
489
+ var command = new MergeModulesCommand(this.Messaging, this.WindowsInstallerBackendHelper, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
490
command.Execute();
491
+
492
+ trackedFiles.AddRange(command.TrackedFiles);
493
}
494
495
if (this.Messaging.EncounteredError)
@@ -508,8 +513,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
513
trackedFiles.AddRange(command.TrackedFiles);
514
}
515
511
- // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables).
512
- trackedFiles.AddRange(fileFacades.Select(f => this.WindowsInstallerBackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber)));
516
+ var trackedInputFiles = this.TrackInputFiles(data, trackedFiles);
517
+ trackedFiles.AddRange(trackedInputFiles);
518
519
var result = this.ServiceProvider.GetService<IBindResult>();
520
result.FileTransfers = fileTransfers;
@@ -613,5 +618,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind
618
619
return layout;
620
}
621
+
622
+ private IEnumerable<ITrackedFile> TrackInputFiles(WindowsInstallerData data, List<ITrackedFile> trackedFiles)
623
+ {
624
+ var trackedInputFiles = new List<ITrackedFile>();
625
+ var intermediateAndTemporaryPaths = new HashSet<string>(trackedFiles.Where(t => t.Type == TrackedFileType.Intermediate || t.Type == TrackedFileType.Temporary).Select(t => t.Path), StringComparer.OrdinalIgnoreCase);
626
+
627
+ foreach (var row in data.Tables.SelectMany(t => t.Rows))
628
+ {
629
+ foreach (var field in row.Fields.Where(f => f.Column.Type == ColumnType.Object))
630
+ {
631
+ var path = field.AsString();
632
+
633
+ if (!intermediateAndTemporaryPaths.Contains(path))
634
+ {
635
+ trackedInputFiles.Add(this.WindowsInstallerBackendHelper.TrackFile(path, TrackedFileType.Input, row.SourceLineNumbers));
636
+ }
637
+ }
638
+ }
639
+
640
+ return trackedInputFiles;
641
+ }
642
}
643
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+14
-8
@@ -49,9 +49,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
49
50
public IEnumerable<IFileFacade> MergeModulesFileFacades { get; private set; }
51
52
+ public IReadOnlyList<ITrackedFile> TrackedFiles { get; private set; }
53
+
54
public void Execute()
55
{
56
var mergeModulesFileFacades = new List<IFileFacade>();
57
+ var trackedFiles = new List<ITrackedFile>();
58
59
var merge = MsmInterop.GetMsmMerge();
60
@@ -66,21 +69,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind
69
70
foreach (var wixMergeRow in this.WixMergeSymbols)
71
{
69
- var containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
72
+ var modulesTrackedFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
73
74
// If the module has files and creating layout
72
- if (containsFiles && !this.SuppressLayout)
75
+ if (modulesTrackedFiles.Count > 0 && !this.SuppressLayout)
76
{
77
this.ExtractFilesFromMergeModule(merge, wixMergeRow);
78
+ trackedFiles.AddRange(modulesTrackedFiles);
79
}
80
}
81
82
this.MergeModulesFileFacades = mergeModulesFileFacades;
83
+ this.TrackedFiles = trackedFiles;
84
}
85
81
- private bool CreateFacadesForMergeModuleFiles(WixMergeSymbol wixMergeRow, List<IFileFacade> mergeModulesFileFacades, Dictionary<string, IFileFacade> indexedFileFacades)
86
+ private IReadOnlyCollection<ITrackedFile> CreateFacadesForMergeModuleFiles(WixMergeSymbol wixMergeRow, List<IFileFacade> mergeModulesFileFacades, Dictionary<string, IFileFacade> indexedFileFacades)
87
{
83
- var containsFiles = false;
88
+ var trackedFiles = new List<ITrackedFile>();
89
90
try
91
{
@@ -125,12 +130,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
130
{
131
mergeModulesFileFacades.Add(mergeModuleFileFacade);
132
128
- // Keep updating the indexes as new rows are added.
133
+ // Keep updating the indexes as new facades are added.
134
indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
135
uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
131
- }
136
133
- containsFiles = true;
137
+ // Track where file will be extracted.
138
+ trackedFiles.Add(this.BackendHelper.TrackFile(mergeModuleFileFacade.SourcePath, TrackedFileType.Intermediate, mergeModuleFileFacade.SourceLineNumber));
139
+ }
140
}
141
}
142
}
@@ -164,7 +170,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
170
throw new WixException(ErrorMessages.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile));
171
}
172
167
- return containsFiles;
173
+ return trackedFiles;
174
}
175
176
private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeSymbol wixMergeRow)
src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+12
-1
@@ -22,9 +22,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
22
/// </summary>
23
internal class MergeModulesCommand
24
{
25
- public MergeModulesCommand(IMessaging messaging, IEnumerable<IFileFacade> fileFacades, IntermediateSection section, IEnumerable<string> suppressedTableNames, string outputPath, string intermediateFolder)
25
+ public MergeModulesCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileFacade> fileFacades, IntermediateSection section, IEnumerable<string> suppressedTableNames, string outputPath, string intermediateFolder)
26
{
27
this.Messaging = messaging;
28
+ this.BackendHelper = backendHelper;
29
this.FileFacades = fileFacades;
30
this.Section = section;
31
this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>();
@@ -34,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
35
36
private IMessaging Messaging { get; }
37
38
+ private IBackendHelper BackendHelper { get; }
39
+
40
private IEnumerable<IFileFacade> FileFacades { get; }
41
42
private IntermediateSection Section { get; }
@@ -44,8 +47,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
47
48
private string IntermediateFolder { get; }
49
50
+ public IReadOnlyList<ITrackedFile> TrackedFiles { get; private set; }
51
+
52
public void Execute()
53
{
54
+ var trackedFiles = new List<ITrackedFile>();
55
+
56
var wixMergeSymbols = this.Section.Symbols.OfType<WixMergeSymbol>().ToList();
57
if (!wixMergeSymbols.Any())
58
{
@@ -93,6 +100,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
100
merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
101
moduleOpen = true;
102
103
+ trackedFiles.Add(this.BackendHelper.TrackFile(wixMergeRow.SourceFile, TrackedFileType.Input, wixMergeRow.SourceLineNumbers));
104
+
105
// If there is merge configuration data, create a callback object to contain it all.
106
ConfigurationCallback callback = null;
107
if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData))
@@ -326,6 +335,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
335
336
db.Commit();
337
}
338
+
339
+ this.TrackedFiles = trackedFiles;
340
}
341
}
342
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+14
-1
@@ -9,12 +9,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9
using System.Xml;
10
using WixToolset.Data;
11
using WixToolset.Data.Symbols;
12
+ using WixToolset.Extensibility.Data;
13
+ using WixToolset.Extensibility.Services;
14
15
internal class ProcessPackageSoftwareTagsCommand
16
{
15
- public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IEnumerable<WixProductTagSymbol> softwareTags, string intermediateFolder)
17
+ public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IBackendHelper backendHelper, IEnumerable<WixProductTagSymbol> softwareTags, string intermediateFolder)
18
{
19
this.Section = section;
20
+ this.BackendHelper = backendHelper;
21
this.SoftwareTags = softwareTags;
22
this.IntermediateFolder = intermediateFolder;
23
}
@@ -23,10 +26,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
26
27
private IntermediateSection Section { get; }
28
29
+ private IBackendHelper BackendHelper { get; }
30
+
31
private IEnumerable<WixProductTagSymbol> SoftwareTags { get; }
32
33
+ public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; }
34
+
35
public void Execute()
36
{
37
+ var trackedFiles = new List<ITrackedFile>();
38
+
39
string productName = null;
40
string productVersion = null;
41
string manufacturer = null;
@@ -70,6 +79,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
79
// Write the tag file.
80
fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(workingFolder, fileSymbol.Name) };
81
82
+ trackedFiles.Add(this.BackendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Intermediate, tagRow.SourceLineNumbers));
83
+
84
using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create))
85
{
86
CreateTagFile(fs, uniqueId, productName, productVersion, tagRow.Regid, manufacturer, persistentId);
@@ -86,6 +97,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
97
});
98
}
99
}
100
+
101
+ this.TrackedFiles = trackedFiles;
102
}
103
104
private static string NormalizeGuid(string guidString)
src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
+2
-1
@@ -31,6 +31,7 @@ namespace WixToolset.Core.Bind
31
foreach (var expectedEmbeddedFileByUri in group)
32
{
33
var baseUri = expectedEmbeddedFileByUri.Key;
34
+ trackedFiles.Add(this.BackendHelper.TrackFile(baseUri.LocalPath, TrackedFileType.Input));
35
36
using (var wixout = WixOutput.Read(baseUri))
37
{
@@ -41,7 +42,7 @@ namespace WixToolset.Core.Bind
42
if (uniqueIds.Add(embeddedFile.EmbeddedFileId))
43
{
44
wixout.ExtractEmbeddedFile(embeddedFile.EmbeddedFileId, embeddedFile.OutputPath);
44
- trackedFiles.Add(this.BackendHelper.TrackFile(embeddedFile.OutputPath, TrackedFileType.Temporary));
45
+ trackedFiles.Add(this.BackendHelper.TrackFile(embeddedFile.OutputPath, TrackedFileType.Intermediate));
46
}
47
}
48
}
src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+26
-24
@@ -132,12 +132,7 @@ namespace WixToolset.Core.CommandLine
132
{
133
using (new IntermediateFieldContext("wix.lib"))
134
{
135
- var wixlib = this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths, cancellationToken);
136
-
137
- if (!this.Messaging.EncounteredError)
138
- {
139
- wixlib.Save(this.OutputFile);
140
- }
135
+ this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths, cancellationToken);
136
}
137
}
138
else
@@ -260,7 +255,7 @@ namespace WixToolset.Core.CommandLine
255
return intermediates;
256
}
257
263
- private Intermediate LibraryPhase(IReadOnlyCollection<Intermediate> intermediates, IReadOnlyCollection<Localization> localizations, bool bindFiles, IReadOnlyCollection<IBindPath> bindPaths, CancellationToken cancellationToken)
258
+ private void LibraryPhase(IReadOnlyCollection<Intermediate> intermediates, IReadOnlyCollection<Localization> localizations, bool bindFiles, IReadOnlyCollection<IBindPath> bindPaths, CancellationToken cancellationToken)
259
{
260
var context = this.ServiceProvider.GetService<ILibraryContext>();
261
context.BindFiles = bindFiles;
@@ -270,18 +265,22 @@ namespace WixToolset.Core.CommandLine
265
context.Intermediates = intermediates;
266
context.CancellationToken = cancellationToken;
267
273
- Intermediate library = null;
268
try
269
{
270
var librarian = this.ServiceProvider.GetService<ILibrarian>();
277
- library = librarian.Combine(context);
271
+ var result = librarian.Combine(context);
272
+
273
+ if (!this.Messaging.EncounteredError)
274
+ {
275
+ result.Library.Save(this.OutputFile);
276
+
277
+ this.LayoutFiles(this.IntermediateFolder, result.TrackedFiles, null, cancellationToken);
278
+ }
279
}
280
catch (WixException e)
281
{
282
this.Messaging.Write(e.Error);
283
}
283
-
284
- return library;
284
}
285
286
private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator, CancellationToken cancellationToken)
@@ -365,19 +364,7 @@ namespace WixToolset.Core.CommandLine
364
return;
365
}
366
368
- {
369
- var context = this.ServiceProvider.GetService<ILayoutContext>();
370
- context.Extensions = this.ExtensionManager.GetServices<ILayoutExtension>();
371
- context.TrackedFiles = bindResult.TrackedFiles;
372
- context.FileTransfers = bindResult.FileTransfers;
373
- context.IntermediateFolder = intermediateFolder;
374
- context.TrackingFile = this.TrackingFile;
375
- context.ResetAcls = this.commandLine.ResetAcls;
376
- context.CancellationToken = cancellationToken;
377
-
378
- var layout = this.ServiceProvider.GetService<ILayoutCreator>();
379
- layout.Layout(context);
380
- }
367
+ this.LayoutFiles(intermediateFolder, bindResult.TrackedFiles, bindResult.FileTransfers, cancellationToken);
368
}
369
finally
370
{
@@ -385,6 +372,21 @@ namespace WixToolset.Core.CommandLine
372
}
373
}
374
375
+ private void LayoutFiles(string intermediateFolder, IReadOnlyCollection<ITrackedFile> trackedFiles, IReadOnlyCollection<IFileTransfer> fileTransfers, CancellationToken cancellationToken)
376
+ {
377
+ var context = this.ServiceProvider.GetService<ILayoutContext>();
378
+ context.Extensions = this.ExtensionManager.GetServices<ILayoutExtension>();
379
+ context.TrackedFiles = trackedFiles;
380
+ context.FileTransfers = fileTransfers;
381
+ context.IntermediateFolder = intermediateFolder;
382
+ context.TrackingFile = this.TrackingFile;
383
+ context.ResetAcls = this.commandLine.ResetAcls;
384
+ context.CancellationToken = cancellationToken;
385
+
386
+ var layout = this.ServiceProvider.GetService<ILayoutCreator>();
387
+ layout.Layout(context);
388
+ }
389
+
390
private IEnumerable<Intermediate> LoadLibraries(IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator)
391
{
392
try
src/wix/WixToolset.Core/ILibrarian.cs
+9
-2
@@ -5,9 +5,16 @@ namespace WixToolset.Core
5
using WixToolset.Data;
6
using WixToolset.Extensibility.Data;
7
8
-#pragma warning disable 1591 // TODO: add documentation
8
+ /// <summary>
9
+ /// Create libraries from input intermediates.
10
+ /// </summary>
11
public interface ILibrarian
12
{
11
- Intermediate Combine(ILibraryContext context);
13
+ /// <summary>
14
+ /// Combine intermediates into a single result.
15
+ /// </summary>
16
+ /// <param name="context">Library context.</param>
17
+ /// <returns>Library result.</returns>
18
+ ILibraryResult Combine(ILibraryContext context);
19
}
20
}
src/wix/WixToolset.Core/Librarian.cs
+20
-6
@@ -21,17 +21,20 @@ namespace WixToolset.Core
21
this.ServiceProvider = serviceProvider;
22
23
this.Messaging = this.ServiceProvider.GetService<IMessaging>();
24
+ this.LayoutServices = this.ServiceProvider.GetService<ILayoutServices>();
25
}
26
27
private IServiceProvider ServiceProvider { get; }
28
29
private IMessaging Messaging { get; }
30
31
+ private ILayoutServices LayoutServices { get; }
32
+
33
/// <summary>
34
/// Create a library by combining several intermediates (objects).
35
/// </summary>
33
- /// <returns>Returns the new library.</returns>
34
- public Intermediate Combine(ILibraryContext context)
36
+ /// <returns>Returns tracked input files and the new library.</returns>
37
+ public ILibraryResult Combine(ILibraryContext context)
38
{
39
if (String.IsNullOrEmpty(context.LibraryId))
40
{
@@ -43,7 +46,9 @@ namespace WixToolset.Core
46
extension.PreCombine(context);
47
}
48
49
+ ILibraryResult result = this.ServiceProvider.GetService<ILibraryResult>();
50
Intermediate library = null;
51
+ IReadOnlyCollection<ITrackedFile> trackedFiles = null;
52
try
53
{
54
var sections = context.Intermediates.SelectMany(i => i.Sections).ToList();
@@ -56,7 +61,7 @@ namespace WixToolset.Core
61
return null;
62
}
63
59
- this.ResolveFilePathsToEmbed(context, sections);
64
+ trackedFiles = this.ResolveFilePathsToEmbed(context, sections);
65
66
foreach (var section in sections)
67
{
@@ -71,17 +76,22 @@ namespace WixToolset.Core
76
}
77
finally
78
{
79
+ result.Library = library;
80
+ result.TrackedFiles = trackedFiles;
81
+
82
foreach (var extension in context.Extensions)
83
{
76
- extension.PostCombine(library);
84
+ extension.PostCombine(result);
85
}
86
}
87
80
- return this.Messaging.EncounteredError ? null : library;
88
+ return result;
89
}
90
83
- private void ResolveFilePathsToEmbed(ILibraryContext context, IEnumerable<IntermediateSection> sections)
91
+ private IReadOnlyCollection<ITrackedFile> ResolveFilePathsToEmbed(ILibraryContext context, IEnumerable<IntermediateSection> sections)
92
{
93
+ var trackedFiles = new List<ITrackedFile>();
94
+
95
// Resolve paths to files that are to be embedded in the library.
96
if (context.BindFiles)
97
{
@@ -105,6 +115,8 @@ namespace WixToolset.Core
115
{
116
// File was successfully resolved so track the embedded index as the embedded file index.
117
field.Set(new IntermediateFieldPathValue { Embed = true, Path = file });
118
+
119
+ trackedFiles.Add(this.LayoutServices.TrackFile(file, TrackedFileType.Input, symbol.SourceLineNumbers));
120
}
121
else
122
{
@@ -114,6 +126,8 @@ namespace WixToolset.Core
126
}
127
}
128
}
129
+
130
+ return trackedFiles;
131
}
132
133
private void Validate(Intermediate library)
src/wix/WixToolset.Core/LibraryResult.cs
new
+19
@@ -0,0 +1,19 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+namespace WixToolset.Core
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Threading;
8
+ using WixToolset.Data;
9
+ using WixToolset.Extensibility;
10
+ using WixToolset.Extensibility.Data;
11
+ using WixToolset.Extensibility.Services;
12
+
13
+ internal class LibraryResult : ILibraryResult
14
+ {
15
+ public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; set; }
16
+
17
+ public Intermediate Library { get; set; }
18
+ }
19
+}
src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+1
@@ -35,6 +35,7 @@ namespace WixToolset.Core
35
this.AddService<IPreprocessContext>((provider, singletons) => new PreprocessContext(provider));
36
this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider));
37
this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider));
38
+ this.AddService<ILibraryResult>((provider, singletons) => new LibraryResult());
39
this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider));
40
this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider));
41
this.AddService<IBindContext>((provider, singletons) => new BindContext(provider));