in platforms/Windows/CustomActions/SwiftInstaller/Sources/swift_installer.cc [291:427]
UINT SwiftInstaller_InstallAuxiliaryFiles(MSIHANDLE hInstall) {
std::wstring data = msi::get_property(hInstall, L"CustomActionData");
trim(data);
std::filesystem::path SDKROOT{data};
LOG(hInstall, info) << "SDKROOT: " << SDKROOT.string();
// Copy SDK Module Maps
std::filesystem::path UniversalCRTSdkDir = winsdk::install_root();
LOG(hInstall, info) << "UniversalCRTSdkDir: " << UniversalCRTSdkDir;
if (!UniversalCRTSdkDir.empty()) {
// FIXME(compnerd) Technically we are using the UniversalCRTSdkDir here
// instead of the WindowsSdkDir which would contain `um`.
// FIXME(compnerd) we may end up in a state where the ucrt and Windows SDKs
// do not match. Users have reported cases where they somehow managed to
// setup such a configuration. We should split this up to explicitly
// handle the UCRT and WinSDK paths separately.
for (const auto &version : winsdk::available_versions()) {
const struct {
std::filesystem::path src;
std::filesystem::path dst;
} items[] = {
{ SDKROOT / "usr" / "share" / "ucrt.modulemap",
UniversalCRTSdkDir / "Include" / version / "ucrt" / "module.modulemap" },
{ SDKROOT / "usr" / "share" / "winsdk.modulemap",
UniversalCRTSdkDir / "Include" / version / "um" / "module.modulemap" },
};
for (const auto &item : items) {
static const constexpr std::filesystem::copy_options options =
std::filesystem::copy_options::overwrite_existing;
std::error_code ec;
if (!std::filesystem::copy_file(item.src, item.dst, options, ec)) {
LOG(hInstall, error)
<< "unable to copy " << item.src << " to " << item.dst << ": "
<< ec.message();
continue;
}
LOG(hInstall, info) << "Deployed " << item.dst;
}
}
}
// Copy MSVC Tools Module Maps
for (const auto &VCToolsInstallDir : msvc::available_toolsets()) {
const struct {
std::filesystem::path src;
std::filesystem::path dst;
} items[] = {
{ SDKROOT / "usr" / "share" / "visualc.modulemap",
VCToolsInstallDir / "include" / "module.modulemap" },
{ SDKROOT / "usr" / "share" / "visualc.apinotes",
VCToolsInstallDir / "include" / "visualc.apinotes" },
};
for (const auto &item : items) {
static const constexpr std::filesystem::copy_options options =
std::filesystem::copy_options::overwrite_existing;
std::error_code ec;
if (!std::filesystem::copy_file(item.src, item.dst, options, ec)) {
LOG(hInstall, error)
<< "unable to copy " << item.src << " to " << item.dst << ": "
<< ec.message();
continue;
}
LOG(hInstall, info) << "Deployed " << item.dst;
}
}
// TODO(compnerd) it would be ideal to record the files deployed here to the
// `RemoveFile` table which would allow them to be cleaned up on removal.
// This is tricky as we cannot modify the on-disk database. The deferred
// action is already executed post-InstallExecute which means that we can now
// identify the cached MSI by:
//
// std::wstring product_code = msi::get_property(hInstall, L"ProductCode");
//
// DWORD size = 0;
// (void)MsiGetProductInfoW(product_code.c_str(),
// INSTALLPROPERTY_LOCALPACKAGE, L"", &size);
// std::vector<wchar_t> buffer;
// buffer.resize(++size);
// (void)MsiGetProductInfoW(product_code.c_str(),
// INSTALLPROPERTY_LOCALPACKAGE, buffer.data(),
// &size);
//
// We can then create a new property for the location of the entry and a new
// RemoveFile entry for cleaning up the file. Note that we may have to tweak
// things to get repairs to work properly with the tracking.
//
// PMSIHANDLE database;
// (void)MsiOpenDatabaseW(buffer.data(), MSIDBOPEN_TRANSACT, &database);
//
// static const wchar_t query[] =
// LR"SQL(
//INSERT INTO `Property` (`Property`, `Value`)
// VALUES(?, ?);
//INSERT INTO `RemoveFile` (`FileKey`, `Component_`, `FileName`, `DirProperty`, `InstallMode`
// VALUES (?, ?, ?, ?, ?);
// )SQL";
//
// PMSIHANDLE view;
// (void)MsiDatabaseOpenViewW(database, query, &view);
//
// std::hash<std::wstring> hasher;
//
// std::wostringstream component;
// component << "cmp" << hasher(path.wstring());
//
// std::wostringstream property;
// property << "prop" << hasher(path.wstring());
//
// PMSIHANDLE record = MsiRecordCreate(7);
// (void)MsiRecordSetStringW(record, 1, property.str().c_str());
// (void)MsiRecordSetStringW(record, 2, path.parent_path().wstring().c_str());
// (void)MsiRecordSetStringW(record, 3, component.str().c_str());
// (void)MsiRecordSetStringW(record, 4, component.str().c_str());
// (void)MsiRecordSetStringW(record, 5, path.filename().wstring().c_str());
// (void)MsiRecordSetStringW(record, 6, property.str().c_str());
// (void)MsiRecordSetInteger(record, 7, 2);
//
// (void)MsiViewExecute(view, record);
//
// (void)MsiDatabaseCommit(database);
//
// Currently, this seems to fail with the commiting of the database, which is
// still a mystery to me. This relies on the clever usage of the
// `EnsureTable` in the WiX definition to ensure that we do not need to create
// the table.
//
// The error handling has been elided here for brevity's sake.
return ERROR_SUCCESS;
}