Skip to content

Commit cc4d6b5

Browse files
committed
WiX: add custom actions to handle Clang resources
1 parent a22a5bb commit cc4d6b5

File tree

5 files changed

+292
-0
lines changed

5 files changed

+292
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include <Windows.h>
2+
#include <msi.h>
3+
#include <msiquery.h>
4+
5+
#include <filesystem>
6+
7+
std::wstring get_error_message(const std::error_code& error) {
8+
auto message = error.message();
9+
auto length = MultiByteToWideChar(CP_THREAD_ACP, 0, message.c_str(), message.size(), nullptr, 0);
10+
std::wstring result(length, L'\0');
11+
MultiByteToWideChar(CP_THREAD_ACP, 0, message.c_str(), message.size(), &result[0], length);
12+
return result;
13+
}
14+
15+
namespace msi {
16+
void log(MSIHANDLE hInstall, UINT eMessageType, std::wstring message) noexcept {
17+
PMSIHANDLE record = MsiCreateRecord(0);
18+
(void)MsiRecordSetStringW(record, 0, message.c_str());
19+
(void)MsiProcessMessage(hInstall, INSTALLMESSAGE(eMessageType), record);
20+
}
21+
22+
void log_last_error(MSIHANDLE hInstall) noexcept {
23+
PMSIHANDLE record = MsiGetLastErrorRecord();
24+
(void)MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, record);
25+
}
26+
27+
std::wstring get_property(MSIHANDLE hInstall, std::wstring name, UINT &result) noexcept {
28+
DWORD size = 0;
29+
30+
result = MsiGetPropertyW(hInstall, name.c_str(), L"", &size);
31+
switch (result) {
32+
case ERROR_MORE_DATA:
33+
break;
34+
case ERROR_SUCCESS:
35+
result = ERROR_INSTALL_FAILURE;
36+
return nullptr;
37+
default:
38+
log_last_error(hInstall);
39+
return nullptr;
40+
}
41+
42+
std::vector<WCHAR> buffer;
43+
buffer.resize(size + 1);
44+
45+
size = buffer.capacity();
46+
result = MsiGetPropertyW(hInstall, name.c_str(), buffer.data(), &size);
47+
switch (result) {
48+
case ERROR_SUCCESS:
49+
break;
50+
default:
51+
log_last_error(hInstall);
52+
return nullptr;
53+
}
54+
55+
return {buffer.data(), buffer.capacity()};
56+
}
57+
}
58+
59+
extern "C" _declspec(dllexport) UINT __stdcall CopyClangResources(MSIHANDLE hInstall) {
60+
UINT result = ERROR_SUCCESS;
61+
62+
// Get the runtime resource directory path
63+
auto resourceDirectory = msi::get_property(hInstall, L"CustomActionData", result);
64+
if (result != ERROR_SUCCESS) {
65+
return result;
66+
}
67+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Swift runtime resources: " + resourceDirectory);
68+
69+
// Get the source and destination paths
70+
std::filesystem::path resourcePath(resourceDirectory);
71+
if (resourcePath.has_filename()) {
72+
resourcePath = resourcePath.parent_path();
73+
}
74+
auto sourcePath = resourcePath.parent_path() / "clang";
75+
auto destinationPath = resourcePath / "clang";
76+
77+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources: " + sourcePath.wstring());
78+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift: " + destinationPath.wstring());
79+
80+
// Verify that Clang resources contain exactly one top-level directory
81+
{
82+
int directoryCount = 0, fileCount = 0;
83+
for (const auto& entry : std::filesystem::directory_iterator(sourcePath)) {
84+
if (entry.is_directory()) {
85+
directoryCount++;
86+
sourcePath = entry.path();
87+
} else {
88+
fileCount++;
89+
}
90+
}
91+
if (directoryCount != 1 || fileCount != 0) {
92+
msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, L"lib\\clang must contain exactly one directory.");
93+
return ERROR_FILE_CORRUPT;
94+
}
95+
}
96+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Detected Clang version: " + sourcePath.filename().wstring());
97+
98+
// Copy the contents of the single directory directly to the destination path
99+
std::error_code error;
100+
auto options = std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive;
101+
102+
std::filesystem::copy(sourcePath, destinationPath, options, error);
103+
if (error) {
104+
std::wstring errorMessage = L"Error during directory copy: " + get_error_message(error);
105+
msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, errorMessage);
106+
return ERROR_DISK_OPERATION_FAILED;
107+
}
108+
109+
return result;
110+
}
111+
112+
extern "C" _declspec(dllexport) UINT __stdcall RemoveClangResources(MSIHANDLE hInstall) {
113+
UINT result = ERROR_SUCCESS;
114+
115+
// Get the directory path for removal
116+
auto resourceDirectory = msi::get_property(hInstall, L"_usr_lib_swift", result);
117+
if (result != ERROR_SUCCESS) {
118+
return result;
119+
}
120+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Swift runtime resources: " + resourceDirectory);
121+
122+
std::filesystem::path resourcePath(resourceDirectory);
123+
if (resourcePath.has_filename()) {
124+
resourcePath = resourcePath.parent_path();
125+
}
126+
auto directoryPath = resourcePath / "clang";
127+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift: " + directoryPath.wstring());
128+
129+
// Remove the directory and its contents
130+
std::error_code error;
131+
auto count = std::filesystem::remove_all(directoryPath, error);
132+
if (error) {
133+
std::wstring errorMessage = L"Error during directory removal: " + get_error_message(error);
134+
msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, errorMessage);
135+
return ERROR_DISK_OPERATION_FAILED;
136+
} else if (count == 0) {
137+
msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift doesn't exist!");
138+
}
139+
140+
return result;
141+
}
142+
143+
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
144+
switch (ul_reason_for_call) {
145+
case DLL_PROCESS_ATTACH:
146+
case DLL_PROCESS_DETACH:
147+
case DLL_THREAD_ATTACH:
148+
case DLL_THREAD_DETACH:
149+
break;
150+
}
151+
return TRUE;
152+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="16.0">
2+
<ItemGroup>
3+
<ProjectConfiguration Include="Debug|Win32">
4+
<Configuration>Debug</Configuration>
5+
<Platform>x86</Platform>
6+
</ProjectConfiguration>
7+
<ProjectConfiguration Include="Release|Win32">
8+
<Configuration>Release</Configuration>
9+
<Platform>x86</Platform>
10+
</ProjectConfiguration>
11+
<ProjectConfiguration Include="Debug|ARM64">
12+
<Configuration>Debug</Configuration>
13+
<Platform>ARM64</Platform>
14+
</ProjectConfiguration>
15+
<ProjectConfiguration Include="Release|ARM64">
16+
<Configuration>Release</Configuration>
17+
<Platform>ARM64</Platform>
18+
</ProjectConfiguration>
19+
<ProjectConfiguration Include="Debug|x64">
20+
<Configuration>Debug</Configuration>
21+
<Platform>x64</Platform>
22+
</ProjectConfiguration>
23+
<ProjectConfiguration Include="Release|x64">
24+
<Configuration>Release</Configuration>
25+
<Platform>x64</Platform>
26+
</ProjectConfiguration>
27+
</ItemGroup>
28+
29+
<PropertyGroup>
30+
<ProjectGuid>{84027311-67A4-4351-AD3A-53529767BFA0}</ProjectGuid>
31+
<RootNamespace>SwiftInstaller</RootNamespace>
32+
</PropertyGroup>
33+
34+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
35+
36+
<PropertyGroup>
37+
<ConfigurationType>DynamicLibrary</ConfigurationType>
38+
<CharacterSet>Unicode</CharacterSet>
39+
<TargetName>CustomActions</TargetName>
40+
41+
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
42+
43+
<PlatformToolset Condition="'$(PlatformToolset)' == ''">$(DefaultPlatformToolset)</PlatformToolset>
44+
<PlatformToolset>$(PlatformToolset)</PlatformToolset>
45+
46+
<IntDir>build\$(Configuration)\$(PlatformShortName)\obj\</IntDir>
47+
<OutDir>build\$(Configuration)\$(PlatformShortName)\bin\</OutDir>
48+
</PropertyGroup>
49+
50+
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
51+
<UseDebugLibraries>true</UseDebugLibraries>
52+
</PropertyGroup>
53+
54+
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
55+
<UseDebugLibraries>false</UseDebugLibraries>
56+
<WholeProgramOptimization>true</WholeProgramOptimization>
57+
</PropertyGroup>
58+
59+
<ItemDefinitionGroup>
60+
<ClCompile>
61+
<LanguageStandard>stdcpp17</LanguageStandard>
62+
<PreprocessorDefinitions>VC_EXTRALEAN;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
63+
</ClCompile>
64+
<Link>
65+
<AdditionalDependencies>msi.lib</AdditionalDependencies>
66+
</Link>
67+
</ItemDefinitionGroup>
68+
69+
<ItemDefinitionGroup Condition="'$(Configuration)' == 'Debug'">
70+
<ClCompile>
71+
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
72+
</ClCompile>
73+
<Link>
74+
<GenerateDebugInformation>true</GenerateDebugInformation>
75+
</Link>
76+
</ItemDefinitionGroup>
77+
78+
<ItemDefinitionGroup Condition="'$(Configuration)' == 'Release'">
79+
<ClCompile>
80+
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
81+
</ClCompile>
82+
</ItemDefinitionGroup>
83+
84+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
85+
86+
<ItemGroup>
87+
<ClCompile Include="CustomActions.cc" />
88+
</ItemGroup>
89+
90+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Targets"/>
91+
</Project>

platforms/Windows/toolchain-amd64.wxs

+21
Original file line numberDiff line numberDiff line change
@@ -578,5 +578,26 @@
578578
<WixVariable Id="WixUIDialogBmp" Value="Resources\swift_dialog.png" />
579579
<WixVariable Id="WixUIBannerBmp" Value="Resources\swift_banner.png" />
580580

581+
<Binary Id="CustomActions" SourceFile="$(var.CustomActions.TargetDir)\CustomActions.dll" />
582+
583+
<CustomAction Id="CopyClangResources.SetProperty"
584+
Property="CopyClangResources"
585+
Value="[_usr_lib_swift]" />
586+
<CustomAction Id="CopyClangResources"
587+
BinaryRef="CustomActions"
588+
DllEntry="CopyClangResources"
589+
Execute="deferred"
590+
Impersonate="no"
591+
Return="check" />
592+
<CustomAction Id="RemoveClangResources"
593+
BinaryRef="CustomActions"
594+
DllEntry="RemoveClangResources"
595+
Return="check" />
596+
597+
<InstallExecuteSequence>
598+
<Custom Action="CopyClangResources.SetProperty" Before="CopyClangResources" />
599+
<Custom Action="CopyClangResources" After="InstallFiles" Condition="NOT REMOVE" />
600+
<Custom Action="RemoveClangResources" Before="RemoveFiles" Condition='REMOVE="ALL"' />
601+
</InstallExecuteSequence>
581602
</Package>
582603
</Wix>

platforms/Windows/toolchain-arm64.wxs

+21
Original file line numberDiff line numberDiff line change
@@ -578,5 +578,26 @@
578578
<WixVariable Id="WixUIDialogBmp" Value="Resources\swift_dialog.png" />
579579
<WixVariable Id="WixUIBannerBmp" Value="Resources\swift_banner.png" />
580580

581+
<Binary Id="CustomActions" SourceFile="$(var.CustomActions.TargetDir)\CustomActions.dll" />
582+
583+
<CustomAction Id="CopyClangResources.SetProperty"
584+
Property="CopyClangResources"
585+
Value="[_usr_lib_swift]" />
586+
<CustomAction Id="CopyClangResources"
587+
BinaryRef="CustomActions"
588+
DllEntry="CopyClangResources"
589+
Execute="deferred"
590+
Impersonate="no"
591+
Return="check" />
592+
<CustomAction Id="RemoveClangResources"
593+
BinaryRef="CustomActions"
594+
DllEntry="RemoveClangResources"
595+
Return="check" />
596+
597+
<InstallExecuteSequence>
598+
<Custom Action="CopyClangResources.SetProperty" Before="CopyClangResources" />
599+
<Custom Action="CopyClangResources" After="InstallFiles" Condition="NOT REMOVE" />
600+
<Custom Action="RemoveClangResources" Before="RemoveFiles" Condition='REMOVE="ALL"' />
601+
</InstallExecuteSequence>
581602
</Package>
582603
</Wix>

platforms/Windows/toolchain.wixproj

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
<PackageReference Include="WixToolset.Heat" Version="4.0.0" />
3535
</ItemGroup>
3636

37+
<ItemGroup>
38+
<ProjectReference Include="CustomActions\CustomActions.vcxproj">
39+
<Name>CustomActions</Name>
40+
<SetConfiguration>Configuration=Release</SetConfiguration>
41+
</ProjectReference>
42+
</ItemGroup>
43+
3744
<ItemGroup>
3845
<!-- FIXME(compnerd): include the shims in the toolchain instead of the SDK?
3946
<HarvestDirectory Include="$(TOOLCHAIN_ROOT)\usr\lib\swift\shims">

0 commit comments

Comments
 (0)