aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-03-30 19:46:56 +1000
committerSean Hall <r.sean.hall@gmail.com>2020-03-30 21:57:49 +1000
commit0afd76e4c5d46f237591d860e7d445e267522187 (patch)
tree9f3b648da7733464c83ba6e253a65a18d17f5583 /src
parentd74e3dd4bcc573d0c4b1fb5c36c8bf0115cc21a1 (diff)
downloadwix-0afd76e4c5d46f237591d860e7d445e267522187.tar.gz
wix-0afd76e4c5d46f237591d860e7d445e267522187.tar.bz2
wix-0afd76e4c5d46f237591d860e7d445e267522187.zip
Add DetectSHA2Support "search".
Diffstat (limited to 'src')
-rw-r--r--src/be/UtilBundleExtension.cpp87
-rw-r--r--src/be/UtilBundleExtension.h16
-rw-r--r--src/be/detectsha2support.cpp58
-rw-r--r--src/be/detectsha2support.h8
-rw-r--r--src/be/packages.config1
-rw-r--r--src/be/precomp.h23
-rw-r--r--src/be/utilbe.cpp28
-rw-r--r--src/be/utilbe.vcxproj20
-rw-r--r--src/be/utilsearch.cpp137
-rw-r--r--src/be/utilsearch.h52
-rw-r--r--src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs5
-rw-r--r--src/test/WixToolsetTest.Util/UtilExtensionFixture.cs55
-rw-r--r--src/wixext/Tuples/UtilTupleDefinitions.cs10
-rw-r--r--src/wixext/Tuples/WixDetectSHA2SupportTuple.cs33
-rw-r--r--src/wixext/UtilBinder.cs347
-rw-r--r--src/wixext/UtilCompiler.cs198
-rw-r--r--src/wixext/UtilConstants.cs2
-rw-r--r--src/wixext/UtilErrors.cs6
-rw-r--r--src/wixext/util.xsd24
19 files changed, 609 insertions, 501 deletions
diff --git a/src/be/UtilBundleExtension.cpp b/src/be/UtilBundleExtension.cpp
new file mode 100644
index 00000000..2ac842a5
--- /dev/null
+++ b/src/be/UtilBundleExtension.cpp
@@ -0,0 +1,87 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include "BextBaseBundleExtension.h"
5
6class CWixUtilBundleExtension : public CBextBaseBundleExtension
7{
8public: // IBundleExtension
9 virtual STDMETHODIMP Search(
10 __in LPCWSTR wzId,
11 __in LPCWSTR wzVariable
12 )
13 {
14 HRESULT hr = S_OK;
15
16 hr = UtilSearchExecute(&m_searches, wzId, wzVariable, m_pEngine);
17
18 return hr;
19 }
20
21public: //CBextBaseBundleExtension
22 virtual STDMETHODIMP Initialize(
23 __in const BUNDLE_EXTENSION_CREATE_ARGS* pCreateArgs
24 )
25 {
26 HRESULT hr = S_OK;
27 IXMLDOMDocument* pixdManifest = NULL;
28 IXMLDOMNode* pixnBundleExtension = NULL;
29
30 hr = CBextBaseBundleExtension::Initialize(pCreateArgs);
31 ExitOnFailure(hr, "CBextBaseBundleExtension initialization failed.");
32
33 hr = XmlLoadDocumentFromFile(m_sczBundleExtensionDataPath, &pixdManifest);
34 ExitOnFailure(hr, "Failed to load bundle extension manifest from path: %ls", m_sczBundleExtensionDataPath);
35
36 hr = BextGetBundleExtensionDataNode(pixdManifest, UTIL_BUNDLE_EXTENSION_ID, &pixnBundleExtension);
37 ExitOnFailure(hr, "Failed to get BundleExtension '%ls'", UTIL_BUNDLE_EXTENSION_ID);
38
39 hr = UtilSearchParseFromXml(&m_searches, pixnBundleExtension);
40 ExitOnFailure(hr, "Failed to parse searches from bundle extension manifest.");
41
42 LExit:
43 ReleaseObject(pixnBundleExtension);
44 ReleaseObject(pixdManifest);
45
46 return hr;
47 }
48
49public:
50 CWixUtilBundleExtension(
51 __in IBundleExtensionEngine* pEngine
52 ) : CBextBaseBundleExtension(pEngine)
53 {
54 m_searches = { };
55 }
56
57 ~CWixUtilBundleExtension()
58 {
59 UtilSearchUninitialize(&m_searches);
60 }
61
62private:
63 UTIL_SEARCHES m_searches;
64};
65
66HRESULT UtilBundleExtensionCreate(
67 __in IBundleExtensionEngine* pEngine,
68 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
69 __out IBundleExtension** ppBundleExtension
70 )
71{
72 HRESULT hr = S_OK;
73 CWixUtilBundleExtension* pExtension = NULL;
74
75 pExtension = new CWixUtilBundleExtension(pEngine);
76 ExitOnNull(pExtension, hr, E_OUTOFMEMORY, "Failed to create new CWixUtilBundleExtension.");
77
78 hr = pExtension->Initialize(pArgs);
79 ExitOnFailure(hr, "CWixUtilBundleExtension initialization failed");
80
81 *ppBundleExtension = pExtension;
82 pExtension = NULL;
83
84LExit:
85 ReleaseObject(pExtension);
86 return hr;
87}
diff --git a/src/be/UtilBundleExtension.h b/src/be/UtilBundleExtension.h
new file mode 100644
index 00000000..16c5b346
--- /dev/null
+++ b/src/be/UtilBundleExtension.h
@@ -0,0 +1,16 @@
1#pragma once
2// 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.
3
4
5// constants
6
7#define UTIL_BUNDLE_EXTENSION_ID L"WixUtilBundleExtension"
8
9
10// function declarations
11
12HRESULT UtilBundleExtensionCreate(
13 __in IBundleExtensionEngine* pEngine,
14 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
15 __out IBundleExtension** ppBundleExtension
16 );
diff --git a/src/be/detectsha2support.cpp b/src/be/detectsha2support.cpp
new file mode 100644
index 00000000..f1f3637e
--- /dev/null
+++ b/src/be/detectsha2support.cpp
@@ -0,0 +1,58 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// https://gist.github.com/navossoc/7572c7d82243e9f818989e2765e7793a
6HRESULT DetectSHA2Support(
7 __out BOOL* pfSupported
8 )
9{
10 HRESULT hr = S_OK;
11 HMODULE hModule = NULL;
12 FARPROC pfn = NULL;
13 DWORD er = ERROR_SUCCESS;
14
15 hr = LoadSystemLibrary(L"wintrust.dll", &hModule);
16 ExitOnFailure(hr, "Failed to load wintrust.dll");
17
18 pfn = ::GetProcAddress(hModule, "CryptCATAdminAcquireContext2");
19 if (pfn)
20 {
21 *pfSupported = TRUE;
22 ExitFunction1(hr = S_OK);
23 }
24
25 er = ::GetLastError();
26 if (er == ERROR_PROC_NOT_FOUND)
27 {
28 *pfSupported = FALSE;
29 ExitFunction1(hr = S_OK);
30 }
31
32 hr = HRESULT_FROM_WIN32(er);
33 ExitOnFailure(hr, "Failed to probe for CryptCATAdminAcquireContext2 in wintrust.dll");
34
35LExit:
36 ::FreeLibrary(hModule);
37
38 return hr;
39}
40
41HRESULT UtilPerformDetectSHA2Support(
42 __in LPCWSTR wzVariable,
43 __in UTIL_SEARCH* /*pSearch*/,
44 __in IBundleExtensionEngine* pEngine
45 )
46{
47 HRESULT hr = S_OK;
48 BOOL fSupported = FALSE;
49
50 hr = DetectSHA2Support(&fSupported);
51 ExitOnFailure(hr, "DetectSHA2Support failed.");
52
53 hr = pEngine->SetVariableNumeric(wzVariable, fSupported ? 1 : 0);
54 ExitOnFailure(hr, "Failed to set variable '%ls'", wzVariable);
55
56LExit:
57 return hr;
58}
diff --git a/src/be/detectsha2support.h b/src/be/detectsha2support.h
new file mode 100644
index 00000000..7f1f6031
--- /dev/null
+++ b/src/be/detectsha2support.h
@@ -0,0 +1,8 @@
1#pragma once
2// 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.
3
4HRESULT UtilPerformDetectSHA2Support(
5 __in LPCWSTR wzVariable,
6 __in UTIL_SEARCH* pSearch,
7 __in IBundleExtensionEngine* pEngine
8 ); \ No newline at end of file
diff --git a/src/be/packages.config b/src/be/packages.config
index f05546c5..ca23641f 100644
--- a/src/be/packages.config
+++ b/src/be/packages.config
@@ -1,5 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<packages> 2<packages>
3 <package id="WixToolset.BextUtil" version="4.0.16" targetFramework="native" />
3 <package id="WixToolset.BootstrapperCore.Native" version="4.0.13" targetFramework="native" /> 4 <package id="WixToolset.BootstrapperCore.Native" version="4.0.13" targetFramework="native" />
4 <package id="WixToolset.DUtil" version="4.0.18" targetFramework="native" /> 5 <package id="WixToolset.DUtil" version="4.0.18" targetFramework="native" />
5</packages> \ No newline at end of file 6</packages> \ No newline at end of file
diff --git a/src/be/precomp.h b/src/be/precomp.h
index 9dc3c468..a4ab7abf 100644
--- a/src/be/precomp.h
+++ b/src/be/precomp.h
@@ -17,9 +17,20 @@
17 17
18#define MAXUINT USHRT_MAX 18#define MAXUINT USHRT_MAX
19 19
20#include "dutil.h" 20#include <dutil.h>
21#include "memutil.h" 21#include <memutil.h>
22 22#include <strutil.h>
23#include "BootstrapperEngine.h" 23#include <pathutil.h>
24#include "BundleExtensionEngine.h" 24#include <xmlutil.h>
25#include "BundleExtension.h" 25
26#include <BundleExtensionEngine.h>
27#include <BundleExtension.h>
28
29#include <IBundleExtensionEngine.h>
30#include <IBundleExtension.h>
31#include <bextutil.h>
32#include <BextBundleExtensionEngine.h>
33
34#include "utilsearch.h"
35#include "detectsha2support.h"
36#include "UtilBundleExtension.h"
diff --git a/src/be/utilbe.cpp b/src/be/utilbe.cpp
index 370669dd..d9816dc7 100644
--- a/src/be/utilbe.cpp
+++ b/src/be/utilbe.cpp
@@ -1,19 +1,41 @@
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
3#include "precomp.h" 3#include "precomp.h"
4#include "BextBaseBundleExtensionProc.h"
5
6static IBundleExtension* vpBundleExtension = NULL;
4 7
5// function definitions 8// function definitions
6 9
7extern "C" HRESULT WINAPI BundleExtensionCreate( 10extern "C" HRESULT WINAPI BundleExtensionCreate(
8 __in const BUNDLE_EXTENSION_CREATE_ARGS* /*pArgs*/, 11 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
9 __inout BUNDLE_EXTENSION_CREATE_RESULTS* /*pResults*/ 12 __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults
10 ) 13 )
11{ 14{
12 HRESULT hr = S_OK; 15 HRESULT hr = S_OK;
16 IBundleExtensionEngine* pEngine = NULL;
17
18 hr = XmlInitialize();
19 ExitOnFailure(hr, "Failed to initialize XML.");
20
21 hr = BextInitializeFromCreateArgs(pArgs, &pEngine);
22 ExitOnFailure(hr, "Failed to initialize bext");
23
24 hr = UtilBundleExtensionCreate(pEngine, pArgs, &vpBundleExtension);
25 BextExitOnFailure(hr, "Failed to create WixUtilBundleExtension");
26
27 pResults->pfnBundleExtensionProc = BextBaseBundleExtensionProc;
28 pResults->pvBundleExtensionProcContext = vpBundleExtension;
29
30LExit:
31 ReleaseObject(pEngine);
13 32
14 return hr; 33 return hr;
15} 34}
16 35
17extern "C" void WINAPI BundleExtensionDestroy() 36extern "C" void WINAPI BundleExtensionDestroy()
18{ 37{
38 BextUninitialize();
39 ReleaseNullObject(vpBundleExtension);
40 XmlUninitialize();
19} \ No newline at end of file 41} \ No newline at end of file
diff --git a/src/be/utilbe.vcxproj b/src/be/utilbe.vcxproj
index 963eac7f..e64d113e 100644
--- a/src/be/utilbe.vcxproj
+++ b/src/be/utilbe.vcxproj
@@ -1,10 +1,9 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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<!-- 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. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 3<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <Import Project="..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props" Condition="Exists('..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props')" />
5 <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" /> 5 <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" />
6 <Import Project="..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" /> 6 <Import Project="..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" />
7
8 <ItemGroup Label="ProjectConfigurations"> 7 <ItemGroup Label="ProjectConfigurations">
9 <ProjectConfiguration Include="Debug|Win32"> 8 <ProjectConfiguration Include="Debug|Win32">
10 <Configuration>Debug</Configuration> 9 <Configuration>Debug</Configuration>
@@ -15,7 +14,6 @@
15 <Platform>Win32</Platform> 14 <Platform>Win32</Platform>
16 </ProjectConfiguration> 15 </ProjectConfiguration>
17 </ItemGroup> 16 </ItemGroup>
18
19 <PropertyGroup Label="Globals"> 17 <PropertyGroup Label="Globals">
20 <ProjectGuid>{630C1EE7-2517-4A8C-83E3-DA1150308B58}</ProjectGuid> 18 <ProjectGuid>{630C1EE7-2517-4A8C-83E3-DA1150308B58}</ProjectGuid>
21 <ConfigurationType>DynamicLibrary</ConfigurationType> 19 <ConfigurationType>DynamicLibrary</ConfigurationType>
@@ -25,37 +23,37 @@
25 <ProjectModuleDefinitionFile>utilbe.def</ProjectModuleDefinitionFile> 23 <ProjectModuleDefinitionFile>utilbe.def</ProjectModuleDefinitionFile>
26 <Description>WiX Toolset Util BundleExtension</Description> 24 <Description>WiX Toolset Util BundleExtension</Description>
27 </PropertyGroup> 25 </PropertyGroup>
28
29 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> 26 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
30 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> 27 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
31
32 <PropertyGroup> 28 <PropertyGroup>
33 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries> 29 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries>
34 </PropertyGroup> 30 </PropertyGroup>
35
36 <ItemGroup> 31 <ItemGroup>
32 <ClCompile Include="detectsha2support.cpp" />
37 <ClCompile Include="precomp.cpp"> 33 <ClCompile Include="precomp.cpp">
38 <PrecompiledHeader>Create</PrecompiledHeader> 34 <PrecompiledHeader>Create</PrecompiledHeader>
39 </ClCompile> 35 </ClCompile>
40 <ClCompile Include="utilbe.cpp" /> 36 <ClCompile Include="utilbe.cpp" />
37 <ClCompile Include="UtilBundleExtension.cpp" />
38 <ClCompile Include="utilsearch.cpp" />
41 </ItemGroup> 39 </ItemGroup>
42
43 <ItemGroup> 40 <ItemGroup>
41 <ClInclude Include="detectsha2support.h" />
44 <ClInclude Include="precomp.h" /> 42 <ClInclude Include="precomp.h" />
43 <ClInclude Include="UtilBundleExtension.h" />
44 <ClInclude Include="utilsearch.h" />
45 </ItemGroup> 45 </ItemGroup>
46
47 <ItemGroup> 46 <ItemGroup>
48 <None Include="packages.config" /> 47 <None Include="packages.config" />
49 <None Include="utilbe.def" /> 48 <None Include="utilbe.def" />
50 </ItemGroup> 49 </ItemGroup>
51
52 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> 50 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
53
54 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> 51 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
55 <PropertyGroup> 52 <PropertyGroup>
56 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> 53 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
57 </PropertyGroup> 54 </PropertyGroup>
55 <Error Condition="!Exists('..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props'))" />
58 <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props'))" /> 56 <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props'))" />
59 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props'))" /> 57 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props'))" />
60 </Target> 58 </Target>
61</Project> 59</Project> \ No newline at end of file
diff --git a/src/be/utilsearch.cpp b/src/be/utilsearch.cpp
new file mode 100644
index 00000000..4e9d86a1
--- /dev/null
+++ b/src/be/utilsearch.cpp
@@ -0,0 +1,137 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6STDMETHODIMP UtilSearchParseFromXml(
7 __in UTIL_SEARCHES* pSearches,
8 __in IXMLDOMNode* pixnBundleExtension
9 )
10{
11 HRESULT hr = S_OK;
12 IXMLDOMNodeList* pixnNodes = NULL;
13 IXMLDOMNode* pixnNode = NULL;
14 DWORD cNodes = 0;
15 BSTR bstrNodeName = NULL;
16
17 // Select Util search nodes.
18 hr = XmlSelectNodes(pixnBundleExtension, L"WixDetectSHA2Support", &pixnNodes);
19 ExitOnFailure(hr, "Failed to select Util search nodes.");
20
21 // Get Util search node count.
22 hr = pixnNodes->get_length((long*)&cNodes);
23 ExitOnFailure(hr, "Failed to get Util search node count.");
24
25 if (!cNodes)
26 {
27 ExitFunction();
28 }
29
30 // Allocate memory for searches.
31 pSearches->rgSearches = (UTIL_SEARCH*)MemAlloc(sizeof(UTIL_SEARCH) * cNodes, TRUE);
32 ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
33
34 pSearches->cSearches = cNodes;
35
36 // Parse search elements.
37 for (DWORD i = 0; i < cNodes; ++i)
38 {
39 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
40
41 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
42 ExitOnFailure(hr, "Failed to get next node.");
43
44 // @Id
45 hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczId);
46 ExitOnFailure(hr, "Failed to get @Id.");
47
48 // Read type specific attributes.
49 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"WixDetectSHA2Support", -1))
50 {
51 pSearch->Type = UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT;
52 }
53 else
54 {
55 hr = E_UNEXPECTED;
56 ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
57 }
58
59 // prepare next iteration
60 ReleaseNullObject(pixnNode);
61 ReleaseNullBSTR(bstrNodeName);
62 }
63
64LExit:
65 ReleaseBSTR(bstrNodeName);
66 ReleaseObject(pixnNode);
67 ReleaseObject(pixnNodes);
68
69 return hr;
70}
71
72void UtilSearchUninitialize(
73 __in UTIL_SEARCHES* pSearches
74 )
75{
76 if (pSearches->rgSearches)
77 {
78 for (DWORD i = 0; i < pSearches->cSearches; ++i)
79 {
80 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
81
82 ReleaseStr(pSearch->sczId);
83 }
84 MemFree(pSearches->rgSearches);
85 }
86}
87
88STDMETHODIMP UtilSearchExecute(
89 __in UTIL_SEARCHES* pSearches,
90 __in LPCWSTR wzSearchId,
91 __in LPCWSTR wzVariable,
92 __in IBundleExtensionEngine* pEngine
93 )
94{
95 HRESULT hr = S_OK;
96 UTIL_SEARCH* pSearch = NULL;
97
98 hr = UtilSearchFindById(pSearches, wzSearchId, &pSearch);
99 ExitOnFailure(hr, "Search id '%ls' is unknown to the util extension.");
100
101 switch (pSearch->Type)
102 {
103 case UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT:
104 hr = UtilPerformDetectSHA2Support(wzVariable, pSearch, pEngine);
105 break;
106 default:
107 hr = E_UNEXPECTED;
108 }
109
110LExit:
111 return hr;
112}
113
114STDMETHODIMP UtilSearchFindById(
115 __in UTIL_SEARCHES* pSearches,
116 __in LPCWSTR wzId,
117 __out UTIL_SEARCH** ppSearch
118 )
119{
120 HRESULT hr = S_OK;
121
122 for (DWORD i = 0; i < pSearches->cSearches; ++i)
123 {
124 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
125
126 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pSearch->sczId, -1, wzId, -1))
127 {
128 *ppSearch = pSearch;
129 ExitFunction1(hr = S_OK);
130 }
131 }
132
133 hr = E_NOTFOUND;
134
135LExit:
136 return hr;
137}
diff --git a/src/be/utilsearch.h b/src/be/utilsearch.h
new file mode 100644
index 00000000..1e0ca96d
--- /dev/null
+++ b/src/be/utilsearch.h
@@ -0,0 +1,52 @@
1#pragma once
2// 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.
3
4
5// constants
6
7enum UTIL_SEARCH_TYPE
8{
9 UTIL_SEARCH_TYPE_NONE,
10 UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT,
11};
12
13
14// structs
15
16typedef struct _UTIL_SEARCH
17{
18 LPWSTR sczId;
19
20 UTIL_SEARCH_TYPE Type;
21} UTIL_SEARCH;
22
23typedef struct _UTIL_SEARCHES
24{
25 UTIL_SEARCH* rgSearches;
26 DWORD cSearches;
27} UTIL_SEARCHES;
28
29
30// function declarations
31
32STDMETHODIMP UtilSearchParseFromXml(
33 __in UTIL_SEARCHES* pSearches,
34 __in IXMLDOMNode* pixnBundleExtension
35 );
36
37void UtilSearchUninitialize(
38 __in UTIL_SEARCHES* pSearches
39 );
40
41STDMETHODIMP UtilSearchExecute(
42 __in UTIL_SEARCHES* pSearches,
43 __in LPCWSTR wzSearchId,
44 __in LPCWSTR wzVariable,
45 __in IBundleExtensionEngine* pEngine
46 );
47
48STDMETHODIMP UtilSearchFindById(
49 __in UTIL_SEARCHES* pSearches,
50 __in LPCWSTR wzId,
51 __out UTIL_SEARCH** ppSearch
52 );
diff --git a/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs b/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs
index cafd4255..82646b37 100644
--- a/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs
+++ b/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs
@@ -7,6 +7,7 @@
7 <util:RegistrySearchRef Id="RegistrySearchId" /> 7 <util:RegistrySearchRef Id="RegistrySearchId" />
8 <util:ProductSearchRef Id="ProductSearchId" /> 8 <util:ProductSearchRef Id="ProductSearchId" />
9 <util:FileSearchRef Id="FileSearchId" /> 9 <util:FileSearchRef Id="FileSearchId" />
10 <util:DetectSHA2SupportRef Id="DetectSHA2SupportId" />
10 11
11 <Chain> 12 <Chain>
12 <MsiPackage SourceFile="test.msi"> 13 <MsiPackage SourceFile="test.msi">
@@ -32,4 +33,8 @@
32 <Fragment> 33 <Fragment>
33 <util:FileSearch Id="FileSearchId" Variable="FileSearchVariable" Path="%windir%\System32\mscoree.dll" Result="exists" /> 34 <util:FileSearch Id="FileSearchId" Variable="FileSearchVariable" Path="%windir%\System32\mscoree.dll" Result="exists" />
34 </Fragment> 35 </Fragment>
36
37 <Fragment>
38 <util:DetectSHA2Support Id="DetectSHA2SupportId" Variable="IsSHA2Supported" />
39 </Fragment>
35</Wix> 40</Wix>
diff --git a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs
index 8f33eff5..c5b370ac 100644
--- a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs
+++ b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs
@@ -146,6 +146,9 @@ namespace WixToolsetTest.Util
146 { 146 {
147 var baseFolder = fs.GetFolder(); 147 var baseFolder = fs.GetFolder();
148 var intermediateFolder = Path.Combine(baseFolder, "obj"); 148 var intermediateFolder = Path.Combine(baseFolder, "obj");
149 var bundlePath = Path.Combine(baseFolder, @"bin\test.exe");
150 var baFolderPath = Path.Combine(baseFolder, "ba");
151 var extractFolderPath = Path.Combine(baseFolder, "extract");
149 152
150 var result = WixRunner.Execute(new[] 153 var result = WixRunner.Execute(new[]
151 { 154 {
@@ -156,45 +159,35 @@ namespace WixToolsetTest.Util
156 "-bindpath", Path.Combine(folder, "data"), 159 "-bindpath", Path.Combine(folder, "data"),
157 "-intermediateFolder", intermediateFolder, 160 "-intermediateFolder", intermediateFolder,
158 "-burnStub", burnStubPath, 161 "-burnStub", burnStubPath,
159 "-o", Path.Combine(baseFolder, @"bin\test.exe") 162 "-o", bundlePath
160 }); 163 });
161 164
162 result.AssertSuccess(); 165 result.AssertSuccess();
163 166
164 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.exe"))); 167 Assert.True(File.Exists(bundlePath));
165#if TODO 168#if TODO
166 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb"))); 169 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
167#endif 170#endif
168 171
169 var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"test.wir")); 172 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
170 var section = intermediate.Sections.Single(); 173 extractResult.AssertSuccess();
171 174
172 var searchTuples = section.Tuples.OfType<WixSearchTuple>().OrderBy(t => t.Id.Id).ToList(); 175 var bundleExtensionDatas = extractResult.SelectBundleExtensionDataNodes("/be:BundleExtensionData/be:BundleExtension[@Id='WixUtilBundleExtension']");
173 Assert.Equal(3, searchTuples.Count); 176 Assert.Equal(1, bundleExtensionDatas.Count);
174 Assert.Equal("FileSearchId", searchTuples[0].Id.Id); 177 Assert.Equal("<BundleExtension Id='WixUtilBundleExtension'>" +
175 Assert.Equal("FileSearchVariable", searchTuples[0].Variable); 178 "<WixDetectSHA2Support Id='DetectSHA2SupportId' />" +
176 Assert.Equal("ProductSearchId", searchTuples[1].Id.Id); 179 "</BundleExtension>", bundleExtensionDatas[0].GetTestXml());
177 Assert.Equal("ProductSearchVariable", searchTuples[1].Variable); 180
178 Assert.Equal("1 & 2 < 3", searchTuples[1].Condition); 181 var utilSearches = extractResult.SelectManifestNodes("/burn:BurnManifest/*[self::burn:ExtensionSearch or self::burn:FileSearch or self::burn:MsiProductSearch or self::burn:RegistrySearch]");
179 Assert.Equal("RegistrySearchId", searchTuples[2].Id.Id); 182 Assert.Equal(4, utilSearches.Count);
180 Assert.Equal("RegistrySearchVariable", searchTuples[2].Variable); 183 Assert.Equal("<ExtensionSearch Id='DetectSHA2SupportId' Variable='IsSHA2Supported' " +
181 184 "ExtensionId='WixUtilBundleExtension' />", utilSearches[0].GetTestXml());
182 var fileSearchTuple = section.Tuples.OfType<WixFileSearchTuple>().Single(); 185 Assert.Equal("<FileSearch Id='FileSearchId' Variable='FileSearchVariable' " +
183 Assert.Equal("FileSearchId", fileSearchTuple.Id.Id); 186 $@"Path='%windir%\System32\mscoree.dll' Type='exists' />", utilSearches[1].GetTestXml());
184 Assert.Equal(@"%windir%\System32\mscoree.dll", fileSearchTuple.Path); 187 Assert.Equal("<MsiProductSearch Id='ProductSearchId' Variable='ProductSearchVariable' Condition='1 &amp; 2 &lt; 3' " +
185 Assert.Equal(WixFileSearchAttributes.Default | WixFileSearchAttributes.WantExists, fileSearchTuple.Attributes); 188 "UpgradeCode='{738D02BF-E231-4370-8209-E9FD4E1BE2A1}' Type='version' />", utilSearches[2].GetTestXml());
186 189 Assert.Equal("<RegistrySearch Id='RegistrySearchId' Variable='RegistrySearchVariable' " +
187 var productSearchTuple = section.Tuples.OfType<WixProductSearchTuple>().Single(); 190 @"Root='HKLM' Key='SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' Value='Release' Type='value' VariableType='string' />", utilSearches[3].GetTestXml());
188 Assert.Equal("ProductSearchId", productSearchTuple.Id.Id);
189 Assert.Equal("{738D02BF-E231-4370-8209-E9FD4E1BE2A1}", productSearchTuple.Guid);
190 Assert.Equal(WixProductSearchAttributes.Version | WixProductSearchAttributes.UpgradeCode, productSearchTuple.Attributes);
191
192 var registrySearchTuple = section.Tuples.OfType<WixRegistrySearchTuple>().Single();
193 Assert.Equal("RegistrySearchId", registrySearchTuple.Id.Id);
194 Assert.Equal(RegistryRootType.LocalMachine, registrySearchTuple.Root);
195 Assert.Equal(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full", registrySearchTuple.Key);
196 Assert.Equal("Release", registrySearchTuple.Value);
197 Assert.Equal(WixRegistrySearchAttributes.WantValue | WixRegistrySearchAttributes.Raw, registrySearchTuple.Attributes);
198 } 191 }
199 } 192 }
200 193
diff --git a/src/wixext/Tuples/UtilTupleDefinitions.cs b/src/wixext/Tuples/UtilTupleDefinitions.cs
index 00c98337..ece64502 100644
--- a/src/wixext/Tuples/UtilTupleDefinitions.cs
+++ b/src/wixext/Tuples/UtilTupleDefinitions.cs
@@ -4,6 +4,7 @@ namespace WixToolset.Util
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Data; 6 using WixToolset.Data;
7 using WixToolset.Data.Burn;
7 8
8 public enum UtilTupleDefinitionType 9 public enum UtilTupleDefinitionType
9 { 10 {
@@ -19,6 +20,7 @@ namespace WixToolset.Util
19 User, 20 User,
20 UserGroup, 21 UserGroup,
21 WixCloseApplication, 22 WixCloseApplication,
23 WixDetectSHA2Support,
22 WixFormatFiles, 24 WixFormatFiles,
23 WixInternetShortcut, 25 WixInternetShortcut,
24 WixRemoveFolderEx, 26 WixRemoveFolderEx,
@@ -82,6 +84,9 @@ namespace WixToolset.Util
82 case UtilTupleDefinitionType.WixCloseApplication: 84 case UtilTupleDefinitionType.WixCloseApplication:
83 return UtilTupleDefinitions.WixCloseApplication; 85 return UtilTupleDefinitions.WixCloseApplication;
84 86
87 case UtilTupleDefinitionType.WixDetectSHA2Support:
88 return UtilTupleDefinitions.WixDetectSHA2Support;
89
85 case UtilTupleDefinitionType.WixFormatFiles: 90 case UtilTupleDefinitionType.WixFormatFiles:
86 return UtilTupleDefinitions.WixFormatFiles; 91 return UtilTupleDefinitions.WixFormatFiles;
87 92
@@ -107,5 +112,10 @@ namespace WixToolset.Util
107 throw new ArgumentOutOfRangeException(nameof(type)); 112 throw new ArgumentOutOfRangeException(nameof(type));
108 } 113 }
109 } 114 }
115
116 static UtilTupleDefinitions()
117 {
118 WixDetectSHA2Support.AddTag(BurnConstants.BundleExtensionSearchTupleDefinitionTag);
119 }
110 } 120 }
111} 121}
diff --git a/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs b/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs
new file mode 100644
index 00000000..38b76ff2
--- /dev/null
+++ b/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs
@@ -0,0 +1,33 @@
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.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Tuples;
7
8 public static partial class UtilTupleDefinitions
9 {
10 public static readonly IntermediateTupleDefinition WixDetectSHA2Support = new IntermediateTupleDefinition(
11 UtilTupleDefinitionType.WixDetectSHA2Support.ToString(),
12 new IntermediateFieldDefinition[0],
13 typeof(WixDetectSHA2SupportTuple));
14 }
15}
16
17namespace WixToolset.Util.Tuples
18{
19 using WixToolset.Data;
20
21 public class WixDetectSHA2SupportTuple : IntermediateTuple
22 {
23 public WixDetectSHA2SupportTuple() : base(UtilTupleDefinitions.WixDetectSHA2Support, null, null)
24 {
25 }
26
27 public WixDetectSHA2SupportTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilTupleDefinitions.WixDetectSHA2Support, sourceLineNumber, id)
28 {
29 }
30
31 public IntermediateField this[GroupTupleFields index] => this.Fields[(int)index];
32 }
33}
diff --git a/src/wixext/UtilBinder.cs b/src/wixext/UtilBinder.cs
deleted file mode 100644
index ef80a876..00000000
--- a/src/wixext/UtilBinder.cs
+++ /dev/null
@@ -1,347 +0,0 @@
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.Extensions
4{
5#if TODO_BRINGBACK_FOR_BUNDLES
6 using System;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using WixToolset.Data;
10 using WixToolset.Extensibility;
11
12 /// <summary>
13 /// The binder for the WiX Toolset Utility Extension.
14 /// </summary>
15 public sealed class UtilBinder : BinderExtension
16 {
17 // TODO: When WixSearch is supported in Product, etc, we may need to call
18 // ReorderWixSearch() from each of those initializers.
19
20 // TODO: A general-purpose "reorder this table given these constraints"
21 // mechanism may end up being helpful. This could be declaratively stated
22 // in the table definitions, or exposed from the core Wix.dll and called
23 // as-needed by any extensions.
24
25 /// <summary>
26 /// Called before bundle binding occurs.
27 /// </summary>
28 public override void Initialize(Output bundle)
29 {
30 if (OutputType.Bundle == bundle.Type)
31 {
32 this.ReorderWixSearch(bundle);
33 }
34 }
35
36 /// <summary>
37 /// Reorders Any WixSearch items.
38 /// </summary>
39 /// <param name="output">Output containing the tables to process.</param>
40 private void ReorderWixSearch(Output output)
41 {
42 Table wixSearchTable = output.Tables["WixSearch"];
43 if (null == wixSearchTable || wixSearchTable.Rows.Count == 0)
44 {
45 // nothing to do!
46 return;
47 }
48
49 RowDictionary rowDictionary = new RowDictionary();
50 foreach (Row row in wixSearchTable.Rows)
51 {
52 rowDictionary.AddRow(row);
53 }
54
55 Constraints constraints = new Constraints();
56 Table wixSearchRelationTable = output.Tables["WixSearchRelation"];
57 if (null != wixSearchRelationTable && wixSearchRelationTable.Rows.Count > 0)
58 {
59 // add relational info to our data...
60 foreach (Row row in wixSearchRelationTable.Rows)
61 {
62 constraints.AddConstraint((string)row[0], (string)row[1]);
63 }
64 }
65
66 this.FindCircularReference(constraints);
67
68 if (this.Core.EncounteredError)
69 {
70 return;
71 }
72
73 this.FlattenDependentReferences(constraints);
74
75 // Reorder by topographical sort (http://en.wikipedia.org/wiki/Topological_sorting)
76 // We use a variation of Kahn (1962) algorithm as described in
77 // Wikipedia, with the additional criteria that start nodes are sorted
78 // lexicographically at each step to ensure a deterministic ordering
79 // based on 'after' dependencies and ID.
80 TopologicalSort sorter = new TopologicalSort();
81 List <string> sortedIds = sorter.Sort(rowDictionary.Keys, constraints);
82
83 // Now, re-write the table with the searches in order...
84 wixSearchTable.Rows.Clear();
85 foreach (string id in sortedIds)
86 {
87 wixSearchTable.Rows.Add(rowDictionary[id]);
88 }
89 }
90
91 /// <summary>
92 /// A dictionary of Row items, indexed by their first column.
93 /// </summary>
94 private class RowDictionary : Dictionary<string, Row>
95 {
96 public void AddRow(Row row)
97 {
98 this.Add((string)row[0], row);
99 }
100
101 // TODO: Hide other Add methods?
102 }
103
104 /// <summary>
105 /// A dictionary of constraints, mapping an id to a list of ids.
106 /// </summary>
107 private class Constraints : Dictionary<string, List<string>>
108 {
109 public void AddConstraint(string id, string afterId)
110 {
111 if (!this.ContainsKey(id))
112 {
113 this.Add(id, new List<string>());
114 }
115
116 // TODO: Show warning if a constraint is seen twice?
117 if (!this[id].Contains(afterId))
118 {
119 this[id].Add(afterId);
120 }
121 }
122
123 // TODO: Hide other Add methods?
124 }
125
126 /// <summary>
127 /// Finds circular references in the constraints.
128 /// </summary>
129 /// <param name="constraints">Constraints to check.</param>
130 /// <remarks>This is not particularly performant, but it works.</remarks>
131 private void FindCircularReference(Constraints constraints)
132 {
133 foreach (string id in constraints.Keys)
134 {
135 List<string> seenIds = new List<string>();
136 string chain = null;
137 if (FindCircularReference(constraints, id, id, seenIds, out chain))
138 {
139 // We will show a separate message for every ID that's in
140 // the loop. We could bail after the first one, but then
141 // we wouldn't catch disjoint loops in a single run.
142 this.Core.OnMessage(UtilErrors.CircularSearchReference(chain));
143 }
144 }
145 }
146
147 /// <summary>
148 /// Recursive function that finds circular references in the constraints.
149 /// </summary>
150 /// <param name="constraints">Constraints to check.</param>
151 /// <param name="checkId">The identifier currently being looking for. (Fixed across a given run.)</param>
152 /// <param name="currentId">The idenifier curently being tested.</param>
153 /// <param name="seenIds">A list of identifiers seen, to ensure each identifier is only expanded once.</param>
154 /// <param name="chain">If a circular reference is found, will contain the chain of references.</param>
155 /// <returns>True if a circular reference is found, false otherwise.</returns>
156 private bool FindCircularReference(Constraints constraints, string checkId, string currentId, List<string> seenIds, out string chain)
157 {
158 chain = null;
159 List<string> afterList = null;
160 if (constraints.TryGetValue(currentId, out afterList))
161 {
162 foreach (string afterId in afterList)
163 {
164 if (afterId == checkId)
165 {
166 chain = String.Format(CultureInfo.InvariantCulture, "{0} -> {1}", currentId, afterId);
167 return true;
168 }
169
170 if (!seenIds.Contains(afterId))
171 {
172 seenIds.Add(afterId);
173 if (FindCircularReference(constraints, checkId, afterId, seenIds, out chain))
174 {
175 chain = String.Format(CultureInfo.InvariantCulture, "{0} -> {1}", currentId, chain);
176 return true;
177 }
178 }
179 }
180 }
181
182 return false;
183 }
184
185 /// <summary>
186 /// Flattens any dependency chains to simplify reordering.
187 /// </summary>
188 /// <param name="constraints"></param>
189 private void FlattenDependentReferences(Constraints constraints)
190 {
191 foreach (string id in constraints.Keys)
192 {
193 List<string> flattenedIds = new List<string>();
194 AddDependentReferences(constraints, id, flattenedIds);
195 List<string> constraintList = constraints[id];
196 foreach (string flattenedId in flattenedIds)
197 {
198 if (!constraintList.Contains(flattenedId))
199 {
200 constraintList.Add(flattenedId);
201 }
202 }
203 }
204 }
205
206 /// <summary>
207 /// Adds dependent references to a list.
208 /// </summary>
209 /// <param name="constraints"></param>
210 /// <param name="currentId"></param>
211 /// <param name="seenIds"></param>
212 private void AddDependentReferences(Constraints constraints, string currentId, List<string> seenIds)
213 {
214 List<string> afterList = null;
215 if (constraints.TryGetValue(currentId, out afterList))
216 {
217 foreach (string afterId in afterList)
218 {
219 if (!seenIds.Contains(afterId))
220 {
221 seenIds.Add(afterId);
222 AddDependentReferences(constraints, afterId, seenIds);
223 }
224 }
225 }
226 }
227
228 /// <summary>
229 /// Reorder by topological sort
230 /// </summary>
231 /// <remarks>
232 /// We use a variation of Kahn (1962) algorithm as described in
233 /// Wikipedia (http://en.wikipedia.org/wiki/Topological_sorting), with
234 /// the additional criteria that start nodes are sorted lexicographically
235 /// at each step to ensure a deterministic ordering based on 'after'
236 /// dependencies and ID.
237 /// </remarks>
238 private class TopologicalSort
239 {
240 private List<string> startIds = new List<string>();
241 private Constraints constraints;
242
243 /// <summary>
244 /// Reorder by topological sort
245 /// </summary>
246 /// <param name="allIds">The complete list of IDs.</param>
247 /// <param name="constraints">Constraints to use.</param>
248 /// <returns>The topologically sorted list of IDs.</returns>
249 internal List<string> Sort(IEnumerable<string> allIds, Constraints constraints)
250 {
251 this.startIds.Clear();
252 this.CopyConstraints(constraints);
253
254 this.FindInitialStartIds(allIds);
255
256 // We always create a new sortedId list, because we return it
257 // to the caller and don't know what its lifetime may be.
258 List<string> sortedIds = new List<string>();
259
260 while (this.startIds.Count > 0)
261 {
262 this.SortStartIds();
263
264 string currentId = this.startIds[0];
265 sortedIds.Add(currentId);
266 this.startIds.RemoveAt(0);
267
268 this.ResolveConstraint(currentId);
269 }
270
271 return sortedIds;
272 }
273
274 /// <summary>
275 /// Copies a Constraints set (to prevent modifying the incoming data).
276 /// </summary>
277 /// <param name="constraints">Constraints to copy.</param>
278 private void CopyConstraints(Constraints constraints)
279 {
280 this.constraints = new Constraints();
281 foreach (string id in constraints.Keys)
282 {
283 foreach (string afterId in constraints[id])
284 {
285 this.constraints.AddConstraint(id, afterId);
286 }
287 }
288 }
289
290 /// <summary>
291 /// Finds initial start IDs. (Those with no constraints.)
292 /// </summary>
293 /// <param name="allIds">The complete list of IDs.</param>
294 private void FindInitialStartIds(IEnumerable<string> allIds)
295 {
296 foreach (string id in allIds)
297 {
298 if (!this.constraints.ContainsKey(id))
299 {
300 this.startIds.Add(id);
301 }
302 }
303 }
304
305 /// <summary>
306 /// Sorts start IDs.
307 /// </summary>
308 private void SortStartIds()
309 {
310 this.startIds.Sort();
311 }
312
313 /// <summary>
314 /// Removes the resolved constraint and updates the list of startIds
315 /// with any now-valid (all constraints resolved) IDs.
316 /// </summary>
317 /// <param name="resolvedId">The ID to resolve from the set of constraints.</param>
318 private void ResolveConstraint(string resolvedId)
319 {
320 List<string> newStartIds = new List<string>();
321
322 foreach (string id in constraints.Keys)
323 {
324 if (this.constraints[id].Contains(resolvedId))
325 {
326 this.constraints[id].Remove(resolvedId);
327
328 // If we just removed the last constraint for this
329 // ID, it is now a valid start ID.
330 if (0 == this.constraints[id].Count)
331 {
332 newStartIds.Add(id);
333 }
334 }
335 }
336
337 foreach (string id in newStartIds)
338 {
339 this.constraints.Remove(id);
340 }
341
342 this.startIds.AddRange(newStartIds);
343 }
344 }
345 }
346#endif
347}
diff --git a/src/wixext/UtilCompiler.cs b/src/wixext/UtilCompiler.cs
index 6b359591..6e3c2531 100644
--- a/src/wixext/UtilCompiler.cs
+++ b/src/wixext/UtilCompiler.cs
@@ -230,6 +230,8 @@ namespace WixToolset.Util
230 break; 230 break;
231 case "ComponentSearch": 231 case "ComponentSearch":
232 case "ComponentSearchRef": 232 case "ComponentSearchRef":
233 case "DetectSHA2Support":
234 case "DetectSHA2SupportRef":
233 case "DirectorySearch": 235 case "DirectorySearch":
234 case "DirectorySearchRef": 236 case "DirectorySearchRef":
235 case "FileSearch": 237 case "FileSearch":
@@ -251,6 +253,12 @@ namespace WixToolset.Util
251 case "ComponentSearchRef": 253 case "ComponentSearchRef":
252 this.ParseComponentSearchRefElement(intermediate, section, element); 254 this.ParseComponentSearchRefElement(intermediate, section, element);
253 break; 255 break;
256 case "DetectSHA2Support":
257 this.ParseDetectSHA2SupportElement(intermediate, section, element);
258 break;
259 case "DetectSHA2SupportRef":
260 this.ParseDetectSHA2SupportRefElement(intermediate, section, element);
261 break;
254 case "DirectorySearch": 262 case "DirectorySearch":
255 this.ParseDirectorySearchElement(intermediate, section, element); 263 this.ParseDirectorySearchElement(intermediate, section, element);
256 break; 264 break;
@@ -422,11 +430,6 @@ namespace WixToolset.Util
422 } 430 }
423 } 431 }
424 432
425 if (null == variable)
426 {
427 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable"));
428 }
429
430 if (null == guid) 433 if (null == guid)
431 { 434 {
432 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Guid")); 435 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Guid"));
@@ -439,16 +442,10 @@ namespace WixToolset.Util
439 442
440 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); 443 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
441 444
445 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
446
442 if (!this.Messaging.EncounteredError) 447 if (!this.Messaging.EncounteredError)
443 { 448 {
444 this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition);
445 if (after != null)
446 {
447 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after);
448 // TODO: We're currently defaulting to "always run after", which we will need to change...
449 this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2);
450 }
451
452 WixComponentSearchAttributes attributes = WixComponentSearchAttributes.KeyPath; 449 WixComponentSearchAttributes attributes = WixComponentSearchAttributes.KeyPath;
453 switch (result) 450 switch (result)
454 { 451 {
@@ -506,6 +503,89 @@ namespace WixToolset.Util
506 } 503 }
507 504
508 /// <summary> 505 /// <summary>
506 /// Parses a DetectSHA2Support element.
507 /// </summary>
508 /// <param name="element">Element to parse.</param>
509 private void ParseDetectSHA2SupportElement(Intermediate intermediate, IntermediateSection section, XElement element)
510 {
511 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
512 Identifier id = null;
513 string variable = null;
514 string condition = null;
515 string after = null;
516
517 foreach (var attrib in element.Attributes())
518 {
519 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
520 {
521 switch (attrib.Name.LocalName)
522 {
523 case "Id":
524 case "Variable":
525 case "Condition":
526 case "After":
527 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
528 break;
529 default:
530 this.ParseHelper.UnexpectedAttribute(element, attrib);
531 break;
532 }
533 }
534 else
535 {
536 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
537 }
538 }
539
540 if (id == null)
541 {
542 id = this.ParseHelper.CreateIdentifier("wds2s", variable, condition, after);
543 }
544
545 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
546
547 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, UtilConstants.UtilBundleExtensionId);
548
549 if (!this.Messaging.EncounteredError)
550 {
551 section.Tuples.Add(new WixDetectSHA2SupportTuple(sourceLineNumbers, id));
552 }
553 }
554
555 /// <summary>
556 /// Parses a DetectSHA2SupportRef element
557 /// </summary>
558 /// <param name="element">Element to parse.</param>
559 private void ParseDetectSHA2SupportRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
560 {
561 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
562 string refId = null;
563
564 foreach (var attrib in element.Attributes())
565 {
566 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
567 {
568 switch (attrib.Name.LocalName)
569 {
570 case "Id":
571 refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
572 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixDetectSHA2Support", refId);
573 break;
574 default:
575 this.ParseHelper.UnexpectedAttribute(element, attrib);
576 break;
577 }
578 }
579 else
580 {
581 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
582 }
583 }
584
585 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
586 }
587
588 /// <summary>
509 /// Parses an event source element. 589 /// Parses an event source element.
510 /// </summary> 590 /// </summary>
511 /// <param name="element">Element to parse.</param> 591 /// <param name="element">Element to parse.</param>
@@ -868,11 +948,6 @@ namespace WixToolset.Util
868 } 948 }
869 } 949 }
870 950
871 if (null == variable)
872 {
873 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable"));
874 }
875
876 if (null == path) 951 if (null == path)
877 { 952 {
878 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); 953 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path"));
@@ -885,16 +960,10 @@ namespace WixToolset.Util
885 960
886 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); 961 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
887 962
963 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
964
888 if (!this.Messaging.EncounteredError) 965 if (!this.Messaging.EncounteredError)
889 { 966 {
890 this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition);
891 if (after != null)
892 {
893 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after);
894 // TODO: We're currently defaulting to "always run after", which we will need to change...
895 this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2);
896 }
897
898 WixFileSearchAttributes attributes = WixFileSearchAttributes.IsDirectory; 967 WixFileSearchAttributes attributes = WixFileSearchAttributes.IsDirectory;
899 switch (result) 968 switch (result)
900 { 969 {
@@ -990,11 +1059,6 @@ namespace WixToolset.Util
990 } 1059 }
991 } 1060 }
992 1061
993 if (null == variable)
994 {
995 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Variable"));
996 }
997
998 if (null == path) 1062 if (null == path)
999 { 1063 {
1000 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path")); 1064 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path"));
@@ -1007,16 +1071,10 @@ namespace WixToolset.Util
1007 1071
1008 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); 1072 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
1009 1073
1074 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, node.Name.LocalName, id, variable, condition, after, null);
1075
1010 if (!this.Messaging.EncounteredError) 1076 if (!this.Messaging.EncounteredError)
1011 { 1077 {
1012 this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition);
1013 if (after != null)
1014 {
1015 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after);
1016 // TODO: We're currently defaulting to "always run after", which we will need to change...
1017 this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2);
1018 }
1019
1020 WixFileSearchAttributes attributes = WixFileSearchAttributes.Default; 1078 WixFileSearchAttributes attributes = WixFileSearchAttributes.Default;
1021 switch (result) 1079 switch (result)
1022 { 1080 {
@@ -1049,38 +1107,6 @@ namespace WixToolset.Util
1049 } 1107 }
1050 1108
1051 /// <summary> 1109 /// <summary>
1052 /// Creates a row in the WixSearch table.
1053 /// </summary>
1054 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
1055 /// <param name="id">Identifier of the search.</param>
1056 /// <param name="variable">The Burn variable to store the result into.</param>
1057 /// <param name="condition">A condition to test before evaluating the search.</param>
1058 private void CreateWixSearchRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string variable, string condition)
1059 {
1060 section.Tuples.Add(new WixSearchTuple(sourceLineNumbers, id)
1061 {
1062 Variable = variable,
1063 Condition = condition,
1064 });
1065 }
1066
1067 /// <summary>
1068 ///
1069 /// </summary>
1070 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
1071 /// <param name="id">Identifier of the search (key into the WixSearch table)</param>
1072 /// <param name="parentId">Identifier of the search that comes before (key into the WixSearch table)</param>
1073 /// <param name="attributes">Further details about the relation between id and parentId.</param>
1074 private void CreateWixSearchRelationRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, int attributes)
1075 {
1076 section.Tuples.Add(new WixSearchRelationTuple(sourceLineNumbers, id)
1077 {
1078 ParentSearchRef = parentId,
1079 Attributes = attributes,
1080 });
1081 }
1082
1083 /// <summary>
1084 /// Parses a file share element. 1110 /// Parses a file share element.
1085 /// </summary> 1111 /// </summary>
1086 /// <param name="element">Element to parse.</param> 1112 /// <param name="element">Element to parse.</param>
@@ -2523,11 +2549,6 @@ namespace WixToolset.Util
2523 } 2549 }
2524 } 2550 }
2525 2551
2526 if (null == variable)
2527 {
2528 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable"));
2529 }
2530
2531 if (null == upgradeCode && null == productCode) 2552 if (null == upgradeCode && null == productCode)
2532 { 2553 {
2533 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ProductCode", "UpgradeCode", true)); 2554 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ProductCode", "UpgradeCode", true));
@@ -2545,16 +2566,10 @@ namespace WixToolset.Util
2545 2566
2546 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); 2567 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2547 2568
2569 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
2570
2548 if (!this.Messaging.EncounteredError) 2571 if (!this.Messaging.EncounteredError)
2549 { 2572 {
2550 this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition);
2551 if (after != null)
2552 {
2553 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after);
2554 // TODO: We're currently defaulting to "always run after", which we will need to change...
2555 this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2);
2556 }
2557
2558 WixProductSearchAttributes attributes = WixProductSearchAttributes.Version; 2573 WixProductSearchAttributes attributes = WixProductSearchAttributes.Version;
2559 switch (result) 2574 switch (result)
2560 { 2575 {
@@ -2662,11 +2677,6 @@ namespace WixToolset.Util
2662 } 2677 }
2663 } 2678 }
2664 2679
2665 if (null == variable)
2666 {
2667 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable"));
2668 }
2669
2670 if (!root.HasValue) 2680 if (!root.HasValue)
2671 { 2681 {
2672 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root")); 2682 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root"));
@@ -2726,16 +2736,10 @@ namespace WixToolset.Util
2726 2736
2727 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); 2737 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2728 2738
2739 this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
2740
2729 if (!this.Messaging.EncounteredError) 2741 if (!this.Messaging.EncounteredError)
2730 { 2742 {
2731 this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition);
2732 if (after != null)
2733 {
2734 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after);
2735 // TODO: We're currently defaulting to "always run after", which we will need to change...
2736 this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2);
2737 }
2738
2739 section.Tuples.Add(new WixRegistrySearchTuple(sourceLineNumbers, id) 2743 section.Tuples.Add(new WixRegistrySearchTuple(sourceLineNumbers, id)
2740 { 2744 {
2741 Root = (RegistryRootType)root, 2745 Root = (RegistryRootType)root,
diff --git a/src/wixext/UtilConstants.cs b/src/wixext/UtilConstants.cs
index 28ff368f..d9ad460f 100644
--- a/src/wixext/UtilConstants.cs
+++ b/src/wixext/UtilConstants.cs
@@ -13,5 +13,7 @@ namespace WixToolset.Util
13 internal static readonly string[] RegistryPermissions = { "Read", "Write", "CreateSubkeys", "EnumerateSubkeys", "Notify", "CreateLink" }; 13 internal static readonly string[] RegistryPermissions = { "Read", "Write", "CreateSubkeys", "EnumerateSubkeys", "Notify", "CreateLink" };
14 internal static readonly string[] ServicePermissions = { "ServiceQueryConfig", "ServiceChangeConfig", "ServiceQueryStatus", "ServiceEnumerateDependents", "ServiceStart", "ServiceStop", "ServicePauseContinue", "ServiceInterrogate", "ServiceUserDefinedControl" }; 14 internal static readonly string[] ServicePermissions = { "ServiceQueryConfig", "ServiceChangeConfig", "ServiceQueryStatus", "ServiceEnumerateDependents", "ServiceStart", "ServiceStop", "ServicePauseContinue", "ServiceInterrogate", "ServiceUserDefinedControl" };
15 internal static readonly string[] StandardPermissions = { "Delete", "ReadPermission", "ChangePermission", "TakeOwnership", "Synchronize" }; 15 internal static readonly string[] StandardPermissions = { "Delete", "ReadPermission", "ChangePermission", "TakeOwnership", "Synchronize" };
16
17 internal const string UtilBundleExtensionId = "WixUtilBundleExtension";
16 } 18 }
17} 19}
diff --git a/src/wixext/UtilErrors.cs b/src/wixext/UtilErrors.cs
index 988b8321..c04f0449 100644
--- a/src/wixext/UtilErrors.cs
+++ b/src/wixext/UtilErrors.cs
@@ -13,11 +13,6 @@ namespace WixToolset.Util
13 return Message(null, Ids.ArgumentRequiresValue, "The argument '{0}' does not have a value specified and it is required.", argument); 13 return Message(null, Ids.ArgumentRequiresValue, "The argument '{0}' does not have a value specified and it is required.", argument);
14 } 14 }
15 15
16 public static Message CircularSearchReference(string chain)
17 {
18 return Message(null, Ids.CircularSearchReference, "A circular reference of search ordering constraints was detected: {0}. Search ordering references must form a directed acyclic graph.", chain);
19 }
20
21 public static Message DirectoryNotFound(string directory) 16 public static Message DirectoryNotFound(string directory)
22 { 17 {
23 return Message(null, Ids.DirectoryNotFound, "The directory '{0}' could not be found.", directory); 18 return Message(null, Ids.DirectoryNotFound, "The directory '{0}' could not be found.", directory);
@@ -102,7 +97,6 @@ namespace WixToolset.Util
102 FileNotFound = 5059, 97 FileNotFound = 5059,
103 PerformanceCategoryNotFound = 5060, 98 PerformanceCategoryNotFound = 5060,
104 UnsupportedPerformanceCounterType = 5061, 99 UnsupportedPerformanceCounterType = 5061,
105 CircularSearchReference = 5062,
106 InvalidRegistryObject = 5063, 100 InvalidRegistryObject = 5063,
107 } 101 }
108 } 102 }
diff --git a/src/wixext/util.xsd b/src/wixext/util.xsd
index fb8d8032..a8c3d208 100644
--- a/src/wixext/util.xsd
+++ b/src/wixext/util.xsd
@@ -172,6 +172,30 @@
172 <xs:attribute name="Id" type="xs:string" use="required" /> 172 <xs:attribute name="Id" type="xs:string" use="required" />
173 </xs:complexType> 173 </xs:complexType>
174 </xs:element> 174 </xs:element>
175 <xs:element name="DetectSHA2Support">
176 <xs:annotation>
177 <xs:documentation>Detects support for SHA2.</xs:documentation>
178 <xs:appinfo>
179 <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Bundle" />
180 <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Fragment" />
181 </xs:appinfo>
182 </xs:annotation>
183 <xs:complexType>
184 <xs:attributeGroup ref="SearchCommonAttributes" />
185 </xs:complexType>
186 </xs:element>
187 <xs:element name="DetectSHA2SupportRef">
188 <xs:annotation>
189 <xs:documentation>References a DetectSHA2Support.</xs:documentation>
190 <xs:appinfo>
191 <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Bundle" />
192 <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Fragment" />
193 </xs:appinfo>
194 </xs:annotation>
195 <xs:complexType>
196 <xs:attribute name="Id" type="xs:string" use="required" />
197 </xs:complexType>
198 </xs:element>
175 <xs:element name="DirectorySearch"> 199 <xs:element name="DirectorySearch">
176 <xs:annotation> 200 <xs:annotation>
177 <xs:documentation>Describes a directory search.</xs:documentation> 201 <xs:documentation>Describes a directory search.</xs:documentation>