std::vector available_toolsets()

in platforms/Windows/CustomActions/SwiftInstaller/Sources/swift_installer.cc [163:269]


std::vector<std::filesystem::path> available_toolsets() noexcept {
  windows::raii::com_initializer com;

  std::vector<std::filesystem::path> toolsets;

  ISetupConfigurationPtr configuration;
  if (FAILED(configuration.CreateInstance(__uuidof(SetupConfiguration))))
    return toolsets;

  ISetupConfiguration2Ptr configuration2;
  if (FAILED(configuration->QueryInterface(&configuration2)))
    return toolsets;

  IEnumSetupInstancesPtr instances;
  if (FAILED(configuration2->EnumAllInstances(&instances)))
    return toolsets;

  ULONG fetched;
  ISetupInstancePtr instance;
  while (SUCCEEDED(instances->Next(1, &instance, &fetched)) && fetched) {
    ISetupInstance2Ptr instance2;
    if (FAILED(instance->QueryInterface(&instance2)))
      continue;

    InstanceState state;
    if (FAILED(instance2->GetState(&state)))
      continue;

    // Ensure that the instance state matches
    //  eLocal: The instance installation path exists.
    //  eRegistered: A product is registered to the instance.
    if (~state & eLocal or ~state & eRegistered)
      continue;

    LPSAFEARRAY packages;
    if (FAILED(instance2->GetPackages(&packages)))
      continue;

    LONG lower, upper;
    if (FAILED(SafeArrayGetLBound(packages, 1, &lower)) ||
        FAILED(SafeArrayGetUBound(packages, 1, &upper)))
      continue;

    for (LONG index = 0, count = upper - lower + 1; index < count; ++index) {
      IUnknownPtr element;
      if (FAILED(SafeArrayGetElement(packages, &index, &element)))
        continue;

      ISetupPackageReferencePtr package;
      if (FAILED(element->QueryInterface(&package)))
        continue;

      _bstr_t package_id;
      if (FAILED(package->GetId(package_id.GetAddress())))
        continue;

      // Ensure that we are dealing with a (known) MSVC ToolSet
      if (std::none_of(std::begin(known_toolsets), std::end(known_toolsets),
                      [package_id = package_id.GetBSTR()](const wchar_t *id) {
                        return wcscmp(package_id, id) == 0;
                      }))
        continue;

      _bstr_t VSInstallDir;
      if (FAILED(instance2->GetInstallationPath(VSInstallDir.GetAddress())))
        continue;

      std::filesystem::path VCInstallDir{static_cast<wchar_t *>(VSInstallDir)};
      VCInstallDir.append("VC");

      std::string VCToolsVersion;
      // VSInstallDir\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt
      // contains the default version of the v141 MSVC toolset.  Prefer to use
      // VSInstallDir\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v142.default.txt
      // which contains the default v142 version of the toolset.
      for (const auto &file : {"Microsoft.VCToolsVersion.v142.default.txt",
                               "Microsoft.VCToolsVersion.default.txt"}) {
        std::filesystem::path path{VCInstallDir};
        path.append("Auxiliary");
        path.append("Build");
        path.append(file);

        VCToolsVersion = contents(path);
        // Strip any line ending characters from the contents of the file.
        trim(VCToolsVersion);
        if (!VCToolsVersion.empty())
          break;
      }

      if (VCToolsVersion.empty())
        continue;

      std::filesystem::path VCToolsInstallDir{VCInstallDir};
      VCToolsInstallDir.append("Tools");
      VCToolsInstallDir.append("MSVC");
      VCToolsInstallDir.append(VCToolsVersion);

      // FIXME(compnerd) should we actually just walk the directory structure
      // instead and populate all the toolsets?  That would match roughly what
      // we do with the UCRT currently.

      toolsets.push_back(VCToolsInstallDir);
    }
  }

  return toolsets;
}