diff --git a/src/api/wix/WixToolset.Data/Symbols/HarvestPayloadsSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/HarvestPayloadsSymbol.cs
new file mode 100644
index 000000000..65b424acf
--- /dev/null
+++ b/src/api/wix/WixToolset.Data/Symbols/HarvestPayloadsSymbol.cs
@@ -0,0 +1,68 @@
+// 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.
+
+namespace WixToolset.Data
+{
+ using WixToolset.Data.Symbols;
+
+ public static partial class SymbolDefinitions
+ {
+ public static readonly IntermediateSymbolDefinition HarvestPayloads = new IntermediateSymbolDefinition(
+ SymbolDefinitionType.HarvestPayloads,
+ new[]
+ {
+ new IntermediateFieldDefinition(nameof(HarvestFilesSymbolFields.Inclusions), IntermediateFieldType.String),
+ new IntermediateFieldDefinition(nameof(HarvestFilesSymbolFields.Exclusions), IntermediateFieldType.String),
+ new IntermediateFieldDefinition(nameof(HarvestFilesSymbolFields.ComplexReferenceParentType), IntermediateFieldType.String),
+ new IntermediateFieldDefinition(nameof(HarvestFilesSymbolFields.ParentId), IntermediateFieldType.String),
+ },
+ typeof(HarvestPayloadsSymbol));
+ }
+}
+
+namespace WixToolset.Data.Symbols
+{
+ public enum HarvestPayloadsSymbolFields
+ {
+ Inclusions,
+ Exclusions,
+ ComplexReferenceParentType,
+ ParentId,
+ }
+
+ public class HarvestPayloadsSymbol : IntermediateSymbol
+ {
+ public HarvestPayloadsSymbol() : base(SymbolDefinitions.HarvestPayloads, null, null)
+ {
+ }
+
+ public HarvestPayloadsSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.HarvestPayloads, sourceLineNumber, id)
+ {
+ }
+
+ public IntermediateField this[HarvestPayloadsSymbolFields index] => this.Fields[(int)index];
+
+ public string Inclusions
+ {
+ get => (string)this.Fields[(int)HarvestPayloadsSymbolFields.Inclusions];
+ set => this.Set((int)HarvestPayloadsSymbolFields.Inclusions, value);
+ }
+
+ public string Exclusions
+ {
+ get => (string)this.Fields[(int)HarvestPayloadsSymbolFields.Exclusions];
+ set => this.Set((int)HarvestPayloadsSymbolFields.Exclusions, value);
+ }
+
+ public string ComplexReferenceParentType
+ {
+ get => (string)this.Fields[(int)HarvestPayloadsSymbolFields.ComplexReferenceParentType];
+ set => this.Set((int)HarvestPayloadsSymbolFields.ComplexReferenceParentType, value);
+ }
+
+ public string ParentId
+ {
+ get => (string)this.Fields[(int)HarvestPayloadsSymbolFields.ParentId];
+ set => this.Set((int)HarvestPayloadsSymbolFields.ParentId, value);
+ }
+ }
+}
diff --git a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
index 64f51162e..64c1a2a55 100644
--- a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
@@ -41,6 +41,7 @@ public enum SymbolDefinitionType
File,
FileSFPCatalog,
HarvestFiles,
+ HarvestPayloads,
Icon,
ImageFamilies,
IniFile,
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/EarliestCoreBundleSCD.wixproj b/src/ext/Bal/test/examples/EarliestCoreBundleSCD/EarliestCoreBundleSCD.wixproj
index 1179bea7a..aea77d1b1 100644
--- a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/EarliestCoreBundleSCD.wixproj
+++ b/src/ext/Bal/test/examples/EarliestCoreBundleSCD/EarliestCoreBundleSCD.wixproj
@@ -1,14 +1,6 @@
-
-
- publish.Example.EarliestCoreMBA.scd
- ba.xslt
-
-
-
-
-
+
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/SelfContainedBundle.wxs b/src/ext/Bal/test/examples/EarliestCoreBundleSCD/SelfContainedBundle.wxs
index 38a167f17..68b697e72 100644
--- a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/SelfContainedBundle.wxs
+++ b/src/ext/Bal/test/examples/EarliestCoreBundleSCD/SelfContainedBundle.wxs
@@ -1,9 +1,11 @@
-
-
-
+
+
+
+
+
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/ba.xslt b/src/ext/Bal/test/examples/EarliestCoreBundleSCD/ba.xslt
deleted file mode 100644
index d30b2564a..000000000
--- a/src/ext/Bal/test/examples/EarliestCoreBundleSCD/ba.xslt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/EarliestCoreBundleTrimmedSCD.wixproj b/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/EarliestCoreBundleTrimmedSCD.wixproj
index f9926550b..5475022cc 100644
--- a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/EarliestCoreBundleTrimmedSCD.wixproj
+++ b/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/EarliestCoreBundleTrimmedSCD.wixproj
@@ -1,14 +1,6 @@
-
-
- publish.Example.EarliestCoreMBA.trimmedscd
- ba.xslt
-
-
-
-
-
+
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs b/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
index bf4ad6e37..8895b279f 100644
--- a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
+++ b/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
@@ -1,9 +1,11 @@
-
-
-
+
+
+
+
+
diff --git a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/ba.xslt b/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/ba.xslt
deleted file mode 100644
index d30b2564a..000000000
--- a/src/ext/Bal/test/examples/EarliestCoreBundleTrimmedSCD/ba.xslt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleSCD/LatestCoreBundleSCD.wixproj b/src/ext/Bal/test/examples/LatestCoreBundleSCD/LatestCoreBundleSCD.wixproj
index 048e3c976..73582984e 100644
--- a/src/ext/Bal/test/examples/LatestCoreBundleSCD/LatestCoreBundleSCD.wixproj
+++ b/src/ext/Bal/test/examples/LatestCoreBundleSCD/LatestCoreBundleSCD.wixproj
@@ -1,14 +1,6 @@
-
-
- publish.Example.LatestCoreMBA.scd
- ba.xslt
-
-
-
-
-
+
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleSCD/SelfContainedBundle.wxs b/src/ext/Bal/test/examples/LatestCoreBundleSCD/SelfContainedBundle.wxs
index 0022b6908..1f379b598 100644
--- a/src/ext/Bal/test/examples/LatestCoreBundleSCD/SelfContainedBundle.wxs
+++ b/src/ext/Bal/test/examples/LatestCoreBundleSCD/SelfContainedBundle.wxs
@@ -1,9 +1,11 @@
-
-
-
+
+
+
+
+
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleSCD/ba.xslt b/src/ext/Bal/test/examples/LatestCoreBundleSCD/ba.xslt
deleted file mode 100644
index f606296eb..000000000
--- a/src/ext/Bal/test/examples/LatestCoreBundleSCD/ba.xslt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/LatestCoreBundleTrimmedSCD.wixproj b/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/LatestCoreBundleTrimmedSCD.wixproj
index 056bf2bb8..532f09b46 100644
--- a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/LatestCoreBundleTrimmedSCD.wixproj
+++ b/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/LatestCoreBundleTrimmedSCD.wixproj
@@ -1,14 +1,6 @@
-
-
- publish.Example.LatestCoreMBA.trimmedscd
- ba.xslt
-
-
-
-
-
+
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs b/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
index 322a27a3a..cd32628a5 100644
--- a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
+++ b/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/TrimmedSelfContainedBundle.wxs
@@ -1,9 +1,11 @@
-
-
-
+
+
+
+
+
diff --git a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/ba.xslt b/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/ba.xslt
deleted file mode 100644
index f606296eb..000000000
--- a/src/ext/Bal/test/examples/LatestCoreBundleTrimmedSCD/ba.xslt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ext/Bal/test/examples/examples.proj b/src/ext/Bal/test/examples/examples.proj
index c15447668..e439c288f 100644
--- a/src/ext/Bal/test/examples/examples.proj
+++ b/src/ext/Bal/test/examples/examples.proj
@@ -34,16 +34,12 @@
Condition="'%(CoreMBAProject.SkipFDD)'==''" />
-
+
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index 381ea39af..12a2aaf1c 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -142,7 +142,15 @@ namespace DutilTests
else
{
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
- NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+
+ if ('\\' == *sczCanonicalized)
+ {
+ NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
+ else
+ {
+ NativeAssert::StringEqual(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
}
hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
@@ -153,7 +161,15 @@ namespace DutilTests
else
{
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
- NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+
+ if ('\\' == *sczCanonicalized)
+ {
+ NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
+ else
+ {
+ NativeAssert::StringEqual(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
}
hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
@@ -288,7 +304,15 @@ namespace DutilTests
{
hr = PathAllocCanonicalizePath(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
- NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+
+ if ('\\' == *sczCanonicalized)
+ {
+ NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
+ else
+ {
+ NativeAssert::StringEqual(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
+ }
hr = PathAllocCanonicalizePath(L"abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
@@ -937,50 +961,22 @@ namespace DutilTests
void PathGetTempPathTest()
{
HRESULT hr = S_OK;
- LPCWSTR wzEnvName = L"TMP";
- LPCWSTR wzEnvName2 = L"TEMP";
- LPCWSTR wzLongTempPath = L"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\\";
LPWSTR sczTempPath = NULL;
- WCHAR wzOriginalTemp[MAX_PATH + 1] = { };
- WCHAR wzOriginalTemp2[MAX_PATH + 1] = { };
DWORD cch = 0;
- DWORD cch2 = 0;
SIZE_T cchTemp = 0;
- size_t cchTemp2 = 0;
-
- try
- {
- cch = ::GetEnvironmentVariableW(wzEnvName, wzOriginalTemp, countof(wzOriginalTemp));
- Assert::NotEqual(0, cch);
-
- if (!::SetEnvironmentVariableW(wzEnvName, wzLongTempPath))
- {
- Assert::Equal(0xFFFFFFFF, ::GetLastError());
- }
+ WCHAR wzPath[MAX_PATH + 1];
- cch2 = ::GetEnvironmentVariableW(wzEnvName2, wzOriginalTemp2, countof(wzOriginalTemp2));
- Assert::NotEqual(0, cch2);
+ hr = PathGetTempPath(&sczTempPath, &cchTemp);
+ NativeAssert::Succeeded(hr, "Failed to get temp path.");
- hr = PathGetTempPath(&sczTempPath, &cchTemp);
- NativeAssert::Succeeded(hr, "Failed to get temp path.");
+ cch = countof(wzPath);
+ cch = ::GetTempPathW(cch, wzPath);
+ Assert::NotEqual((DWORD)0, cch);
- PathFixedBackslashTerminate(wzOriginalTemp2, countof(wzOriginalTemp2));
+ // normalize trailing backslash
+ PathFixedBackslashTerminate(wzPath, cch);
- hr = ::StringCchLengthW(wzOriginalTemp2, countof(wzOriginalTemp2), &cchTemp2);
- NativeAssert::Succeeded(hr, "Failed to get temp path length.");
-
- NativeAssert::StringEqual(wzOriginalTemp2, sczTempPath);
- Assert::Equal(cchTemp2, cchTemp);
- }
- finally
- {
- if (cch)
- {
- ::SetEnvironmentVariableW(wzEnvName, wzOriginalTemp);
- }
-
- ReleaseStr(sczTempPath);
- }
+ NativeAssert::StringEqual(wzPath, sczTempPath);
}
[Fact]
diff --git a/src/libs/libs.cmd b/src/libs/libs.cmd
index d8b81f546..acf5ff555 100644
--- a/src/libs/libs.cmd
+++ b/src/libs/libs.cmd
@@ -22,6 +22,7 @@
msbuild -Restore libs_t.proj -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:%_L%\libs_build.binlog || exit /b
dotnet test ^
+ --results-directory %_L%\TestResults --blame-hang-timeout 1min --blame-hang-dump-type mini -l:"console;verbosity=detailed" ^
%_B%\net6.0\WixToolsetTest.Versioning.dll ^
%_B%\x86\DUtilUnitTest.dll ^
%_B%\x64\DUtilUnitTest.dll ^
diff --git a/src/test/burn/TestData/Manual/BundleB/Bundle.wxs b/src/test/burn/TestData/Manual/BundleB/Bundle.wxs
index 8c670577c..248ec05c3 100644
--- a/src/test/burn/TestData/Manual/BundleB/Bundle.wxs
+++ b/src/test/burn/TestData/Manual/BundleB/Bundle.wxs
@@ -30,11 +30,11 @@
-
+
-
+
diff --git a/src/test/burn/TestData/Manual/BundleB/BundleB.wixproj b/src/test/burn/TestData/Manual/BundleB/BundleB.wixproj
index 36792c2bd..d0b05bc11 100644
--- a/src/test/burn/TestData/Manual/BundleB/BundleB.wixproj
+++ b/src/test/burn/TestData/Manual/BundleB/BundleB.wixproj
@@ -7,25 +7,11 @@
-generate payloadgroup -sw5149
-
-
- BAPayloads
- BAPayloads
- ba.xslt
-
-
- PackagePayloads
- PackagePayloads
- package.xslt
-
-
-
-
diff --git a/src/test/burn/TestData/Manual/BundleB/BundleB.wxs b/src/test/burn/TestData/Manual/BundleB/BundleB.wxs
index 54082131d..f1a044456 100644
--- a/src/test/burn/TestData/Manual/BundleB/BundleB.wxs
+++ b/src/test/burn/TestData/Manual/BundleB/BundleB.wxs
@@ -4,7 +4,7 @@
-
+
diff --git a/src/test/burn/TestData/Manual/BundleB/ba.xslt b/src/test/burn/TestData/Manual/BundleB/ba.xslt
deleted file mode 100644
index 54bc7fe64..000000000
--- a/src/test/burn/TestData/Manual/BundleB/ba.xslt
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- ba_
- BAPayloads
-
-
-
diff --git a/src/test/burn/TestData/Manual/BundleB/package.xslt b/src/test/burn/TestData/Manual/BundleB/package.xslt
deleted file mode 100644
index 304ff78b8..000000000
--- a/src/test/burn/TestData/Manual/BundleB/package.xslt
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- package_
- PackagePayloads
-
-
-
diff --git a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
index f5a9781b7..be337a601 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
@@ -58,6 +58,7 @@ public void Execute()
switch (symbol.Definition.Type)
{
// Symbols used internally and are not added to a data manifest.
+ case SymbolDefinitionType.HarvestPayloads:
case SymbolDefinitionType.ProvidesDependency:
case SymbolDefinitionType.WixApprovedExeForElevation:
case SymbolDefinitionType.WixBootstrapperApplication:
diff --git a/src/wix/WixToolset.Core/Compiler.cs b/src/wix/WixToolset.Core/Compiler.cs
index 2efca5ebe..21604ad58 100644
--- a/src/wix/WixToolset.Core/Compiler.cs
+++ b/src/wix/WixToolset.Core/Compiler.cs
@@ -5787,7 +5787,7 @@ private void ParseFilesElement(XElement node, ComplexReferenceParentType parentT
switch (child.Name.LocalName)
{
case "Exclude":
- this.ParseFilesExcludeElement(child, exclusions);
+ this.ParseFilesOrPayloadsExcludeElement(child, exclusions);
break;
default:
this.Core.UnexpectedElement(node, child);
@@ -5833,7 +5833,7 @@ private void ParseFilesElement(XElement node, ComplexReferenceParentType parentT
});
}
- private void ParseFilesExcludeElement(XElement node, IList paths)
+ private void ParseFilesOrPayloadsExcludeElement(XElement node, IList paths)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs
index 14de99eb8..49a729dbb 100644
--- a/src/wix/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs
@@ -364,6 +364,9 @@ private void ParseBundleElement(XElement node)
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads);
break;
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads);
+ break;
case "RelatedBundle":
this.ParseRelatedBundleElement(child);
break;
@@ -694,15 +697,15 @@ private void ParseBootstrapperApplicationElement(XElement node)
this.Messaging.Write(CompilerErrors.AlreadyDefinedBootstrapperApplicationSource(childSourceLineNumbers, exePayloadSourceLineNumbers, exePayloadRefNode.Name.LocalName));
}
break;
-
case "Payload":
this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
break;
-
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
break;
-
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
+ break;
default:
this.Core.UnexpectedElement(node, child);
break;
@@ -879,6 +882,9 @@ private void ParseBootstrapperApplicationRefElement(XElement node)
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
break;
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
+ break;
default:
this.Core.UnexpectedElement(node, child);
break;
@@ -1264,6 +1270,9 @@ private void ParseBootstrapperExtensionElement(XElement node)
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
break;
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
+ break;
default:
this.Core.UnexpectedElement(node, child);
break;
@@ -1560,6 +1569,9 @@ private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id);
break;
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.PayloadGroup, id);
+ break;
default:
this.Core.UnexpectedElement(node, child);
break;
@@ -1632,6 +1644,80 @@ private Identifier ParsePayloadGroupRefElement(XElement node, ComplexReferencePa
return id;
}
+ ///
+ /// Parses a payloads harvesting element.
+ ///
+ /// Element to parse.
+ /// ComplexReferenceParentType of parent element (BA or PayloadGroup).
+ /// Identifier of parent element.
+ private void ParsePayloadsElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId)
+ {
+ var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
+ var win64 = this.Context.IsCurrentPlatform64Bit;
+ var inclusions = new List();
+ var exclusions = new List();
+
+ foreach (var attrib in node.Attributes())
+ {
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
+ {
+ switch (attrib.Name.LocalName)
+ {
+ case "Include":
+ inclusions.AddRange(this.Core.GetAttributeValue(sourceLineNumbers, attrib).Split(';'));
+ break;
+ default:
+ this.Core.UnexpectedAttribute(node, attrib);
+ break;
+ }
+ }
+ else
+ {
+ var context = new Dictionary() { { "Win64", win64.ToString() } };
+ this.Core.ParseExtensionAttribute(node, attrib, context);
+ }
+ }
+
+ foreach (var child in node.Elements())
+ {
+ if (CompilerCore.WixNamespace == child.Name.Namespace)
+ {
+ switch (child.Name.LocalName)
+ {
+ case "Exclude":
+ this.ParseFilesOrPayloadsExcludeElement(child, exclusions);
+ break;
+ default:
+ this.Core.UnexpectedElement(node, child);
+ break;
+ }
+ }
+ else
+ {
+ var context = new Dictionary() { { "Win64", win64.ToString() } };
+ this.Core.ParseExtensionElement(node, child, context);
+ }
+ }
+
+ if (!inclusions.Any())
+ {
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Include"));
+ }
+
+ var inclusionsAsString = String.Join(";", inclusions);
+ var exclusionsAsString = String.Join(";", exclusions);
+
+ var id = this.Core.CreateIdentifier("hvp", parentId.Id, inclusionsAsString, exclusionsAsString);
+
+ this.Core.AddSymbol(new HarvestPayloadsSymbol(sourceLineNumbers, id)
+ {
+ Inclusions = inclusionsAsString,
+ Exclusions = exclusionsAsString,
+ ComplexReferenceParentType = parentType.ToString(),
+ ParentId = parentId.Id,
+ });
+ }
+
///
/// Parse ExitCode element.
///
@@ -2279,6 +2365,9 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id);
break;
+ case "Payloads":
+ this.ParsePayloadsElement(child, ComplexReferenceParentType.Package, id);
+ break;
case "Provides":
this.ParseProvidesElement(child, packageType, id.Id, out _);
break;
diff --git a/src/wix/WixToolset.Core/HarvestFilesCommand.cs b/src/wix/WixToolset.Core/HarvestFilesAndPayloadsCommand.cs
similarity index 72%
rename from src/wix/WixToolset.Core/HarvestFilesCommand.cs
rename to src/wix/WixToolset.Core/HarvestFilesAndPayloadsCommand.cs
index 33c38589c..3b1b13580 100644
--- a/src/wix/WixToolset.Core/HarvestFilesCommand.cs
+++ b/src/wix/WixToolset.Core/HarvestFilesAndPayloadsCommand.cs
@@ -11,11 +11,11 @@ namespace WixToolset.Core
using WixToolset.Extensibility.Data;
using WixToolset.Extensibility.Services;
- internal class HarvestFilesCommand
+ internal class HarvestFilesAndPayloadsCommand
{
private const string BindPathOpenString = "!(bindpath.";
- public HarvestFilesCommand(IOptimizeContext context)
+ public HarvestFilesAndPayloadsCommand(IOptimizeContext context)
{
this.Context = context;
this.Messaging = this.Context.ServiceProvider.GetService();
@@ -31,6 +31,7 @@ public HarvestFilesCommand(IOptimizeContext context)
internal void Execute()
{
var harvestedFiles = new HashSet();
+ var harvestedPayloads = new HashSet();
foreach (var section in this.Context.Intermediates.SelectMany(i => i.Sections))
{
@@ -39,6 +40,14 @@ internal void Execute()
this.HarvestFiles(harvestFiles, section, harvestedFiles);
}
}
+
+ foreach (var section in this.Context.Intermediates.SelectMany(i => i.Sections))
+ {
+ foreach (var harvestPayloads in section.Symbols.OfType().ToList())
+ {
+ this.HarvestPayloads(harvestPayloads, section, harvestedPayloads);
+ }
+ }
}
private void HarvestFiles(HarvestFilesSymbol harvestFile, IntermediateSection section, ISet harvestedFiles)
@@ -52,8 +61,8 @@ private void HarvestFiles(HarvestFilesSymbol harvestFile, IntermediateSection se
var resolvedFiles = Enumerable.Empty();
- var included = this.GetWildcardFiles(harvestFile, inclusions);
- var excluded = this.GetWildcardFiles(harvestFile, exclusions);
+ var included = this.GetWildcardFiles(inclusions, harvestFile.SourceLineNumbers, harvestFile.SourcePath);
+ var excluded = this.GetWildcardFiles(exclusions, harvestFile.SourceLineNumbers, harvestFile.SourcePath);
foreach (var excludedFile in excluded)
{
@@ -128,10 +137,70 @@ private void HarvestFiles(HarvestFilesSymbol harvestFile, IntermediateSection se
}
}
- private IEnumerable GetWildcardFiles(HarvestFilesSymbol harvestFile, IEnumerable patterns)
+ private void HarvestPayloads(HarvestPayloadsSymbol harvestPayload, IntermediateSection section, HashSet harvestedPayloads)
+ {
+ var sourceLineNumbers = harvestPayload.SourceLineNumbers;
+ var inclusions = harvestPayload.Inclusions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ var exclusions = harvestPayload.Exclusions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+
+ var comparer = new WildcardFileComparer();
+
+ var resolvedFiles = Enumerable.Empty();
+
+ var included = this.GetWildcardFiles(inclusions, sourceLineNumbers);
+ var excluded = this.GetWildcardFiles(exclusions, sourceLineNumbers);
+
+ foreach (var excludedFile in excluded)
+ {
+ this.Messaging.Write(OptimizerVerboses.ExcludedFile(sourceLineNumbers, excludedFile.Path));
+ }
+
+ resolvedFiles = included.Except(excluded, comparer).ToList();
+
+ if (!resolvedFiles.Any())
+ {
+ this.Messaging.Write(OptimizerWarnings.ZeroFilesHarvested(sourceLineNumbers));
+ }
+
+ foreach (var payloadByRecursiveDir in resolvedFiles.GroupBy(resolvedFile => resolvedFile.RecursiveDir, resolvedFile => resolvedFile.Path))
+ {
+ var recursiveDir = payloadByRecursiveDir.Key;
+
+ foreach (var file in payloadByRecursiveDir)
+ {
+ if (harvestedPayloads.Add(file))
+ {
+ var name = Path.GetFileName(file);
+
+ var id = this.ParseHelper.CreateIdentifier("pld", harvestPayload.ParentId, recursiveDir.ToUpperInvariant(), name.ToUpperInvariant());
+
+ this.Messaging.Write(OptimizerVerboses.HarvestedFile(sourceLineNumbers, file));
+
+ section.AddSymbol(new WixBundlePayloadSymbol(sourceLineNumbers, id)
+ {
+ Name = Path.Combine(recursiveDir, name),
+ SourceFile = new IntermediateFieldPathValue { Path = file },
+ Compressed = null,
+ UnresolvedSourceFile = file, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
+ });
+
+ if (Enum.TryParse(harvestPayload.ComplexReferenceParentType, out var parentType)
+ && ComplexReferenceParentType.Unknown != parentType && null != harvestPayload.ParentId)
+ {
+ this.ParseHelper.CreateWixGroupSymbol(section, sourceLineNumbers, parentType, harvestPayload.ParentId, ComplexReferenceChildType.Payload, id.Id);
+ }
+ }
+ else
+ {
+ this.Messaging.Write(OptimizerWarnings.SkippingDuplicateFile(sourceLineNumbers, file));
+ }
+ }
+ }
+ }
+
+ private IEnumerable GetWildcardFiles(IEnumerable patterns, SourceLineNumber sourceLineNumbers, string sourcePath = null)
{
- var sourceLineNumbers = harvestFile.SourceLineNumbers;
- var sourcePath = harvestFile.SourcePath?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+ sourcePath = sourcePath?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var files = new List();
@@ -177,7 +246,7 @@ private IEnumerable GetWildcardFiles(HarvestFilesSymbol harvestFil
}
catch (DirectoryNotFoundException e)
{
- this.Messaging.Write(OptimizerWarnings.ExpectedDirectory(harvestFile.SourceLineNumbers, e.Message));
+ this.Messaging.Write(OptimizerWarnings.ExpectedDirectory(sourceLineNumbers, e.Message));
}
}
}
diff --git a/src/wix/WixToolset.Core/Optimizer.cs b/src/wix/WixToolset.Core/Optimizer.cs
index 33f757a31..89b723e84 100644
--- a/src/wix/WixToolset.Core/Optimizer.cs
+++ b/src/wix/WixToolset.Core/Optimizer.cs
@@ -26,7 +26,7 @@ public void Optimize(IOptimizeContext context)
}
{
- var command = new HarvestFilesCommand(context);
+ var command = new HarvestFilesAndPayloadsCommand(context);
command.Execute();
}
diff --git a/src/wix/WixToolset.Core/OptimizerWarnings.cs b/src/wix/WixToolset.Core/OptimizerWarnings.cs
index 784dc587c..616883f45 100644
--- a/src/wix/WixToolset.Core/OptimizerWarnings.cs
+++ b/src/wix/WixToolset.Core/OptimizerWarnings.cs
@@ -8,7 +8,7 @@ internal static class OptimizerWarnings
{
public static Message ZeroFilesHarvested(SourceLineNumber sourceLineNumbers)
{
- return Message(sourceLineNumbers, Ids.ZeroFilesHarvested, "Files inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify your Files paths, inclusions, and exclusions for accuracy.");
+ return Message(sourceLineNumbers, Ids.ZeroFilesHarvested, "Inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify paths, inclusions, and exclusions on Files or Payloads for accuracy.");
}
public static Message ExpectedDirectory(SourceLineNumber sourceLineNumbers, string harvestDirectory)
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs
index 323b5eb0d..30d3aaea1 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs
@@ -9,6 +9,7 @@ namespace WixToolsetTest.CoreIntegration
using WixInternal.TestSupport;
using WixInternal.Core.TestPackage;
using Xunit;
+ using System.Linq;
public class BundleManifestFixture
{
@@ -133,13 +134,13 @@ public void PopulatesBAManifestWithPayloadInformation()
{
{ "WixPayloadProperties", new List { "Size" } },
};
- var payloadElements = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixPayloadProperties", ignoreAttributesByElementName);
+ var payloadElements = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixPayloadProperties", ignoreAttributesByElementName).OrderBy(line => line).ToArray();
WixAssert.CompareLineByLine(new[]
{
- "",
"",
- "",
+ "",
"",
+ "",
}, payloadElements);
}
}
@@ -380,8 +381,8 @@ public void PopulatesManifestWithExePackages()
var exePackageElements = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Chain/burn:ExePackage", ignoreAttributesByElementName);
WixAssert.CompareLineByLine(new[]
{
- "",
- "",
+ "",
+ "",
}, exePackageElements);
}
}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/HarvestFilesFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/HarvestFilesFixture.cs
index b407bb86d..97f3e4425 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/HarvestFilesFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/HarvestFilesFixture.cs
@@ -35,7 +35,7 @@ public void ZeroFilesHarvestedIsAWarning()
var messages = result.Messages.Select(m => FormatMessage(m, sourceFolder, baseFolder)).ToArray();
WixAssert.CompareLineByLine(new[]
{
- "8600: Files inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify your Files paths, inclusions, and exclusions for accuracy.",
+ "8600: Inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify paths, inclusions, and exclusions on Files or Payloads for accuracy.",
}, messages);
});
}
@@ -49,9 +49,9 @@ public void MissingHarvestDirectoryIsAWarning()
WixAssert.CompareLineByLine(new[]
{
@"8601: Missing directory for harvesting files: Could not find a part of the path '\files2\MissingDirectory'.",
- @"8600: Files inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify your Files paths, inclusions, and exclusions for accuracy.",
+ @"8600: Inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify paths, inclusions, and exclusions on Files or Payloads for accuracy.",
@"8601: Missing directory for harvesting files: Could not find a part of the path '\files2\ThisDirectoryIsAlsoMissing'.",
- @"8600: Files inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify your Files paths, inclusions, and exclusions for accuracy.",
+ @"8600: Inclusions and exclusions resulted in zero files harvested. Unless that is expected, you should verify paths, inclusions, and exclusions on Files or Payloads for accuracy.",
}, messages);
});
}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SharedPayloadsBetweenPackages/SharedPayloadsBetweenPackages.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SharedPayloadsBetweenPackages/SharedPayloadsBetweenPackages.wxs
index 55f9d110f..96148c6c0 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SharedPayloadsBetweenPackages/SharedPayloadsBetweenPackages.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SharedPayloadsBetweenPackages/SharedPayloadsBetweenPackages.wxs
@@ -10,9 +10,10 @@
+
-
+
diff --git a/src/xsd/wix.xsd b/src/xsd/wix.xsd
index 8cf810481..d72d8e697 100644
--- a/src/xsd/wix.xsd
+++ b/src/xsd/wix.xsd
@@ -87,6 +87,7 @@
+
@@ -462,6 +463,7 @@
+
@@ -524,6 +526,7 @@
+
@@ -666,6 +669,7 @@
+
@@ -934,6 +938,7 @@
+
@@ -998,6 +1003,7 @@
+
@@ -1045,6 +1051,7 @@
+
@@ -1081,6 +1088,7 @@
+
@@ -1169,6 +1177,7 @@
+
@@ -1930,6 +1939,7 @@
+
@@ -8973,6 +8983,54 @@
+
+
+
+ Authoring to define the set of files to be harvested for inclusion in a bundle.
+
+
+
+
+
+
+
+
+ Extensibility point in the WiX XML Schema. Schema extensions can register additional
+ elements at this point in the schema.
+
+
+
+
+
+
+
+ A file-selection pattern that can include directory names, file names, and wildcards.
+ If a pattern is not an absolute path (via a preprocessor variable, unnamed bind path,
+ or named bind path), it is interpreted as relative to the directory containing the
+ source file. Absolute paths via a named bind path are recommended.
+
+ Wildcards include typical `*.ext` globs and MSBuild-style `**` globs to indicate
+ that directories should be recursed. Examples include:
+
+ | Pattern | Description |
+ | ------- | ----------- |
+ | `!(bindpath.ToBeHarvested)\**` | All files in the parent directory identified by the `ToBeHarvested` bind path and its subdirectories. |
+ | `$(PayloadFiles)\bin\Release\**` | All files in the `bin\Release` subdirectory in the directory named by the `PayloadFiles` preprocessor variable and its subdirectories. |
+ | `!(bindpath.arm64)\**.pdb` | All files with `.pdb` extension in the parent directory identified by the `arm64` bind path and its subdirectories. |
+ | `**` | If an unnamed bind path was specified, all files in that directory and its subdirectories. If an unnamed bind path was _not_ specified, all files in directory of the source .wxs file and its subdirectories. |
+
+
+
+
+
+
+ Extensibility point in the WiX XML Schema. Schema extensions can register additional
+ attributes at this point in the schema.
+
+
+
+
+
@@ -9040,7 +9098,7 @@
- Using wildcards, defines the files from a parent `Files` element that should be excluded from harvesting.
+ Using wildcards, defines the files from a parent `Files` or `Payloads` element that should be excluded from harvesting.
@@ -9057,7 +9115,7 @@
- Excludes files from the set of files harvested via the parent `Files` element.
+ Excludes files from the set of files harvested via the parent `Files` or `Payloads` element.
Inclusion and exclusion wildcards must match paths. For example, if you have a
`Files` element with `Include="!(bindpath.ToBeHarvested)\**"` and want to
exclude one of the files, use an `Exclude` element with a `Files` attribute