UINT SwiftInstaller_InstallAuxiliaryFiles()

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;
}