From 1527dc35321ab61b602988aebda22ad8e9e1670e Mon Sep 17 00:00:00 2001 From: YR Chen Date: Thu, 25 May 2023 11:58:14 +0800 Subject: [PATCH 1/2] WiX: add custom actions to handle Clang resources --- .../Windows/CustomActions/CustomActions.cc | 152 ++++++++++++++++++ .../CustomActions/CustomActions.vcxproj | 91 +++++++++++ platforms/Windows/bld/bld.wixproj | 7 + platforms/Windows/bld/bld.wxs | 23 +++ 4 files changed, 273 insertions(+) create mode 100644 platforms/Windows/CustomActions/CustomActions.cc create mode 100644 platforms/Windows/CustomActions/CustomActions.vcxproj diff --git a/platforms/Windows/CustomActions/CustomActions.cc b/platforms/Windows/CustomActions/CustomActions.cc new file mode 100644 index 00000000..5c0f3cd6 --- /dev/null +++ b/platforms/Windows/CustomActions/CustomActions.cc @@ -0,0 +1,152 @@ +#include +#include +#include + +#include + +std::wstring get_error_message(const std::error_code& error) { + auto message = error.message(); + auto length = MultiByteToWideChar(CP_THREAD_ACP, 0, message.c_str(), message.size(), nullptr, 0); + std::wstring result(length, L'\0'); + MultiByteToWideChar(CP_THREAD_ACP, 0, message.c_str(), message.size(), &result[0], length); + return result; +} + +namespace msi { +void log(MSIHANDLE hInstall, UINT eMessageType, std::wstring message) noexcept { + PMSIHANDLE record = MsiCreateRecord(0); + (void)MsiRecordSetStringW(record, 0, message.c_str()); + (void)MsiProcessMessage(hInstall, INSTALLMESSAGE(eMessageType), record); +} + +void log_last_error(MSIHANDLE hInstall) noexcept { + PMSIHANDLE record = MsiGetLastErrorRecord(); + (void)MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, record); +} + +std::wstring get_property(MSIHANDLE hInstall, std::wstring name, UINT &result) noexcept { + DWORD size = 0; + + result = MsiGetPropertyW(hInstall, name.c_str(), L"", &size); + switch (result) { + case ERROR_MORE_DATA: + break; + case ERROR_SUCCESS: + result = ERROR_INSTALL_FAILURE; + return nullptr; + default: + log_last_error(hInstall); + return nullptr; + } + + std::vector buffer; + buffer.resize(size + 1); + + size = buffer.capacity(); + result = MsiGetPropertyW(hInstall, name.c_str(), buffer.data(), &size); + switch (result) { + case ERROR_SUCCESS: + break; + default: + log_last_error(hInstall); + return nullptr; + } + + return {buffer.data(), buffer.capacity()}; +} +} + +extern "C" _declspec(dllexport) UINT __stdcall CopyClangResources(MSIHANDLE hInstall) { + UINT result = ERROR_SUCCESS; + + // Get the runtime resource directory path + auto resourceDirectory = msi::get_property(hInstall, L"CustomActionData", result); + if (result != ERROR_SUCCESS) { + return result; + } + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Swift runtime resources: " + resourceDirectory); + + // Get the source and destination paths + std::filesystem::path resourcePath(resourceDirectory); + if (resourcePath.has_filename()) { + resourcePath = resourcePath.parent_path(); + } + auto sourcePath = resourcePath.parent_path() / "clang"; + auto destinationPath = resourcePath / "clang"; + + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources: " + sourcePath.wstring()); + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift: " + destinationPath.wstring()); + + // Verify that Clang resources contain exactly one top-level directory + { + int directoryCount = 0, fileCount = 0; + for (const auto& entry : std::filesystem::directory_iterator(sourcePath)) { + if (entry.is_directory()) { + directoryCount++; + sourcePath = entry.path(); + } else { + fileCount++; + } + } + if (directoryCount != 1 || fileCount != 0) { + msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, L"lib\\clang must contain exactly one directory."); + return ERROR_FILE_CORRUPT; + } + } + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Detected Clang version: " + sourcePath.filename().wstring()); + + // Copy the contents of the single directory directly to the destination path + std::error_code error; + auto options = std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive; + + std::filesystem::copy(sourcePath, destinationPath, options, error); + if (error) { + std::wstring errorMessage = L"Error during directory copy: " + get_error_message(error); + msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, errorMessage); + return ERROR_DISK_OPERATION_FAILED; + } + + return result; +} + +extern "C" _declspec(dllexport) UINT __stdcall RemoveClangResources(MSIHANDLE hInstall) { + UINT result = ERROR_SUCCESS; + + // Get the directory path for removal + auto resourceDirectory = msi::get_property(hInstall, L"_usr_lib_swift", result); + if (result != ERROR_SUCCESS) { + return result; + } + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Swift runtime resources: " + resourceDirectory); + + std::filesystem::path resourcePath(resourceDirectory); + if (resourcePath.has_filename()) { + resourcePath = resourcePath.parent_path(); + } + auto directoryPath = resourcePath / "clang"; + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift: " + directoryPath.wstring()); + + // Remove the directory and its contents + std::error_code error; + auto count = std::filesystem::remove_all(directoryPath, error); + if (error) { + std::wstring errorMessage = L"Error during directory removal: " + get_error_message(error); + msi::log(hInstall, INSTALLMESSAGE_ERROR | MB_OK, errorMessage); + return ERROR_DISK_OPERATION_FAILED; + } else if (count == 0) { + msi::log(hInstall, INSTALLMESSAGE_INFO, L"Clang resources for Swift doesn't exist!"); + } + + return result; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} diff --git a/platforms/Windows/CustomActions/CustomActions.vcxproj b/platforms/Windows/CustomActions/CustomActions.vcxproj new file mode 100644 index 00000000..703afe82 --- /dev/null +++ b/platforms/Windows/CustomActions/CustomActions.vcxproj @@ -0,0 +1,91 @@ + + + + Debug + x86 + + + Release + x86 + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + + + {84027311-67A4-4351-AD3A-53529767BFA0} + SwiftInstaller + + + + + + DynamicLibrary + Unicode + CustomActions + + 10.0 + + $(DefaultPlatformToolset) + $(PlatformToolset) + + build\$(Configuration)\$(PlatformShortName)\obj\ + build\$(Configuration)\$(PlatformShortName)\bin\ + + + + true + + + + false + true + + + + + stdcpp17 + VC_EXTRALEAN;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) + + + msi.lib + + + + + + MultiThreadedDebug + + + true + + + + + + MultiThreaded + + + + + + + + + + + diff --git a/platforms/Windows/bld/bld.wixproj b/platforms/Windows/bld/bld.wixproj index 7847d2e5..b63ef56e 100644 --- a/platforms/Windows/bld/bld.wixproj +++ b/platforms/Windows/bld/bld.wixproj @@ -12,6 +12,13 @@ + + + CustomActions + Configuration=Release + + + ClangResources diff --git a/platforms/Windows/bld/bld.wxs b/platforms/Windows/bld/bld.wxs index 5676c194..51351df3 100644 --- a/platforms/Windows/bld/bld.wxs +++ b/platforms/Windows/bld/bld.wxs @@ -348,5 +348,28 @@ + + + + + + + + + + + + + From 18565fee625a706a9a539502a80a593138943475 Mon Sep 17 00:00:00 2001 From: YR Chen Date: Wed, 9 Aug 2023 22:41:32 +0800 Subject: [PATCH 2/2] WiX: Ignore `CustomActions` build cache in Git Due to MSBuild restrictions, the `CustomActions` build is out-of-tree. We change the directory to `.output` so it won't go in the Git working tree. --- platforms/Windows/CustomActions/CustomActions.vcxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/Windows/CustomActions/CustomActions.vcxproj b/platforms/Windows/CustomActions/CustomActions.vcxproj index 703afe82..105889de 100644 --- a/platforms/Windows/CustomActions/CustomActions.vcxproj +++ b/platforms/Windows/CustomActions/CustomActions.vcxproj @@ -43,8 +43,8 @@ $(DefaultPlatformToolset) $(PlatformToolset) - build\$(Configuration)\$(PlatformShortName)\obj\ - build\$(Configuration)\$(PlatformShortName)\bin\ + .output\$(Configuration)\$(PlatformShortName)\obj\ + .output\$(Configuration)\$(PlatformShortName)\bin\