aboutsummaryrefslogtreecommitdiff
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
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.
-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));