/****************************************************************************
|
**
|
** Copyright (C) 2016 The Qt Company Ltd.
|
** Contact: https://www.qt.io/licensing/
|
**
|
** This file is part of the Qt VS Tools.
|
**
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
** Commercial License Usage
|
** Licensees holding valid commercial Qt licenses may use this file in
|
** accordance with the commercial license agreement provided with the
|
** Software or, alternatively, in accordance with the terms contained in
|
** a written agreement between you and The Qt Company. For licensing terms
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
** information use the contact form at https://www.qt.io/contact-us.
|
**
|
** GNU General Public License Usage
|
** Alternatively, this file may be used under the terms of the GNU
|
** General Public License version 3 as published by the Free Software
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
** included in the packaging of this file. Please review the following
|
** information to ensure the GNU General Public License requirements will
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
**
|
** $QT_END_LICENSE$
|
**
|
****************************************************************************/
|
|
using Microsoft.VisualStudio.VCProjectEngine;
|
using Microsoft.Win32;
|
using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using System.IO;
|
using System.Linq;
|
using System.Threading;
|
using QtVsTools.VisualStudio;
|
using EnvDTE;
|
|
namespace QtVsTools.Core
|
{
|
/// <summary>
|
/// Summary description for QtVersionManager.
|
/// </summary>
|
public class QtVersionManager
|
{
|
private static QtVersionManager instance;
|
private string regVersionPath;
|
private string strVersionKey;
|
private Hashtable versionCache;
|
|
protected QtVersionManager()
|
{
|
strVersionKey = "Versions";
|
regVersionPath = Resources.registryVersionPath;
|
RefreshVersionNames();
|
}
|
|
void RefreshVersionNames()
|
{
|
var rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath;
|
try {
|
using (var rootKey = Registry.CurrentUser.OpenSubKey(rootKeyPath, true))
|
using (var versionsKey = rootKey.OpenSubKey(strVersionKey, true)) {
|
versionsKey.SetValue("VersionNames", string.Join(";", GetVersions()));
|
}
|
|
} catch (Exception e) {
|
Messages.Print(
|
e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
|
}
|
}
|
|
static EventWaitHandle
|
packageInit = new EventWaitHandle(false, EventResetMode.ManualReset),
|
packageInitDone = null;
|
|
static public QtVersionManager The(EventWaitHandle initDone = null)
|
{
|
if (initDone == null) {
|
packageInit.WaitOne();
|
packageInitDone.WaitOne();
|
} else {
|
packageInitDone = initDone;
|
packageInit.Set();
|
}
|
|
if (instance == null)
|
instance = new QtVersionManager();
|
return instance;
|
}
|
|
public VersionInformation GetVersionInfo(string name)
|
{
|
if (name == "$(DefaultQtVersion)")
|
name = GetDefaultVersion();
|
if (name == null)
|
return null;
|
if (versionCache == null)
|
versionCache = new Hashtable();
|
|
var vi = versionCache[name] as VersionInformation;
|
if (vi == null) {
|
var qtdir = GetInstallPath(name);
|
versionCache[name] = vi = VersionInformation.Get(qtdir);
|
if (vi != null)
|
vi.name = name;
|
}
|
return vi;
|
}
|
|
public VersionInformation GetVersionInfo(EnvDTE.Project project)
|
{
|
return GetVersionInfo(GetProjectQtVersion(project));
|
}
|
|
public void ClearVersionCache()
|
{
|
if (versionCache != null)
|
versionCache.Clear();
|
}
|
|
public string[] GetVersions()
|
{
|
return GetVersions(Registry.CurrentUser);
|
}
|
|
public string GetQtVersionFromInstallDir(string qtDir)
|
{
|
if (qtDir == null)
|
return null;
|
|
qtDir = qtDir.ToLower();
|
var versions = GetVersions();
|
foreach (var version in versions) {
|
var installPath = GetInstallPath(version);
|
if (installPath == null)
|
continue;
|
if (installPath.ToLower() == qtDir)
|
return version;
|
}
|
|
return null;
|
}
|
|
public string[] GetVersions(RegistryKey root)
|
{
|
var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false);
|
if (key == null)
|
return new string[] { };
|
var versionKey = key.OpenSubKey(strVersionKey, false);
|
if (versionKey == null)
|
return new string[] { };
|
return versionKey.GetSubKeyNames();
|
}
|
|
/// <summary>
|
/// Check if all Qt versions are valid and readable.
|
/// </summary>
|
/// Also sets the default Qt version to the newest version, if needed.
|
/// <param name="errorMessage"></param>
|
/// <returns>true, if we found an invalid version</returns>
|
public bool HasInvalidVersions(out string errorMessage)
|
{
|
var validVersions = new Dictionary<string, QtConfig>();
|
var invalidVersions = new List<string>();
|
|
foreach (var v in GetVersions()) {
|
if (v == "$(DefaultQtVersion)")
|
continue;
|
|
var vPath = GetInstallPath(v);
|
if (string.IsNullOrEmpty(vPath)) {
|
invalidVersions.Add(v);
|
continue;
|
}
|
|
if (vPath.StartsWith("SSH:") || vPath.StartsWith("WSL:"))
|
continue;
|
|
var qmakePath = Path.Combine(vPath, "bin", "qmake.exe");
|
if (!File.Exists(qmakePath))
|
qmakePath = Path.Combine(vPath, "qmake.exe");
|
if (!File.Exists(qmakePath)) {
|
invalidVersions.Add(v);
|
continue;
|
}
|
|
validVersions[v] = new QtConfig(vPath);
|
}
|
|
if (invalidVersions.Count > 0) {
|
errorMessage = "These Qt version are inaccessible:\n";
|
foreach (var invalidVersion in invalidVersions)
|
errorMessage += invalidVersion + " in " + GetInstallPath(invalidVersion) + "\n";
|
errorMessage += "Make sure that you have read access to all files in your Qt directories.";
|
|
// Is the default Qt version invalid?
|
var isDefaultQtVersionInvalid = false;
|
var defaultQtVersionName = GetDefaultVersion();
|
if (string.IsNullOrEmpty(defaultQtVersionName)) {
|
isDefaultQtVersionInvalid = true;
|
} else {
|
foreach (var name in invalidVersions) {
|
if (name == defaultQtVersionName) {
|
isDefaultQtVersionInvalid = true;
|
break;
|
}
|
}
|
}
|
|
// find the newest valid Qt version that can be used as default version
|
if (isDefaultQtVersionInvalid && validVersions.Count > 0) {
|
QtConfig defaultQtVersionConfig = null;
|
foreach (var vNameConfig in validVersions) {
|
var vName = vNameConfig.Key;
|
var v = vNameConfig.Value;
|
if (defaultQtVersionConfig == null) {
|
defaultQtVersionConfig = v;
|
defaultQtVersionName = vName;
|
continue;
|
}
|
if (defaultQtVersionConfig.VersionMajor < v.VersionMajor ||
|
(defaultQtVersionConfig.VersionMajor == v.VersionMajor && (defaultQtVersionConfig.VersionMinor < v.VersionMinor ||
|
(defaultQtVersionConfig.VersionMinor == v.VersionMinor && defaultQtVersionConfig.VersionPatch < v.VersionPatch)))) {
|
defaultQtVersionConfig = v;
|
defaultQtVersionName = vName;
|
}
|
}
|
if (defaultQtVersionConfig != null)
|
SaveDefaultVersion(defaultQtVersionName);
|
}
|
|
return true;
|
}
|
errorMessage = null;
|
return false;
|
}
|
|
public string GetInstallPath(string version)
|
{
|
if (version == "$(DefaultQtVersion)")
|
version = GetDefaultVersion();
|
return GetInstallPath(version, Registry.CurrentUser);
|
}
|
|
public string GetInstallPath(string version, RegistryKey root)
|
{
|
if (version == "$(DefaultQtVersion)")
|
version = GetDefaultVersion(root);
|
if (version == "$(QTDIR)")
|
return Environment.GetEnvironmentVariable("QTDIR");
|
|
var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false);
|
if (key == null)
|
return null;
|
var versionKey = key.OpenSubKey(strVersionKey + "\\" + version, false);
|
if (versionKey == null)
|
return null;
|
return (string)versionKey.GetValue("InstallDir");
|
}
|
|
public string GetInstallPath(EnvDTE.Project project)
|
{
|
var version = GetProjectQtVersion(project);
|
if (version == "$(DefaultQtVersion)")
|
version = GetDefaultVersion();
|
if (version == null)
|
return null;
|
return GetInstallPath(version);
|
}
|
|
public bool SaveVersion(string versionName, string path, bool checkPath = true)
|
{
|
var verName = versionName?.Trim().Replace(@"\", "_");
|
if (string.IsNullOrEmpty(verName))
|
return false;
|
var dir = string.Empty;
|
if (verName != "$(QTDIR)") {
|
DirectoryInfo di;
|
try {
|
di = new DirectoryInfo(path);
|
} catch {
|
di = null;
|
}
|
if (di?.Exists == true) {
|
dir = di.FullName;
|
} else if (!checkPath) {
|
dir = path;
|
} else {
|
return false;
|
}
|
}
|
|
string rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath;
|
string versionKeyPath = strVersionKey + "\\" + verName;
|
using (var key = Registry.CurrentUser.CreateSubKey(rootKeyPath)) {
|
if (key == null) {
|
Messages.Print(
|
"ERROR: root registry key creation failed");
|
return false;
|
}
|
using (var versionKey = key.CreateSubKey(versionKeyPath)) {
|
if (versionKey == null) {
|
Messages.Print(
|
"ERROR: version registry key creation failed");
|
return false;
|
}
|
versionKey.SetValue("InstallDir", dir);
|
}
|
}
|
RefreshVersionNames();
|
return true;
|
}
|
|
public void RemoveVersion(string versionName)
|
{
|
var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + regVersionPath, true);
|
if (key == null)
|
return;
|
key.DeleteSubKey(versionName);
|
key.Close();
|
RefreshVersionNames();
|
}
|
|
private bool IsVersionAvailable(string version)
|
{
|
var versionAvailable = false;
|
var versions = GetVersions();
|
foreach (var ver in versions) {
|
if (version == ver) {
|
versionAvailable = true;
|
break;
|
}
|
}
|
return versionAvailable;
|
}
|
|
public bool SaveProjectQtVersion(EnvDTE.Project project, string version)
|
{
|
return SaveProjectQtVersion(project, version, project.ConfigurationManager.ActiveConfiguration.PlatformName);
|
}
|
|
public bool SaveProjectQtVersion(EnvDTE.Project project, string version, string platform)
|
{
|
if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)")
|
return false;
|
if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) {
|
var vcPro = project.Object as VCProject;
|
if (vcPro == null)
|
return false;
|
foreach (VCConfiguration3 config in (IVCCollection)vcPro.Configurations) {
|
config.SetPropertyValue(Resources.projLabelQtSettings, true,
|
"QtInstall", version);
|
}
|
return true;
|
}
|
var key = "Qt5Version " + platform;
|
if (!project.Globals.get_VariableExists(key) || project.Globals[key].ToString() != version)
|
project.Globals[key] = version;
|
if (!project.Globals.get_VariablePersists(key))
|
project.Globals.set_VariablePersists(key, true);
|
return true;
|
}
|
|
public string GetProjectQtVersion(EnvDTE.Project project)
|
{
|
EnvDTE.Configuration config = null;
|
try {
|
config = project.ConfigurationManager.ActiveConfiguration;
|
} catch {
|
// Accessing the ActiveConfiguration property throws an exception
|
// if there's an "unconfigured" platform in the Solution platform combo box.
|
config = project.ConfigurationManager.Item(1);
|
}
|
var version = GetProjectQtVersion(project, config);
|
|
if (version == null && project.Globals.get_VariablePersists("Qt5Version")) {
|
version = (string)project.Globals["Qt5Version"];
|
ExpandEnvironmentVariablesInQtVersion(ref version);
|
return VerifyIfQtVersionExists(version) ? version : null;
|
}
|
|
if (version == null)
|
version = GetSolutionQtVersion(project.DTE.Solution);
|
|
return version;
|
}
|
|
public string GetProjectQtVersion(EnvDTE.Project project, EnvDTE.Configuration config)
|
{
|
if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
|
return QtProject.GetPropertyValue(project, config, "QtInstall");
|
|
var key = "Qt5Version " + config.PlatformName;
|
if (!project.Globals.get_VariablePersists(key))
|
return null;
|
var version = (string)project.Globals[key];
|
ExpandEnvironmentVariablesInQtVersion(ref version);
|
return VerifyIfQtVersionExists(version) ? version : null;
|
}
|
|
public string GetProjectQtVersion(EnvDTE.Project project, string platform)
|
{
|
if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
|
return GetProjectQtVersion(project);
|
|
var key = "Qt5Version " + platform;
|
if (!project.Globals.get_VariablePersists(key))
|
return null;
|
var version = (string)project.Globals[key];
|
ExpandEnvironmentVariablesInQtVersion(ref version);
|
return VerifyIfQtVersionExists(version) ? version : null;
|
}
|
|
private static void ExpandEnvironmentVariablesInQtVersion(ref string version)
|
{
|
if (version != "$(QTDIR)" && version != "$(DefaultQtVersion)") {
|
// Make it possible to specify the version name
|
// via an environment variable
|
var regExp =
|
new System.Text.RegularExpressions.Regex("\\$\\((?<VarName>\\S+)\\)");
|
var match = regExp.Match(version);
|
if (match.Success) {
|
var env = match.Groups["VarName"].Value;
|
version = Environment.GetEnvironmentVariable(env);
|
}
|
}
|
}
|
|
public bool SaveSolutionQtVersion(EnvDTE.Solution solution, string version)
|
{
|
if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)")
|
return false;
|
solution.Globals["Qt5Version"] = version;
|
if (!solution.Globals.get_VariablePersists("Qt5Version"))
|
solution.Globals.set_VariablePersists("Qt5Version", true);
|
return true;
|
}
|
|
public string GetSolutionQtVersion(EnvDTE.Solution solution)
|
{
|
if (solution == null)
|
return null;
|
|
if (solution.Globals.get_VariableExists("Qt5Version")) {
|
var version = (string)solution.Globals["Qt5Version"];
|
return VerifyIfQtVersionExists(version) ? version : null;
|
}
|
|
return null;
|
}
|
|
public string GetDefaultVersion()
|
{
|
return GetDefaultVersion(Registry.CurrentUser);
|
}
|
|
public string GetDefaultVersion(RegistryKey root)
|
{
|
string defaultVersion = null;
|
try {
|
var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false);
|
if (key != null)
|
defaultVersion = (string)key.GetValue("DefaultQtVersion");
|
} catch {
|
Messages.DisplayWarningMessage(SR.GetString("QtVersionManager_CannotLoadQtVersion"));
|
}
|
|
if (defaultVersion == null) {
|
MergeVersions();
|
var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false);
|
if (key != null) {
|
var versions = GetVersions();
|
if (versions != null && versions.Length > 0)
|
defaultVersion = versions[versions.Length - 1];
|
if (defaultVersion != null)
|
SaveDefaultVersion(defaultVersion);
|
}
|
if (defaultVersion == null) {
|
// last fallback... try QTDIR
|
var qtDir = Environment.GetEnvironmentVariable("QTDIR");
|
if (qtDir == null)
|
return null;
|
var d = new DirectoryInfo(qtDir);
|
SaveVersion(d.Name, d.FullName);
|
if (SaveDefaultVersion(d.Name))
|
defaultVersion = d.Name;
|
}
|
}
|
return VerifyIfQtVersionExists(defaultVersion) ? defaultVersion : null;
|
}
|
|
public bool SaveDefaultVersion(string version)
|
{
|
if (version == "$(DefaultQtVersion)")
|
return false;
|
var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + regVersionPath);
|
if (key == null)
|
return false;
|
key.SetValue("DefaultQtVersion", version);
|
return true;
|
}
|
|
private void MergeVersions()
|
{
|
var hkcuVersions = GetVersions();
|
var hklmVersions = GetVersions(Registry.LocalMachine);
|
|
var hkcuInstDirs = new string[hkcuVersions.Length];
|
for (var i = 0; i < hkcuVersions.Length; ++i)
|
hkcuInstDirs[i] = GetInstallPath(hkcuVersions[i]);
|
var hklmInstDirs = new string[hklmVersions.Length];
|
for (var i = 0; i < hklmVersions.Length; ++i)
|
hklmInstDirs[i] = GetInstallPath(hklmVersions[i], Registry.LocalMachine);
|
|
for (var i = 0; i < hklmVersions.Length; ++i) {
|
if (hklmInstDirs[i] == null)
|
continue;
|
|
var found = false;
|
for (var j = 0; j < hkcuInstDirs.Length; ++j) {
|
if (hkcuInstDirs[j] != null
|
&& hkcuInstDirs[j].ToLower() == hklmInstDirs[i].ToLower()) {
|
found = true;
|
break;
|
}
|
}
|
if (!found) {
|
for (var j = 0; j < hkcuVersions.Length; ++j) {
|
if (hkcuVersions[j] != null
|
&& hkcuVersions[j] == hklmVersions[i]) {
|
found = true;
|
break;
|
}
|
}
|
if (!found)
|
SaveVersion(hklmVersions[i], hklmInstDirs[i]);
|
}
|
}
|
}
|
|
private bool VerifyIfQtVersionExists(string version)
|
{
|
if (version == "$(DefaultQtVersion)")
|
version = GetDefaultVersion();
|
if (!string.IsNullOrEmpty(version)) {
|
var regExp =
|
new System.Text.RegularExpressions.Regex("\\$\\(.*\\)");
|
if (regExp.IsMatch(version))
|
return true;
|
return Directory.Exists(GetInstallPath(version));
|
}
|
|
return false;
|
}
|
}
|
}
|