aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-02-23 15:15:10 -0800
committerRob Mensching <rob@firegiant.com>2022-02-23 18:33:52 -0800
commitbc101e0424d627ca79f79b30e9fccf61db722466 (patch)
treec996242aba6e876e91f70413be7fdb7142471304 /src
parent72a63a97f062c1755a2edc4afe45c17bec68ddcf (diff)
downloadwix-bc101e0424d627ca79f79b30e9fccf61db722466.tar.gz
wix-bc101e0424d627ca79f79b30e9fccf61db722466.tar.bz2
wix-bc101e0424d627ca79f79b30e9fccf61db722466.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs4
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/ILibraryResult.cs25
-rw-r--r--src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs21
-rw-r--r--src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj10
-rw-r--r--src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl2
-rw-r--r--src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs6
-rw-r--r--src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj2
-rw-r--r--src/test/wix/WixE2E/WixE2EFixture.cs109
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs34
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs22
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs13
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs15
-rw-r--r--src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs3
-rw-r--r--src/wix/WixToolset.Core/CommandLine/BuildCommand.cs50
-rw-r--r--src/wix/WixToolset.Core/ILibrarian.cs11
-rw-r--r--src/wix/WixToolset.Core/Librarian.cs26
-rw-r--r--src/wix/WixToolset.Core/LibraryResult.cs19
-rw-r--r--src/wix/WixToolset.Core/WixToolsetServiceProvider.cs1
18 files changed, 311 insertions, 62 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs b/src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs
index cbc9e4ba..ff5edae3 100644
--- a/src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs
+++ b/src/api/wix/WixToolset.Extensibility/BaseLibrarianExtension.cs
@@ -48,8 +48,8 @@ namespace WixToolset.Extensibility
48 /// <summary> 48 /// <summary>
49 /// Called at the end of combining. 49 /// Called at the end of combining.
50 /// </summary> 50 /// </summary>
51 /// <param name="library">Combined library intermediate.</param> 51 /// <param name="result">Combined library result.</param>
52 public virtual void PostCombine(Intermediate library) 52 public virtual void PostCombine(ILibraryResult result)
53 { 53 {
54 } 54 }
55 55
diff --git a/src/api/wix/WixToolset.Extensibility/Data/ILibraryResult.cs b/src/api/wix/WixToolset.Extensibility/Data/ILibraryResult.cs
new file mode 100644
index 00000000..6974e265
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/Data/ILibraryResult.cs
@@ -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
3namespace 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}
diff --git a/src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs b/src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs
index d9b04cd2..d25da5e4 100644
--- a/src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs
+++ b/src/api/wix/WixToolset.Extensibility/ILibrarianExtension.cs
@@ -5,13 +5,30 @@ namespace WixToolset.Extensibility
5 using WixToolset.Data; 5 using WixToolset.Data;
6 using WixToolset.Extensibility.Data; 6 using WixToolset.Extensibility.Data;
7 7
8#pragma warning disable 1591 // TODO: add documentation 8 /// <summary>
9 /// Interface all librarian extensions implement.
10 /// </summary>
9 public interface ILibrarianExtension 11 public interface ILibrarianExtension
10 { 12 {
13 /// <summary>
14 /// Called at the beginning of combining.
15 /// </summary>
16 /// <param name="context">Librarian context.</param>
11 void PreCombine(ILibraryContext context); 17 void PreCombine(ILibraryContext context);
12 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>
13 IResolveFileResult ResolveFile(SourceLineNumber sourceLineNumber, IntermediateSymbolDefinition symbolDefinition, string path); 26 IResolveFileResult ResolveFile(SourceLineNumber sourceLineNumber, IntermediateSymbolDefinition symbolDefinition, string path);
14 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);
16 } 33 }
17} 34}
diff --git a/src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj b/src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj
index 07e42b08..6009460c 100644
--- a/src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj
+++ b/src/test/wix/TestData/WixprojModuleCsprojWinFormsNetFx/WixprojModuleCsprojWinFormsNetFx.wixproj
@@ -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. --> 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
2 <Project Sdk='WixToolset.Sdk'> 3 <Project Sdk='WixToolset.Sdk'>
3<!--<Project>
4 <Import Sdk="WixToolset.Sdk" Project='D:\src\wix4\build\wix\Debug\netcoreapp3.1\Sdk\Sdk.props' />-->
5 <PropertyGroup> 4 <PropertyGroup>
6 <OutputType>Module</OutputType> 5 <OutputType>Module</OutputType>
7 </PropertyGroup> 6 </PropertyGroup>
8 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
14 <ItemGroup> 8 <ItemGroup>
15 <ProjectReference Include="..\CsprojWindowsFormsNetFx\CsprojWindowsFormsNetFx.csproj" /> 9 <ProjectReference Include="..\CsprojWindowsFormsNetFx\CsprojWindowsFormsNetFx.csproj" />
16 </ItemGroup> 10 </ItemGroup>
17
18 <!--<Import Sdk="WixToolset.Sdk" Project='D:\src\wix4\build\wix\Debug\netcoreapp3.1\Sdk\Sdk.targets' />-->
19</Project> 11</Project>
diff --git a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl
index 52e1871e..0f902c8d 100644
--- a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl
+++ b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.ja-jp.wxl
@@ -5,7 +5,7 @@ This file contains the declaration of all the localizable strings.
5--> 5-->
6<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP"> 6<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="ja-JP">
7 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> 9 <String Id="FeatureTitle">The First Feature in my Prooooduct, eh?</String>
10 10
11</WixLocalization> 11</WixLocalization>
diff --git a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs
index fe849608..508d4721 100644
--- a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs
+++ b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/Package.wxs
@@ -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">
2 <Package Name='!(loc.PackageName)' Manufacturer='WiX Toolset' Version='0.0.1' UpgradeCode='41a2c17e-1976-465b-bcde-eae03516ca68'> 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' />
3 5
4 <StandardDirectory Id='ProgramFiles6432Folder'> 6 <StandardDirectory Id='ProgramFiles6432Folder'>
5 <Directory Id='ApplicationFolder' Name='Test App'> 7 <Directory Id='ApplicationFolder' Name='Test App'>
@@ -16,5 +18,7 @@
16 18
17 <MergeRef Id='WinFormsModule' /> 19 <MergeRef Id='WinFormsModule' />
18 </Feature> 20 </Feature>
21
22 <ui:WixUI Id="WixUI_Minimal" />
19 </Package> 23 </Package>
20</Wix> 24</Wix>
diff --git a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj
index 7dc26a2d..bbd39870 100644
--- a/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj
+++ b/src/test/wix/TestData/WixprojPackageVcxprojWindowsApp/WixprojPackageVcxprojWindowsApp.wixproj
@@ -5,5 +5,7 @@
5 <ProjectReference Include="..\VcxprojWindowsApp\VcxprojWindowsApp.vcxproj" /> 5 <ProjectReference Include="..\VcxprojWindowsApp\VcxprojWindowsApp.vcxproj" />
6 <ProjectReference Include="..\WixprojLibraryVcxprojDll\WixprojLibraryVcxprojDll.wixproj" /> 6 <ProjectReference Include="..\WixprojLibraryVcxprojDll\WixprojLibraryVcxprojDll.wixproj" />
7 <ProjectReference Include="..\WixprojModuleCsprojWinFormsNetFx\WixprojModuleCsprojWinFormsNetFx.wixproj" /> 7 <ProjectReference Include="..\WixprojModuleCsprojWinFormsNetFx\WixprojModuleCsprojWinFormsNetFx.wixproj" />
8
9 <PackageReference Include="Wixtoolset.UI.wixext" Version="4.0.0-preview.1-build.205" />
8 </ItemGroup> 10 </ItemGroup>
9</Project> 11</Project>
diff --git a/src/test/wix/WixE2E/WixE2EFixture.cs b/src/test/wix/WixE2E/WixE2EFixture.cs
index 89762f72..b1c6b2de 100644
--- a/src/test/wix/WixE2E/WixE2EFixture.cs
+++ b/src/test/wix/WixE2E/WixE2EFixture.cs
@@ -2,7 +2,12 @@
2 2
3namespace WixE2E 3namespace WixE2E
4{ 4{
5 using System;
5 using System.IO; 6 using System.IO;
7 using System.Linq;
8 using System.Security.Cryptography;
9 using System.Text;
10 using System.Threading;
6 using WixBuildTools.TestSupport; 11 using WixBuildTools.TestSupport;
7 using Xunit; 12 using Xunit;
8 13
@@ -52,6 +57,93 @@ namespace WixE2E
52 result.AssertSuccess(); 57 result.AssertSuccess();
53 } 58 }
54 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
55 private static void CleanEverything() 147 private static void CleanEverything()
56 { 148 {
57 var rootFolder = TestData.Get("TestData"); 149 var rootFolder = TestData.Get("TestData");
@@ -71,6 +163,23 @@ namespace WixE2E
71 } 163 }
72 } 164 }
73 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
74 private static MsbuildRunnerResult RestoreAndBuild(string projectPath, bool x64 = true) 183 private static MsbuildRunnerResult RestoreAndBuild(string projectPath, bool x64 = true)
75 { 184 {
76 return MsbuildRunner.Execute(projectPath, new[] { "-Restore", "-v:m", "-bl" }, x64); 185 return MsbuildRunner.Execute(projectPath, new[] { "-Restore", "-v:m", "-bl" }, x64);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index ab471702..c3dbcfce 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -244,6 +244,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
244 command.Execute(); 244 command.Execute();
245 245
246 fileFacades.AddRange(command.MergeModulesFileFacades); 246 fileFacades.AddRange(command.MergeModulesFileFacades);
247 trackedFiles.AddRange(command.TrackedFiles);
247 } 248 }
248 } 249 }
249 250
@@ -260,8 +261,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
260 261
261 if (softwareTags.Any()) 262 if (softwareTags.Any())
262 { 263 {
263 var command = new ProcessPackageSoftwareTagsCommand(section, softwareTags, this.IntermediateFolder); 264 var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, softwareTags, this.IntermediateFolder);
264 command.Execute(); 265 command.Execute();
266
267 trackedFiles.AddRange(command.TrackedFiles);
265 } 268 }
266 } 269 }
267 270
@@ -483,8 +486,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
483 { 486 {
484 this.Messaging.Write(VerboseMessages.MergingModules()); 487 this.Messaging.Write(VerboseMessages.MergingModules());
485 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);
487 command.Execute(); 490 command.Execute();
491
492 trackedFiles.AddRange(command.TrackedFiles);
488 } 493 }
489 494
490 if (this.Messaging.EncounteredError) 495 if (this.Messaging.EncounteredError)
@@ -508,8 +513,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
508 trackedFiles.AddRange(command.TrackedFiles); 513 trackedFiles.AddRange(command.TrackedFiles);
509 } 514 }
510 515
511 // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables). 516 var trackedInputFiles = this.TrackInputFiles(data, trackedFiles);
512 trackedFiles.AddRange(fileFacades.Select(f => this.WindowsInstallerBackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber))); 517 trackedFiles.AddRange(trackedInputFiles);
513 518
514 var result = this.ServiceProvider.GetService<IBindResult>(); 519 var result = this.ServiceProvider.GetService<IBindResult>();
515 result.FileTransfers = fileTransfers; 520 result.FileTransfers = fileTransfers;
@@ -613,5 +618,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind
613 618
614 return layout; 619 return layout;
615 } 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 }
616 } 642 }
617} 643}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
index aac4dd4f..06168727 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
@@ -49,9 +49,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
49 49
50 public IEnumerable<IFileFacade> MergeModulesFileFacades { get; private set; } 50 public IEnumerable<IFileFacade> MergeModulesFileFacades { get; private set; }
51 51
52 public IReadOnlyList<ITrackedFile> TrackedFiles { get; private set; }
53
52 public void Execute() 54 public void Execute()
53 { 55 {
54 var mergeModulesFileFacades = new List<IFileFacade>(); 56 var mergeModulesFileFacades = new List<IFileFacade>();
57 var trackedFiles = new List<ITrackedFile>();
55 58
56 var merge = MsmInterop.GetMsmMerge(); 59 var merge = MsmInterop.GetMsmMerge();
57 60
@@ -66,21 +69,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind
66 69
67 foreach (var wixMergeRow in this.WixMergeSymbols) 70 foreach (var wixMergeRow in this.WixMergeSymbols)
68 { 71 {
69 var containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); 72 var modulesTrackedFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
70 73
71 // If the module has files and creating layout 74 // If the module has files and creating layout
72 if (containsFiles && !this.SuppressLayout) 75 if (modulesTrackedFiles.Count > 0 && !this.SuppressLayout)
73 { 76 {
74 this.ExtractFilesFromMergeModule(merge, wixMergeRow); 77 this.ExtractFilesFromMergeModule(merge, wixMergeRow);
78 trackedFiles.AddRange(modulesTrackedFiles);
75 } 79 }
76 } 80 }
77 81
78 this.MergeModulesFileFacades = mergeModulesFileFacades; 82 this.MergeModulesFileFacades = mergeModulesFileFacades;
83 this.TrackedFiles = trackedFiles;
79 } 84 }
80 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)
82 { 87 {
83 var containsFiles = false; 88 var trackedFiles = new List<ITrackedFile>();
84 89
85 try 90 try
86 { 91 {
@@ -125,12 +130,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
125 { 130 {
126 mergeModulesFileFacades.Add(mergeModuleFileFacade); 131 mergeModulesFileFacades.Add(mergeModuleFileFacade);
127 132
128 // Keep updating the indexes as new rows are added. 133 // Keep updating the indexes as new facades are added.
129 indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); 134 indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
130 uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); 135 uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
131 }
132 136
133 containsFiles = true; 137 // Track where file will be extracted.
138 trackedFiles.Add(this.BackendHelper.TrackFile(mergeModuleFileFacade.SourcePath, TrackedFileType.Intermediate, mergeModuleFileFacade.SourceLineNumber));
139 }
134 } 140 }
135 } 141 }
136 } 142 }
@@ -164,7 +170,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
164 throw new WixException(ErrorMessages.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile)); 170 throw new WixException(ErrorMessages.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile));
165 } 171 }
166 172
167 return containsFiles; 173 return trackedFiles;
168 } 174 }
169 175
170 private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeSymbol wixMergeRow) 176 private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeSymbol wixMergeRow)
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
index 6446692e..96b7866c 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -22,9 +22,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
22 /// </summary> 22 /// </summary>
23 internal class MergeModulesCommand 23 internal class MergeModulesCommand
24 { 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 { 26 {
27 this.Messaging = messaging; 27 this.Messaging = messaging;
28 this.BackendHelper = backendHelper;
28 this.FileFacades = fileFacades; 29 this.FileFacades = fileFacades;
29 this.Section = section; 30 this.Section = section;
30 this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>(); 31 this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>();
@@ -34,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
34 35
35 private IMessaging Messaging { get; } 36 private IMessaging Messaging { get; }
36 37
38 private IBackendHelper BackendHelper { get; }
39
37 private IEnumerable<IFileFacade> FileFacades { get; } 40 private IEnumerable<IFileFacade> FileFacades { get; }
38 41
39 private IntermediateSection Section { get; } 42 private IntermediateSection Section { get; }
@@ -44,8 +47,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
44 47
45 private string IntermediateFolder { get; } 48 private string IntermediateFolder { get; }
46 49
50 public IReadOnlyList<ITrackedFile> TrackedFiles { get; private set; }
51
47 public void Execute() 52 public void Execute()
48 { 53 {
54 var trackedFiles = new List<ITrackedFile>();
55
49 var wixMergeSymbols = this.Section.Symbols.OfType<WixMergeSymbol>().ToList(); 56 var wixMergeSymbols = this.Section.Symbols.OfType<WixMergeSymbol>().ToList();
50 if (!wixMergeSymbols.Any()) 57 if (!wixMergeSymbols.Any())
51 { 58 {
@@ -93,6 +100,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
93 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); 100 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
94 moduleOpen = true; 101 moduleOpen = true;
95 102
103 trackedFiles.Add(this.BackendHelper.TrackFile(wixMergeRow.SourceFile, TrackedFileType.Input, wixMergeRow.SourceLineNumbers));
104
96 // If there is merge configuration data, create a callback object to contain it all. 105 // If there is merge configuration data, create a callback object to contain it all.
97 ConfigurationCallback callback = null; 106 ConfigurationCallback callback = null;
98 if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData)) 107 if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData))
@@ -326,6 +335,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
326 335
327 db.Commit(); 336 db.Commit();
328 } 337 }
338
339 this.TrackedFiles = trackedFiles;
329 } 340 }
330 } 341 }
331} 342}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
index 9a068603..68e11790 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
@@ -9,12 +9,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9 using System.Xml; 9 using System.Xml;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Symbols; 11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services;
12 14
13 internal class ProcessPackageSoftwareTagsCommand 15 internal class ProcessPackageSoftwareTagsCommand
14 { 16 {
15 public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IEnumerable<WixProductTagSymbol> softwareTags, string intermediateFolder) 17 public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IBackendHelper backendHelper, IEnumerable<WixProductTagSymbol> softwareTags, string intermediateFolder)
16 { 18 {
17 this.Section = section; 19 this.Section = section;
20 this.BackendHelper = backendHelper;
18 this.SoftwareTags = softwareTags; 21 this.SoftwareTags = softwareTags;
19 this.IntermediateFolder = intermediateFolder; 22 this.IntermediateFolder = intermediateFolder;
20 } 23 }
@@ -23,10 +26,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
23 26
24 private IntermediateSection Section { get; } 27 private IntermediateSection Section { get; }
25 28
29 private IBackendHelper BackendHelper { get; }
30
26 private IEnumerable<WixProductTagSymbol> SoftwareTags { get; } 31 private IEnumerable<WixProductTagSymbol> SoftwareTags { get; }
27 32
33 public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; }
34
28 public void Execute() 35 public void Execute()
29 { 36 {
37 var trackedFiles = new List<ITrackedFile>();
38
30 string productName = null; 39 string productName = null;
31 string productVersion = null; 40 string productVersion = null;
32 string manufacturer = null; 41 string manufacturer = null;
@@ -70,6 +79,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
70 // Write the tag file. 79 // Write the tag file.
71 fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(workingFolder, fileSymbol.Name) }; 80 fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(workingFolder, fileSymbol.Name) };
72 81
82 trackedFiles.Add(this.BackendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Intermediate, tagRow.SourceLineNumbers));
83
73 using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create)) 84 using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create))
74 { 85 {
75 CreateTagFile(fs, uniqueId, productName, productVersion, tagRow.Regid, manufacturer, persistentId); 86 CreateTagFile(fs, uniqueId, productName, productVersion, tagRow.Regid, manufacturer, persistentId);
@@ -86,6 +97,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
86 }); 97 });
87 } 98 }
88 } 99 }
100
101 this.TrackedFiles = trackedFiles;
89 } 102 }
90 103
91 private static string NormalizeGuid(string guidString) 104 private static string NormalizeGuid(string guidString)
diff --git a/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs b/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
index ec2d8896..5d984d53 100644
--- a/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
+++ b/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
@@ -31,6 +31,7 @@ namespace WixToolset.Core.Bind
31 foreach (var expectedEmbeddedFileByUri in group) 31 foreach (var expectedEmbeddedFileByUri in group)
32 { 32 {
33 var baseUri = expectedEmbeddedFileByUri.Key; 33 var baseUri = expectedEmbeddedFileByUri.Key;
34 trackedFiles.Add(this.BackendHelper.TrackFile(baseUri.LocalPath, TrackedFileType.Input));
34 35
35 using (var wixout = WixOutput.Read(baseUri)) 36 using (var wixout = WixOutput.Read(baseUri))
36 { 37 {
@@ -41,7 +42,7 @@ namespace WixToolset.Core.Bind
41 if (uniqueIds.Add(embeddedFile.EmbeddedFileId)) 42 if (uniqueIds.Add(embeddedFile.EmbeddedFileId))
42 { 43 {
43 wixout.ExtractEmbeddedFile(embeddedFile.EmbeddedFileId, embeddedFile.OutputPath); 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));
45 } 46 }
46 } 47 }
47 } 48 }
diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
index d289b557..67d2876d 100644
--- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -132,12 +132,7 @@ namespace WixToolset.Core.CommandLine
132 { 132 {
133 using (new IntermediateFieldContext("wix.lib")) 133 using (new IntermediateFieldContext("wix.lib"))
134 { 134 {
135 var wixlib = this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths, cancellationToken); 135 this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths, cancellationToken);
136
137 if (!this.Messaging.EncounteredError)
138 {
139 wixlib.Save(this.OutputFile);
140 }
141 } 136 }
142 } 137 }
143 else 138 else
@@ -260,7 +255,7 @@ namespace WixToolset.Core.CommandLine
260 return intermediates; 255 return intermediates;
261 } 256 }
262 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)
264 { 259 {
265 var context = this.ServiceProvider.GetService<ILibraryContext>(); 260 var context = this.ServiceProvider.GetService<ILibraryContext>();
266 context.BindFiles = bindFiles; 261 context.BindFiles = bindFiles;
@@ -270,18 +265,22 @@ namespace WixToolset.Core.CommandLine
270 context.Intermediates = intermediates; 265 context.Intermediates = intermediates;
271 context.CancellationToken = cancellationToken; 266 context.CancellationToken = cancellationToken;
272 267
273 Intermediate library = null;
274 try 268 try
275 { 269 {
276 var librarian = this.ServiceProvider.GetService<ILibrarian>(); 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 }
278 } 279 }
279 catch (WixException e) 280 catch (WixException e)
280 { 281 {
281 this.Messaging.Write(e.Error); 282 this.Messaging.Write(e.Error);
282 } 283 }
283
284 return library;
285 } 284 }
286 285
287 private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator, CancellationToken cancellationToken) 286 private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator, CancellationToken cancellationToken)
@@ -365,19 +364,7 @@ namespace WixToolset.Core.CommandLine
365 return; 364 return;
366 } 365 }
367 366
368 { 367 this.LayoutFiles(intermediateFolder, bindResult.TrackedFiles, bindResult.FileTransfers, cancellationToken);
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 }
381 } 368 }
382 finally 369 finally
383 { 370 {
@@ -385,6 +372,21 @@ namespace WixToolset.Core.CommandLine
385 } 372 }
386 } 373 }
387 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
388 private IEnumerable<Intermediate> LoadLibraries(IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator) 390 private IEnumerable<Intermediate> LoadLibraries(IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator)
389 { 391 {
390 try 392 try
diff --git a/src/wix/WixToolset.Core/ILibrarian.cs b/src/wix/WixToolset.Core/ILibrarian.cs
index 0fcedea5..8f57ca67 100644
--- a/src/wix/WixToolset.Core/ILibrarian.cs
+++ b/src/wix/WixToolset.Core/ILibrarian.cs
@@ -5,9 +5,16 @@ namespace WixToolset.Core
5 using WixToolset.Data; 5 using WixToolset.Data;
6 using WixToolset.Extensibility.Data; 6 using WixToolset.Extensibility.Data;
7 7
8#pragma warning disable 1591 // TODO: add documentation 8 /// <summary>
9 /// Create libraries from input intermediates.
10 /// </summary>
9 public interface ILibrarian 11 public interface ILibrarian
10 { 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);
12 } 19 }
13} 20}
diff --git a/src/wix/WixToolset.Core/Librarian.cs b/src/wix/WixToolset.Core/Librarian.cs
index 1dd1b44d..2762fb33 100644
--- a/src/wix/WixToolset.Core/Librarian.cs
+++ b/src/wix/WixToolset.Core/Librarian.cs
@@ -21,17 +21,20 @@ namespace WixToolset.Core
21 this.ServiceProvider = serviceProvider; 21 this.ServiceProvider = serviceProvider;
22 22
23 this.Messaging = this.ServiceProvider.GetService<IMessaging>(); 23 this.Messaging = this.ServiceProvider.GetService<IMessaging>();
24 this.LayoutServices = this.ServiceProvider.GetService<ILayoutServices>();
24 } 25 }
25 26
26 private IServiceProvider ServiceProvider { get; } 27 private IServiceProvider ServiceProvider { get; }
27 28
28 private IMessaging Messaging { get; } 29 private IMessaging Messaging { get; }
29 30
31 private ILayoutServices LayoutServices { get; }
32
30 /// <summary> 33 /// <summary>
31 /// Create a library by combining several intermediates (objects). 34 /// Create a library by combining several intermediates (objects).
32 /// </summary> 35 /// </summary>
33 /// <returns>Returns the new library.</returns> 36 /// <returns>Returns tracked input files and the new library.</returns>
34 public Intermediate Combine(ILibraryContext context) 37 public ILibraryResult Combine(ILibraryContext context)
35 { 38 {
36 if (String.IsNullOrEmpty(context.LibraryId)) 39 if (String.IsNullOrEmpty(context.LibraryId))
37 { 40 {
@@ -43,7 +46,9 @@ namespace WixToolset.Core
43 extension.PreCombine(context); 46 extension.PreCombine(context);
44 } 47 }
45 48
49 ILibraryResult result = this.ServiceProvider.GetService<ILibraryResult>();
46 Intermediate library = null; 50 Intermediate library = null;
51 IReadOnlyCollection<ITrackedFile> trackedFiles = null;
47 try 52 try
48 { 53 {
49 var sections = context.Intermediates.SelectMany(i => i.Sections).ToList(); 54 var sections = context.Intermediates.SelectMany(i => i.Sections).ToList();
@@ -56,7 +61,7 @@ namespace WixToolset.Core
56 return null; 61 return null;
57 } 62 }
58 63
59 this.ResolveFilePathsToEmbed(context, sections); 64 trackedFiles = this.ResolveFilePathsToEmbed(context, sections);
60 65
61 foreach (var section in sections) 66 foreach (var section in sections)
62 { 67 {
@@ -71,17 +76,22 @@ namespace WixToolset.Core
71 } 76 }
72 finally 77 finally
73 { 78 {
79 result.Library = library;
80 result.TrackedFiles = trackedFiles;
81
74 foreach (var extension in context.Extensions) 82 foreach (var extension in context.Extensions)
75 { 83 {
76 extension.PostCombine(library); 84 extension.PostCombine(result);
77 } 85 }
78 } 86 }
79 87
80 return this.Messaging.EncounteredError ? null : library; 88 return result;
81 } 89 }
82 90
83 private void ResolveFilePathsToEmbed(ILibraryContext context, IEnumerable<IntermediateSection> sections) 91 private IReadOnlyCollection<ITrackedFile> ResolveFilePathsToEmbed(ILibraryContext context, IEnumerable<IntermediateSection> sections)
84 { 92 {
93 var trackedFiles = new List<ITrackedFile>();
94
85 // Resolve paths to files that are to be embedded in the library. 95 // Resolve paths to files that are to be embedded in the library.
86 if (context.BindFiles) 96 if (context.BindFiles)
87 { 97 {
@@ -105,6 +115,8 @@ namespace WixToolset.Core
105 { 115 {
106 // File was successfully resolved so track the embedded index as the embedded file index. 116 // File was successfully resolved so track the embedded index as the embedded file index.
107 field.Set(new IntermediateFieldPathValue { Embed = true, Path = file }); 117 field.Set(new IntermediateFieldPathValue { Embed = true, Path = file });
118
119 trackedFiles.Add(this.LayoutServices.TrackFile(file, TrackedFileType.Input, symbol.SourceLineNumbers));
108 } 120 }
109 else 121 else
110 { 122 {
@@ -114,6 +126,8 @@ namespace WixToolset.Core
114 } 126 }
115 } 127 }
116 } 128 }
129
130 return trackedFiles;
117 } 131 }
118 132
119 private void Validate(Intermediate library) 133 private void Validate(Intermediate library)
diff --git a/src/wix/WixToolset.Core/LibraryResult.cs b/src/wix/WixToolset.Core/LibraryResult.cs
new file mode 100644
index 00000000..80db2c3a
--- /dev/null
+++ b/src/wix/WixToolset.Core/LibraryResult.cs
@@ -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
3namespace 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}
diff --git a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
index 7bbd4075..06dbdfae 100644
--- a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+++ b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
@@ -35,6 +35,7 @@ namespace WixToolset.Core
35 this.AddService<IPreprocessContext>((provider, singletons) => new PreprocessContext(provider)); 35 this.AddService<IPreprocessContext>((provider, singletons) => new PreprocessContext(provider));
36 this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider)); 36 this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider));
37 this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider)); 37 this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider));
38 this.AddService<ILibraryResult>((provider, singletons) => new LibraryResult());
38 this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider)); 39 this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider));
39 this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider)); 40 this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider));
40 this.AddService<IBindContext>((provider, singletons) => new BindContext(provider)); 41 this.AddService<IBindContext>((provider, singletons) => new BindContext(provider));