aboutsummaryrefslogtreecommitdiffstats
path: root/src/qtvstools.core/HelperFunctions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/qtvstools.core/HelperFunctions.cs')
-rw-r--r--src/qtvstools.core/HelperFunctions.cs1900
1 files changed, 1900 insertions, 0 deletions
diff --git a/src/qtvstools.core/HelperFunctions.cs b/src/qtvstools.core/HelperFunctions.cs
new file mode 100644
index 00000000..06a3e8d9
--- /dev/null
+++ b/src/qtvstools.core/HelperFunctions.cs
@@ -0,0 +1,1900 @@
+/****************************************************************************
+**
+** 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 EnvDTE;
+using Microsoft.VisualStudio.VCProjectEngine;
+using Microsoft.Win32;
+using QtVsTools.Core.QtMsBuild;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+
+using Process = System.Diagnostics.Process;
+
+namespace QtVsTools.Core
+{
+ public static class HelperFunctions
+ {
+ public static string FindQtDirWithTools(Project project)
+ {
+ var versionManager = QtVersionManager.The();
+ string projectQtVersion = null;
+ if (IsQtProject(project))
+ projectQtVersion = versionManager.GetProjectQtVersion(project);
+ return FindQtDirWithTools(projectQtVersion);
+ }
+
+ public static string FindQtDirWithTools(string projectQtVersion)
+ {
+ string tool = null;
+ return FindQtDirWithTools(tool, projectQtVersion);
+ }
+
+ public static string FindQtDirWithTools(string tool, string projectQtVersion)
+ {
+ if (!string.IsNullOrEmpty(tool)) {
+ if (!tool.StartsWith("\\bin\\", StringComparison.OrdinalIgnoreCase))
+ tool = "\\bin\\" + tool;
+ if (!tool.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
+ tool += ".exe";
+ }
+
+ var versionManager = QtVersionManager.The();
+ string qtDir = null;
+ if (projectQtVersion != null)
+ qtDir = versionManager.GetInstallPath(projectQtVersion);
+
+ if (qtDir == null)
+ qtDir = Environment.GetEnvironmentVariable("QTDIR");
+
+ var found = false;
+ if (tool == null) {
+ found = File.Exists(qtDir + "\\bin\\designer.exe")
+ && File.Exists(qtDir + "\\bin\\linguist.exe");
+ } else {
+ found = File.Exists(qtDir + tool);
+ }
+ if (!found) {
+ VersionInformation exactlyMatchingVersion = null;
+ VersionInformation matchingVersion = null;
+ VersionInformation somehowMatchingVersion = null;
+ var viProjectQtVersion = versionManager.GetVersionInfo(projectQtVersion);
+ foreach (var qtVersion in versionManager.GetVersions()) {
+ var vi = versionManager.GetVersionInfo(qtVersion);
+ if (tool == null) {
+ found = File.Exists(vi.qtDir + "\\bin\\designer.exe")
+ && File.Exists(vi.qtDir + "\\bin\\linguist.exe");
+ } else {
+ found = File.Exists(vi.qtDir + tool);
+ }
+ if (!found)
+ continue;
+
+ if (viProjectQtVersion != null
+ && vi.qtMajor == viProjectQtVersion.qtMajor
+ && vi.qtMinor == viProjectQtVersion.qtMinor) {
+ exactlyMatchingVersion = vi;
+ break;
+ }
+ if (matchingVersion == null
+ && viProjectQtVersion != null
+ && vi.qtMajor == viProjectQtVersion.qtMajor) {
+ matchingVersion = vi;
+ }
+ if (somehowMatchingVersion == null)
+ somehowMatchingVersion = vi;
+ }
+
+ if (exactlyMatchingVersion != null)
+ qtDir = exactlyMatchingVersion.qtDir;
+ else if (matchingVersion != null)
+ qtDir = matchingVersion.qtDir;
+ else if (somehowMatchingVersion != null)
+ qtDir = somehowMatchingVersion.qtDir;
+ else
+ qtDir = null;
+ }
+ return qtDir;
+ }
+
+ static readonly HashSet<string> _sources = new HashSet<string>(new [] { ".c", ".cpp", ".cxx"},
+ StringComparer.OrdinalIgnoreCase);
+ static public bool IsSourceFile(string fileName)
+ {
+ return _sources.Contains(Path.GetExtension(fileName));
+ }
+
+ static readonly HashSet<string> _headers = new HashSet<string>(new[] { ".h", ".hpp", ".hxx" },
+ StringComparer.OrdinalIgnoreCase);
+ static public bool IsHeaderFile(string fileName)
+ {
+ return _headers.Contains(Path.GetExtension(fileName));
+ }
+
+ public static bool IsUicFile(string fileName)
+ {
+ return ".ui".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool IsMocFile(string fileName)
+ {
+ return ".moc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool IsQrcFile(string fileName)
+ {
+ return ".qrc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool IsWinRCFile(string fileName)
+ {
+ return ".rc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool IsTranslationFile(string fileName)
+ {
+ return ".ts".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
+ }
+
+ static public void SetDebuggingEnvironment(Project prj)
+ {
+ SetDebuggingEnvironment(prj, string.Empty);
+ }
+
+ static public void SetDebuggingEnvironment(Project prj, string solutionConfig)
+ {
+ SetDebuggingEnvironment(prj, "PATH=$(QTDIR)\\bin;$(PATH)", false, solutionConfig);
+ }
+
+ static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite)
+ {
+ SetDebuggingEnvironment(prj, envpath, overwrite, string.Empty);
+ }
+
+ static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite, string solutionConfig)
+ {
+ if (QtProject.GetFormatVersion(prj) >= Resources.qtMinFormatVersion_Settings)
+ return;
+
+ // Get platform name from given solution configuration
+ // or if not available take the active configuration
+ var activePlatformName = string.Empty;
+ if (string.IsNullOrEmpty(solutionConfig)) {
+ // First get active configuration cause not given as parameter
+ try {
+ var activeConf = prj.ConfigurationManager.ActiveConfiguration;
+ activePlatformName = activeConf.PlatformName;
+ } catch {
+ Messages.PaneMessage(prj.DTE, "Could not get the active platform name.");
+ }
+ } else {
+ activePlatformName = solutionConfig.Split('|')[1];
+ }
+
+ var vcprj = prj.Object as VCProject;
+ foreach (VCConfiguration conf in vcprj.Configurations as IVCCollection) {
+ // Set environment only for active (or given) platform
+ var currentPlatform = conf.Platform as VCPlatform;
+ if (currentPlatform == null || currentPlatform.Name != activePlatformName)
+ continue;
+
+ var de = conf.DebugSettings as VCDebugSettings;
+ if (de == null)
+ continue;
+
+ // See: https://connect.microsoft.com/VisualStudio/feedback/details/619702
+ // Project | Properties | Configuration Properties | Debugging | Environment
+ //
+ // Issue: Substitution of ";" to "%3b"
+ // Answer: This behavior currently is by design as ';' is a special MSBuild
+ // character and needs to be escaped. In the Project Properties we show this
+ // escaped value, but it should be the original when we use it.
+ envpath = envpath.Replace("%3b", ";");
+ de.Environment = de.Environment.Replace("%3b", ";");
+
+ var index = envpath.LastIndexOf(";$(PATH)", StringComparison.Ordinal);
+ var withoutPath = (index >= 0 ? envpath.Remove(index) : envpath);
+
+ if (overwrite || string.IsNullOrEmpty(de.Environment))
+ de.Environment = envpath;
+ else if (!de.Environment.Contains(envpath) && !de.Environment.Contains(withoutPath)) {
+ var m = Regex.Match(de.Environment, "PATH\\s*=\\s*");
+ if (m.Success) {
+ de.Environment = Regex.Replace(de.Environment, "PATH\\s*=\\s*", withoutPath + ";");
+ if (!de.Environment.Contains("$(PATH)") && !de.Environment.Contains("%PATH%")) {
+ if (!de.Environment.EndsWith(";", StringComparison.Ordinal))
+ de.Environment = de.Environment + ";";
+ de.Environment += "$(PATH)";
+ }
+ } else {
+ if (!string.IsNullOrEmpty(de.Environment))
+ de.Environment += "\n";
+ de.Environment += envpath;
+ }
+ }
+ }
+ }
+
+ public static bool IsProjectInSolution(DTE dteObject, string fullName)
+ {
+ var fi = new FileInfo(fullName);
+
+ foreach (var p in ProjectsInSolution(dteObject)) {
+ if (p.FullName.ToLower() == fi.FullName.ToLower())
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the normalized file path of a given file.
+ /// </summary>
+ /// <param name="name">file name</param>
+ static public string NormalizeFilePath(string name)
+ {
+ var fi = new FileInfo(name);
+ return fi.FullName;
+ }
+
+ static public string NormalizeRelativeFilePath(string path)
+ {
+ if (path == null)
+ return ".\\";
+
+ path = path.Trim();
+ path = path.Replace("/", "\\");
+
+ var tmp = string.Empty;
+ while (tmp != path) {
+ tmp = path;
+ path = path.Replace("\\\\", "\\");
+ }
+
+ path = path.Replace("\"", "");
+
+ if (path != "." && !IsAbsoluteFilePath(path) && !path.StartsWith(".\\", StringComparison.Ordinal)
+ && !path.StartsWith("$", StringComparison.Ordinal))
+ path = ".\\" + path;
+
+ if (path.EndsWith("\\", StringComparison.Ordinal))
+ path = path.Substring(0, path.Length - 1);
+
+ return path;
+ }
+
+ static public bool IsAbsoluteFilePath(string path)
+ {
+ path = path.Trim();
+ if (path.Length >= 2 && path[1] == ':')
+ return true;
+ if (path.StartsWith("\\", StringComparison.Ordinal) || path.StartsWith("/", StringComparison.Ordinal))
+ return true;
+
+ return false;
+ }
+
+ /// <summary>
+ /// Reads lines from a .pro file that is opened with a StreamReader
+ /// and concatenates strings that end with a backslash.
+ /// </summary>
+ /// <param name="streamReader"></param>
+ /// <returns>the composite string</returns>
+ static private string ReadProFileLine(StreamReader streamReader)
+ {
+ var line = streamReader.ReadLine();
+ if (line == null)
+ return null;
+
+ line = line.TrimEnd(' ', '\t');
+ while (line.EndsWith("\\", StringComparison.Ordinal)) {
+ line = line.Remove(line.Length - 1);
+ var appendix = streamReader.ReadLine();
+ if (appendix != null)
+ line += appendix.TrimEnd(' ', '\t');
+ }
+ return line;
+ }
+
+ /// <summary>
+ /// Reads a .pro file and returns true if it is a subdirs template.
+ /// </summary>
+ /// <param name="profile">full name of .pro file to read</param>
+ /// <returns>true if this is a subdirs file</returns>
+ static public bool IsSubDirsFile(string profile)
+ {
+ StreamReader sr = null;
+ try {
+ sr = new StreamReader(profile);
+
+ var line = string.Empty;
+ while ((line = ReadProFileLine(sr)) != null) {
+ line = line.Replace(" ", string.Empty).Replace("\t", string.Empty);
+ if (line.StartsWith("TEMPLATE", StringComparison.Ordinal))
+ return line.StartsWith("TEMPLATE=subdirs", StringComparison.Ordinal);
+ }
+ } catch (Exception e) {
+ Messages.DisplayErrorMessage(e);
+ } finally {
+ if (sr != null)
+ sr.Dispose();
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the relative path between a given file and a path.
+ /// </summary>
+ /// <param name="path">absolute path</param>
+ /// <param name="file">absolute file name</param>
+ public static string GetRelativePath(string path, string file)
+ {
+ if (file == null || path == null)
+ return "";
+ var fi = new FileInfo(file);
+ var di = new DirectoryInfo(path);
+
+ char[] separator = { '\\' };
+ var fiArray = fi.FullName.Split(separator);
+ var dir = di.FullName;
+ while (dir.EndsWith("\\", StringComparison.Ordinal))
+ dir = dir.Remove(dir.Length - 1, 1);
+ var diArray = dir.Split(separator);
+
+ var minLen = fiArray.Length < diArray.Length ? fiArray.Length : diArray.Length;
+ int i = 0, j = 0, commonParts = 0;
+
+ while (i < minLen && fiArray[i].ToLower() == diArray[i].ToLower()) {
+ commonParts++;
+ i++;
+ }
+
+ if (commonParts < 1)
+ return fi.FullName;
+
+ var result = string.Empty;
+
+ for (j = i; j < fiArray.Length; j++) {
+ if (j == i)
+ result = fiArray[j];
+ else
+ result += "\\" + fiArray[j];
+ }
+ while (i < diArray.Length) {
+ result = "..\\" + result;
+ i++;
+ }
+ //MessageBox.Show(path + "\n" + file + "\n" + result);
+ if (result.StartsWith("..\\", StringComparison.Ordinal))
+ return result;
+ return ".\\" + result;
+ }
+
+ /// <summary>
+ /// Replaces a string in the commandLine, description, outputs and additional dependencies
+ /// in all Custom build tools of the project
+ /// </summary>
+ /// <param name="project">Project</param>
+ /// <param name="oldString">String, which is going to be replaced</param>
+ /// <param name="oldString">String, which is going to replace the other one</param>
+ /// <returns></returns>
+ public static void ReplaceInCustomBuildTools(Project project, string oldString, string replaceString)
+ {
+ var vcPro = (VCProject) project.Object;
+ if (vcPro == null)
+ return;
+
+ var qtMsBuild = new QtMsBuildContainer(new VCPropertyStorageProvider());
+ qtMsBuild.BeginSetItemProperties();
+ foreach (VCFile vcfile in (IVCCollection) vcPro.Files) {
+ foreach (VCFileConfiguration config in (IVCCollection) vcfile.FileConfigurations) {
+ try {
+ if (vcfile.ItemType == "CustomBuild") {
+ var tool = GetCustomBuildTool(config);
+ if (tool == null)
+ continue;
+
+ tool.CommandLine = tool.CommandLine
+ .Replace(oldString, replaceString,
+ StringComparison.OrdinalIgnoreCase);
+ tool.Description = tool.Description
+ .Replace(oldString, replaceString,
+ StringComparison.OrdinalIgnoreCase);
+ tool.Outputs = tool.Outputs
+ .Replace(oldString, replaceString,
+ StringComparison.OrdinalIgnoreCase);
+ tool.AdditionalDependencies = tool.AdditionalDependencies
+ .Replace(oldString, replaceString,
+ StringComparison.OrdinalIgnoreCase);
+ } else {
+ var tool = new QtCustomBuildTool(config, qtMsBuild);
+ tool.CommandLine = tool.CommandLine
+ .Replace(oldString, replaceString,
+ StringComparison.OrdinalIgnoreCase);
+ }
+ } catch (Exception) {
+ }
+ }
+ }
+ qtMsBuild.EndSetItemProperties();
+ }
+
+ /// <summary>
+ /// Since VS2010 it is possible to have VCCustomBuildTools without commandlines
+ /// for certain filetypes. We are not interested in them and thus try to read the
+ /// tool's commandline. If this causes an exception, we ignore it.
+ /// There does not seem to be another way for checking which kind of tool it is.
+ /// </summary>
+ /// <param name="config">File configuration</param>
+ /// <returns></returns>
+ static public VCCustomBuildTool GetCustomBuildTool(VCFileConfiguration config)
+ {
+ var file = config.File as VCFile;
+ if (file == null || file.ItemType != "CustomBuild")
+ return null;
+
+ var tool = config.Tool as VCCustomBuildTool;
+ if (tool == null)
+ return null;
+
+ try {
+ // TODO: The return value is not used at all?
+ var cmdLine = tool.CommandLine;
+ } catch {
+ return null;
+ }
+ return tool;
+ }
+
+ /// <summary>
+ /// Since VS2010 we have to ensure, that a custom build tool is present
+ /// if we want to use it. In order to do so, the ProjectItem's ItemType
+ /// has to be "CustomBuild"
+ /// </summary>
+ /// <param name="projectItem">Project Item which needs to have custom build tool</param>
+ static public void EnsureCustomBuildToolAvailable(ProjectItem projectItem)
+ {
+ foreach (Property prop in projectItem.Properties) {
+ if (prop.Name == "ItemType") {
+ if ((string) prop.Value != "CustomBuild")
+ prop.Value = "CustomBuild";
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// As Qmake -tp vc Adds the full path to the additional dependencies
+ /// we need to do the same when toggling project kind to qmake generated.
+ /// </summary>
+ /// <returns></returns>
+ private static string AddFullPathToAdditionalDependencies(string qtDir, string additionalDependencies)
+ {
+ var returnString = additionalDependencies;
+ returnString =
+ Regex.Replace(returnString, "Qt(\\S+5?)\\.lib", qtDir + "\\lib\\Qt${1}.lib");
+ returnString =
+ Regex.Replace(returnString, "(qtmaind?5?)\\.lib", qtDir + "\\lib\\${1}.lib");
+ returnString =
+ Regex.Replace(returnString, "(enginiod?5?)\\.lib", qtDir + "\\lib\\${1}.lib");
+ return returnString;
+ }
+
+ /// <summary>
+ /// Toggles the kind of a project. If the project is a QMake generated project (qmake -tp vc)
+ /// it is transformed to an Qt VS Tools project and vice versa.
+ /// </summary>
+ /// <param name="project">Project</param>
+ /// <returns></returns>
+ public static void ToggleProjectKind(Project project)
+ {
+ if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings)
+ return;
+
+ string qtDir = null;
+ var vcPro = (VCProject) project.Object;
+ if (!IsQMakeProject(project))
+ return;
+ if (IsQtProject(project)) {
+ // TODO: qtPro is never used.
+ var qtPro = QtProject.Create(project);
+ var vm = QtVersionManager.The();
+ qtDir = vm.GetInstallPath(project);
+
+ foreach (var global in (string[]) project.Globals.VariableNames) {
+ if (global.StartsWith("Qt5Version", StringComparison.Ordinal))
+ project.Globals.set_VariablePersists(global, false);
+ }
+
+ foreach (VCConfiguration config in (IVCCollection) vcPro.Configurations) {
+ var compiler = CompilerToolWrapper.Create(config);
+ var linker = (VCLinkerTool) ((IVCCollection) config.Tools).Item("VCLinkerTool");
+ var librarian = (VCLibrarianTool) ((IVCCollection) config.Tools).Item("VCLibrarianTool");
+ if (compiler != null) {
+ var additionalIncludes = compiler.GetAdditionalIncludeDirectories();
+ additionalIncludes = additionalIncludes.Replace("$(QTDIR)", qtDir,
+ StringComparison.OrdinalIgnoreCase);
+ compiler.SetAdditionalIncludeDirectories(additionalIncludes);
+ }
+ if (linker != null) {
+ linker.AdditionalLibraryDirectories = linker.AdditionalLibraryDirectories.
+ Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase);
+ linker.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, linker.AdditionalDependencies);
+ } else {
+ librarian.AdditionalLibraryDirectories = librarian.AdditionalLibraryDirectories
+ .Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase);
+ librarian.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, librarian.AdditionalDependencies);
+ }
+ }
+
+ ReplaceInCustomBuildTools(project, "$(QTDIR)", qtDir);
+ } else {
+ qtDir = GetQtDirFromQMakeProject(project);
+
+ var vm = QtVersionManager.The();
+ var qtVersion = vm.GetQtVersionFromInstallDir(qtDir);
+ if (qtVersion == null)
+ qtVersion = vm.GetDefaultVersion();
+ if (qtDir == null)
+ qtDir = vm.GetInstallPath(qtVersion);
+ var vi = vm.GetVersionInfo(qtVersion);
+ var platformName = vi.GetVSPlatformName();
+ vm.SaveProjectQtVersion(project, qtVersion, platformName);
+ var qtPro = QtProject.Create(project);
+ if (!qtPro.SelectSolutionPlatform(platformName) || !qtPro.HasPlatform(platformName)) {
+ var newProject = false;
+ qtPro.CreatePlatform("Win32", platformName, null, vi, ref newProject);
+ if (!qtPro.SelectSolutionPlatform(platformName))
+ Messages.PaneMessage(project.DTE, "Can't select the platform " + platformName + ".");
+ }
+
+ var activeConfig = project.ConfigurationManager.ActiveConfiguration.ConfigurationName;
+ var activeVCConfig = (VCConfiguration) ((IVCCollection) qtPro.VCProject.Configurations).Item(activeConfig);
+ if (activeVCConfig.ConfigurationType == ConfigurationTypes.typeDynamicLibrary) {
+ var compiler = CompilerToolWrapper.Create(activeVCConfig);
+ var linker = (VCLinkerTool) ((IVCCollection) activeVCConfig.Tools).Item("VCLinkerTool");
+ var ppdefs = compiler.GetPreprocessorDefinitions();
+ if (ppdefs != null
+ && ppdefs.IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1
+ && ppdefs.IndexOf("QDESIGNER_EXPORT_WIDGETS", StringComparison.Ordinal) > -1
+ && ppdefs.IndexOf("QtDesigner", StringComparison.Ordinal) > -1
+ && linker.AdditionalDependencies != null
+ && linker.AdditionalDependencies.IndexOf("QtDesigner", StringComparison.Ordinal) > -1) {
+ qtPro.MarkAsDesignerPluginProject();
+ }
+ }
+
+ CleanupQMakeDependencies(project);
+
+ foreach (VCConfiguration config in (IVCCollection) vcPro.Configurations) {
+ var compiler = CompilerToolWrapper.Create(config);
+ var linker = (VCLinkerTool) ((IVCCollection) config.Tools).Item("VCLinkerTool");
+
+ if (compiler != null) {
+ var additionalIncludes = compiler.AdditionalIncludeDirectories;
+ if (additionalIncludes != null) {
+ ReplaceDirectory(ref additionalIncludes, qtDir, "$(QTDIR)", project);
+ compiler.AdditionalIncludeDirectories = additionalIncludes;
+ }
+ }
+ if (linker != null) {
+ var linkerToolWrapper = new LinkerToolWrapper(linker);
+ var paths = linkerToolWrapper.AdditionalLibraryDirectories;
+ if (paths != null) {
+ ReplaceDirectory(ref paths, qtDir, "$(QTDIR)", project);
+ linkerToolWrapper.AdditionalLibraryDirectories = paths;
+ }
+ }
+ }
+
+ ReplaceInCustomBuildTools(project, qtDir, "$(QTDIR)");
+ qtPro.TranslateFilterNames();
+ }
+ project.Save(project.FullName);
+ }
+
+ /// <summary>
+ /// Replaces every occurrence of oldDirectory with replacement in the array of strings.
+ /// Parameter oldDirectory must be an absolute path.
+ /// This function converts relative directories to absolute paths internally
+ /// and replaces them, if necessary. If no replacement is done, the path isn't altered.
+ /// </summary>
+ /// <param name="project">The project is needed to convert relative paths to absolute paths.</param>
+ private static void ReplaceDirectory(ref List<string> paths, string oldDirectory, string replacement, Project project)
+ {
+ for (var i = 0; i < paths.Count; ++i) {
+ var dirName = paths[i];
+ if (dirName.StartsWith("\"", StringComparison.Ordinal) && dirName.EndsWith("\"", StringComparison.Ordinal)) {
+ dirName = dirName.Substring(1, dirName.Length - 2);
+ }
+ if (!Path.IsPathRooted(dirName)) {
+ // convert to absolute path
+ dirName = Path.Combine(Path.GetDirectoryName(project.FullName), dirName);
+ dirName = Path.GetFullPath(dirName);
+ var alteredDirName = dirName.Replace(oldDirectory, replacement, StringComparison
+ .OrdinalIgnoreCase);
+ if (alteredDirName == dirName)
+ continue;
+ dirName = alteredDirName;
+ } else {
+ dirName = dirName.Replace(oldDirectory, replacement, StringComparison
+ .OrdinalIgnoreCase);
+ }
+ paths[i] = dirName;
+ }
+ }
+
+ public static string GetQtDirFromQMakeProject(Project project)
+ {
+ var vcProject = project.Object as VCProject;
+ if (vcProject == null)
+ return null;
+
+ try {
+ foreach (VCConfiguration projectConfig in vcProject.Configurations as IVCCollection) {
+ var compiler = CompilerToolWrapper.Create(projectConfig);
+ if (compiler != null) {
+ var additionalIncludeDirectories = compiler.AdditionalIncludeDirectories;
+ if (additionalIncludeDirectories != null) {
+ foreach (var dir in additionalIncludeDirectories) {
+ var subdir = Path.GetFileName(dir);
+ if (subdir != "QtCore" && subdir != "QtGui") // looking for Qt include directories
+ continue;
+ var dirName = Path.GetDirectoryName(dir); // cd ..
+ dirName = Path.GetDirectoryName(dirName); // cd ..
+ if (!Path.IsPathRooted(dirName)) {
+ var projectDir = Path.GetDirectoryName(project.FullName);
+ dirName = Path.Combine(projectDir, dirName);
+ dirName = Path.GetFullPath(dirName);
+ }
+ return dirName;
+ }
+ }
+ }
+
+ var linker = (VCLinkerTool) ((IVCCollection) projectConfig.Tools).Item("VCLinkerTool");
+ if (linker != null) {
+ var linkerWrapper = new LinkerToolWrapper(linker);
+ var linkerPaths = linkerWrapper.AdditionalDependencies;
+ if (linkerPaths != null) {
+ foreach (var library in linkerPaths) {
+ var idx = library.IndexOf("\\lib\\qtmain.lib", StringComparison.OrdinalIgnoreCase);
+ if (idx == -1)
+ idx = library.IndexOf("\\lib\\qtmaind.lib", StringComparison.OrdinalIgnoreCase);
+ if (idx == -1)
+ idx = library.IndexOf("\\lib\\qtcore5.lib", StringComparison.OrdinalIgnoreCase);
+ if (idx == -1)
+ idx = library.IndexOf("\\lib\\qtcored5.lib", StringComparison.OrdinalIgnoreCase);
+ if (idx == -1)
+ continue;
+
+ var dirName = Path.GetDirectoryName(library);
+ dirName = Path.GetDirectoryName(dirName); // cd ..
+ if (!Path.IsPathRooted(dirName)) {
+ var projectDir = Path.GetDirectoryName(project.FullName);
+ dirName = Path.Combine(projectDir, dirName);
+ dirName = Path.GetFullPath(dirName);
+ }
+
+ return dirName;
+ }
+ }
+
+ linkerPaths = linkerWrapper.AdditionalLibraryDirectories;
+ if (linkerPaths != null) {
+ foreach (var libDir in linkerPaths) {
+ var dirName = libDir;
+ if (!Path.IsPathRooted(dirName)) {
+ var projectDir = Path.GetDirectoryName(project.FullName);
+ dirName = Path.Combine(projectDir, dirName);
+ dirName = Path.GetFullPath(dirName);
+ }
+
+ if (File.Exists(dirName + "\\qtmain.lib") ||
+ File.Exists(dirName + "\\qtmaind.lib") ||
+ File.Exists(dirName + "\\QtCore5.lib") ||
+ File.Exists(dirName + "\\QtCored5.lib")) {
+ return Path.GetDirectoryName(dirName);
+ }
+ }
+ }
+ }
+ }
+ } catch { }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Return true if the project is a Qt project, otherwise false.
+ /// </summary>
+ /// <param name="proj">project</param>
+ /// <returns></returns>
+ public static bool IsQtProject(VCProject proj)
+ {
+ if (!IsQMakeProject(proj))
+ return false;
+
+ if (QtProject.GetFormatVersion(proj) >= Resources.qtMinFormatVersion_Settings)
+ return true;
+
+ var envPro = proj.Object as Project;
+ if (envPro.Globals == null || envPro.Globals.VariableNames == null)
+ return false;
+
+ foreach (var global in envPro.Globals.VariableNames as string[]) {
+ if (global.StartsWith("Qt5Version", StringComparison.Ordinal) && envPro.Globals.get_VariablePersists(global))
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if the specified project is a Qt Project.
+ /// </summary>
+ /// <param name="proj">project</param>
+ public static bool IsQtProject(Project proj)
+ {
+ try {
+ if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
+ return IsQtProject(proj.Object as VCProject);
+ } catch { }
+ return false;
+ }
+
+ /// <summary>
+ /// Return true if the project is a QMake -tp vc project, otherwise false.
+ /// </summary>
+ /// <param name="proj">project</param>
+ /// <returns></returns>
+ public static bool IsQMakeProject(VCProject proj)
+ {
+ if (proj == null)
+ return false;
+ var keyword = proj.keyword;
+ if (keyword == null ||
+ (!keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal)
+ && !keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true if the specified project is a QMake -tp vc Project.
+ /// </summary>
+ /// <param name="proj">project</param>
+ public static bool IsQMakeProject(Project proj)
+ {
+ try {
+ if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
+ return IsQMakeProject(proj.Object as VCProject);
+ } catch { }
+ return false;
+ }
+
+ public static void CleanupQMakeDependencies(Project project)
+ {
+ var vcPro = (VCProject) project.Object;
+ // clean up qmake mess
+ var rxp1 = new Regex("\\bQt\\w+d?5?\\.lib\\b");
+ var rxp2 = new Regex("\\bQAx\\w+\\.lib\\b");
+ var rxp3 = new Regex("\\bqtmaind?.lib\\b");
+ var rxp4 = new Regex("\\benginiod?.lib\\b");
+ foreach (VCConfiguration cfg in (IVCCollection) vcPro.Configurations) {
+ var linker = (VCLinkerTool) ((IVCCollection) cfg.Tools).Item("VCLinkerTool");
+ if (linker == null || linker.AdditionalDependencies == null)
+ continue;
+ var linkerWrapper = new LinkerToolWrapper(linker);
+ var deps = linkerWrapper.AdditionalDependencies;
+ var newDeps = new List<string>();
+ foreach (var lib in deps) {
+ var m1 = rxp1.Match(lib);
+ var m2 = rxp2.Match(lib);
+ var m3 = rxp3.Match(lib);
+ var m4 = rxp4.Match(lib);
+ if (m1.Success)
+ newDeps.Add(m1.ToString());
+ else if (m2.Success)
+ newDeps.Add(m2.ToString());
+ else if (m3.Success)
+ newDeps.Add(m3.ToString());
+ else if (m4.Success)
+ newDeps.Add(m4.ToString());
+ else
+ newDeps.Add(lib);
+ }
+ // Remove Duplicates
+ var uniques = new Dictionary<string, int>();
+ foreach (var dep in newDeps)
+ uniques[dep] = 1;
+ var uniqueList = new List<string>(uniques.Keys);
+ linkerWrapper.AdditionalDependencies = uniqueList;
+ }
+ }
+
+ /// <summary>
+ /// Deletes the file's directory if it is empty (not deleting the file itself so it must
+ /// have been deleted before) and every empty parent directory until the first, non-empty
+ /// directory is found.
+ /// </summary>
+ /// <param term='file'>Start point of the deletion</param>
+ public static void DeleteEmptyParentDirs(VCFile file)
+ {
+ var dir = file.FullPath.Remove(file.FullPath.LastIndexOf(Path.DirectorySeparatorChar));
+ DeleteEmptyParentDirs(dir);
+ }
+
+ /// <summary>
+ /// Deletes the directory if it is empty and every empty parent directory until the first,
+ /// non-empty directory is found.
+ /// </summary>
+ /// <param term='file'>Start point of the deletion</param>
+ public static void DeleteEmptyParentDirs(string directory)
+ {
+ var dirInfo = new DirectoryInfo(directory);
+ while (dirInfo.Exists && dirInfo.GetFileSystemInfos().Length == 0) {
+ var tmp = dirInfo;
+ dirInfo = dirInfo.Parent;
+ tmp.Delete();
+ }
+ }
+
+ public static bool HasQObjectDeclaration(VCFile file)
+ {
+ return CxxFileContainsNotCommented(file, new[] { "Q_OBJECT", "Q_GADGET" },
+ StringComparison.Ordinal, true);
+ }
+
+ public static bool CxxFileContainsNotCommented(VCFile file, string str,
+ StringComparison comparisonType, bool suppressStrings)
+ {
+ return CxxFileContainsNotCommented(file, new[] { str }, comparisonType, suppressStrings);
+ }
+
+ public static bool CxxFileContainsNotCommented(VCFile file, string[] searchStrings,
+ StringComparison comparisonType, bool suppressStrings)
+ {
+ // Small optimization, we first read the whole content as a string and look for the
+ // search strings. Once we found at least one, ...
+ bool found = false;
+ var content = string.Empty;
+ try {
+ using (StreamReader sr = new StreamReader(file.FullPath))
+ content = sr.ReadToEnd();
+
+ foreach (var key in searchStrings) {
+ if (content.IndexOf(key, comparisonType) >= 0) {
+ found = true;
+ break;
+ }
+ }
+ } catch { }
+
+ if (!found)
+ return false;
+
+ // ... we will start parsing the file again to see if the actual string is commented
+ // or not. The combination of string.IndexOf(...) and string.Split(...) seems to be
+ // way faster then reading the file line by line.
+ found = false;
+ CxxStreamReader cxxSr = null;
+ try {
+ cxxSr = new CxxStreamReader(content.Split(new[] { "\n", "\r\n" },
+ StringSplitOptions.RemoveEmptyEntries));
+ string strLine;
+ while (!found && (strLine = cxxSr.ReadLine(suppressStrings)) != null) {
+ foreach (var str in searchStrings) {
+ if (strLine.IndexOf(str, comparisonType) != -1) {
+ found = true;
+ break;
+ }
+ }
+ }
+ cxxSr.Close();
+ } catch (Exception) {
+ if (cxxSr != null)
+ cxxSr.Close();
+ }
+ return found;
+ }
+
+ public static void SetEnvironmentVariableEx(string environmentVariable, string variableValue)
+ {
+ try {
+ Environment.SetEnvironmentVariable(environmentVariable, variableValue);
+ } catch {
+ throw new QtVSException(SR.GetString("HelperFunctions_CannotWriteEnvQTDIR"));
+ }
+ }
+
+ public static string ChangePathFormat(string path)
+ {
+ return path.Replace('\\', '/');
+ }
+
+ public static string RemoveFileNameExtension(FileInfo fi)
+ {
+ var lastIndex = fi.Name.LastIndexOf(fi.Extension, StringComparison.Ordinal);
+ return fi.Name.Remove(lastIndex, fi.Extension.Length);
+ }
+
+ public static bool IsInFilter(VCFile vcfile, FakeFilter filter)
+ {
+ var item = (VCProjectItem) vcfile;
+
+ while ((item.Parent != null) && (item.Kind != "VCProject")) {
+ item = (VCProjectItem) item.Parent;
+
+ if (item.Kind == "VCFilter") {
+ var f = (VCFilter) item;
+ if (f.UniqueIdentifier != null
+ && f.UniqueIdentifier.ToLower() == filter.UniqueIdentifier.ToLower())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy, string nodeToCollapseFilter)
+ {
+ if (string.IsNullOrEmpty(nodeToCollapseFilter))
+ return;
+
+ foreach (UIHierarchyItem innerItem in item.UIHierarchyItems) {
+ if (innerItem.Name == nodeToCollapseFilter)
+ CollapseFilter(innerItem, hierarchy);
+ else if (innerItem.UIHierarchyItems.Count > 0)
+ CollapseFilter(innerItem, hierarchy, nodeToCollapseFilter);
+ }
+ }
+
+ public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy)
+ {
+ var subItems = item.UIHierarchyItems;
+ if (subItems != null) {
+ foreach (UIHierarchyItem innerItem in subItems) {
+ if (innerItem.UIHierarchyItems.Count > 0) {
+ CollapseFilter(innerItem, hierarchy);
+
+ if (innerItem.UIHierarchyItems.Expanded) {
+ innerItem.UIHierarchyItems.Expanded = false;
+ if (innerItem.UIHierarchyItems.Expanded) {
+ innerItem.Select(vsUISelectionType.vsUISelectionTypeSelect);
+ hierarchy.DoDefaultAction();
+ }
+ }
+ }
+ }
+ }
+ if (item.UIHierarchyItems.Expanded) {
+ item.UIHierarchyItems.Expanded = false;
+ if (item.UIHierarchyItems.Expanded) {
+ item.Select(vsUISelectionType.vsUISelectionTypeSelect);
+ hierarchy.DoDefaultAction();
+ }
+ }
+ }
+
+ // returns true if some exception occurs
+ public static bool IsGenerated(VCFile vcfile)
+ {
+ try {
+ return IsInFilter(vcfile, Filters.GeneratedFiles());
+ } catch (Exception e) {
+ MessageBox.Show(e.ToString());
+ return true;
+ }
+ }
+
+ // returns false if some exception occurs
+ public static bool IsResource(VCFile vcfile)
+ {
+ try {
+ return IsInFilter(vcfile, Filters.ResourceFiles());
+ } catch (Exception) {
+ return false;
+ }
+ }
+
+ public static List<string> GetProjectFiles(Project pro, FilesToList filter)
+ {
+ var fileList = new List<string>();
+
+ VCProject vcpro;
+ try {
+ vcpro = (VCProject) pro.Object;
+ } catch (Exception e) {
+ Messages.DisplayErrorMessage(e);
+ return null;
+ }
+
+ var configurationName = pro.ConfigurationManager.ActiveConfiguration.ConfigurationName;
+
+ foreach (VCFile vcfile in (IVCCollection) vcpro.Files) {
+ // Why project files are also returned?
+ if (vcfile.ItemName.EndsWith(".vcxproj.filters", StringComparison.Ordinal))
+ continue;
+ var excluded = false;
+ var fileConfigurations = (IVCCollection) vcfile.FileConfigurations;
+ foreach (VCFileConfiguration config in fileConfigurations) {
+ if (config.ExcludedFromBuild && config.MatchName(configurationName, false)) {
+ excluded = true;
+ break;
+ }
+ }
+
+ if (excluded)
+ continue;
+
+ // can be in any filter
+ if (IsTranslationFile(vcfile.Name) && (filter == FilesToList.FL_Translation))
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+
+ // can also be in any filter
+ if (IsWinRCFile(vcfile.Name) && (filter == FilesToList.FL_WinResource))
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+
+ if (IsGenerated(vcfile)) {
+ if (filter == FilesToList.FL_Generated)
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+ continue;
+ }
+
+ if (IsResource(vcfile)) {
+ if (filter == FilesToList.FL_Resources)
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+ continue;
+ }
+
+ switch (filter) {
+ case FilesToList.FL_UiFiles: // form files
+ if (IsUicFile(vcfile.Name))
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+ break;
+ case FilesToList.FL_HFiles:
+ if (IsHeaderFile(vcfile.Name))
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+ break;
+ case FilesToList.FL_CppFiles:
+ if (IsSourceFile(vcfile.Name))
+ fileList.Add(ChangePathFormat(vcfile.RelativePath));
+ break;
+ }
+ }
+
+ return fileList;
+ }
+
+ /// <summary>
+ /// Removes a file reference from the project and moves the file to the "Deleted" folder.
+ /// </summary>
+ /// <param name="vcpro"></param>
+ /// <param name="fileName"></param>
+ public static void RemoveFileInProject(VCProject vcpro, string fileName)
+ {
+ var qtProj = QtProject.Create(vcpro);
+ var fi = new FileInfo(fileName);
+
+ foreach (VCFile vcfile in (IVCCollection) vcpro.Files) {
+ if (vcfile.FullPath.ToLower() == fi.FullName.ToLower()) {
+ vcpro.RemoveFile(vcfile);
+ qtProj.MoveFileToDeletedFolder(vcfile);
+ }
+ }
+ }
+
+ public static Project GetSelectedProject(DTE dteObject)
+ {
+ if (dteObject == null)
+ return null;
+ Array prjs = null;
+ try {
+ prjs = (Array) dteObject.ActiveSolutionProjects;
+ } catch {
+ // When VS2010 is started from the command line,
+ // we may catch a "Unspecified error" here.
+ }
+ if (prjs == null || prjs.Length < 1)
+ return null;
+
+ // don't handle multiple selection... use the first one
+ if (prjs.GetValue(0) is Project)
+ return (Project) prjs.GetValue(0);
+ return null;
+ }
+
+ public static Project GetActiveDocumentProject(DTE dteObject)
+ {
+ if (dteObject == null)
+ return null;
+ var doc = dteObject.ActiveDocument;
+ if (doc == null)
+ return null;
+
+ if (doc.ProjectItem == null)
+ return null;
+
+ return doc.ProjectItem.ContainingProject;
+ }
+
+ public static Project GetSingleProjectInSolution(DTE dteObject)
+ {
+ var projectList = ProjectsInSolution(dteObject);
+ if (dteObject == null || dteObject.Solution == null ||
+ projectList.Count != 1)
+ return null; // no way to know which one to select
+
+ return projectList[0];
+ }
+
+ /// <summary>
+ /// Returns the the current selected Qt Project. If not project
+ /// is selected or if the selected project is not a Qt project
+ /// this function returns null.
+ /// </summary>
+ public static Project GetSelectedQtProject(DTE dteObject)
+ {
+ // can happen sometimes shortly after starting VS
+ if (dteObject == null || dteObject.Solution == null
+ || ProjectsInSolution(dteObject).Count == 0)
+ return null;
+
+ Project pro;
+
+ if ((pro = GetSelectedProject(dteObject)) == null) {
+ if ((pro = GetSingleProjectInSolution(dteObject)) == null)
+ pro = GetActiveDocumentProject(dteObject);
+ }
+ return IsQtProject(pro) ? pro : null;
+ }
+
+ public static VCFile[] GetSelectedFiles(DTE dteObject)
+ {
+ if (GetSelectedQtProject(dteObject) == null)
+ return null;
+
+ if (dteObject.SelectedItems.Count <= 0)
+ return null;
+
+ var items = dteObject.SelectedItems;
+
+ var files = new VCFile[items.Count + 1];
+ for (var i = 1; i <= items.Count; ++i) {
+ var item = items.Item(i);
+ if (item.ProjectItem == null)
+ continue;
+
+ VCProjectItem vcitem;
+ try {
+ vcitem = (VCProjectItem) item.ProjectItem.Object;
+ } catch (Exception) {
+ return null;
+ }
+
+ if (vcitem.Kind == "VCFile")
+ files[i - 1] = (VCFile) vcitem;
+ }
+ files[items.Count] = null;
+ return files;
+ }
+
+ public static Image GetSharedImage(string name)
+ {
+ Image image = null;
+ var a = Assembly.GetExecutingAssembly();
+ using (var imgStream = a.GetManifestResourceStream(name)) {
+ if (imgStream != null)
+ image = Image.FromStream(imgStream);
+ }
+ return image;
+ }
+
+ public static RccOptions ParseRccOptions(string cmdLine, VCFile qrcFile)
+ {
+ var pro = VCProjectToProject((VCProject) qrcFile.project);
+
+ var rccOpts = new RccOptions(pro, qrcFile);
+
+ if (cmdLine.Length > 0) {
+ var cmdSplit = cmdLine.Split(' ', '\t');
+ for (var i = 0; i < cmdSplit.Length; ++i) {
+ var lowercmdSplit = cmdSplit[i].ToLower();
+ if (lowercmdSplit.Equals("-threshold")) {
+ rccOpts.CompressFiles = true;
+ rccOpts.CompressThreshold = int.Parse(cmdSplit[i + 1]);
+ } else if (lowercmdSplit.Equals("-compress")) {
+ rccOpts.CompressFiles = true;
+ rccOpts.CompressLevel = int.Parse(cmdSplit[i + 1]);
+ }
+ }
+ }
+ return rccOpts;
+ }
+
+ public static Project VCProjectToProject(VCProject vcproj)
+ {
+ return (Project) vcproj.Object;
+ }
+
+ public static List<Project> ProjectsInSolution(DTE dteObject)
+ {
+ var projects = new List<Project>();
+ var solution = dteObject.Solution;
+ if (solution != null) {
+ var c = solution.Count;
+ for (var i = 1; i <= c; ++i) {
+ try {
+ var prj = solution.Projects.Item(i);
+ if (prj == null)
+ continue;
+ addSubProjects(prj, ref projects);
+ } catch {
+ // Ignore this exception... maybe the next project is ok.
+ // This happens for example for Intel VTune projects.
+ }
+ }
+ }
+ return projects;
+ }
+
+ private static void addSubProjects(Project prj, ref List<Project> projects)
+ {
+ // If the actual object of the project is null then the project was probably unloaded.
+ if (prj.Object == null)
+ return;
+
+ if (prj.ConfigurationManager != null &&
+ // Is this a Visual C++ project?
+ prj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") {
+ projects.Add(prj);
+ } else {
+ // In this case, prj is a solution folder
+ addSubProjects(prj.ProjectItems, ref projects);
+ }
+ }
+
+ private static void addSubProjects(ProjectItems subItems, ref List<Project> projects)
+ {
+ if (subItems == null)
+ return;
+
+ foreach (ProjectItem item in subItems) {
+ Project subprj = null;
+ try {
+ subprj = item.SubProject;
+ } catch {
+ // The property "SubProject" might not be implemented.
+ // This is the case for Intel Fortran projects. (QTBUG-11567)
+ }
+ if (subprj != null)
+ addSubProjects(subprj, ref projects);
+ }
+ }
+
+ public static int GetMaximumCommandLineLength()
+ {
+ var epsilon = 10; // just to be sure :)
+ var os = Environment.OSVersion;
+ if (os.Version.Major >= 6 ||
+ (os.Version.Major == 5 && os.Version.Minor >= 1))
+ return 8191 - epsilon; // Windows XP and above
+ return 2047 - epsilon;
+ }
+
+ /// <summary>
+ /// Translates the machine type given as command line argument to the linker
+ /// to the internal enum type VCProjectEngine.machineTypeOption.
+ /// </summary>
+ public static machineTypeOption TranslateMachineType(string cmdLineMachine)
+ {
+ switch (cmdLineMachine.ToUpper()) {
+ case "AM33":
+ return machineTypeOption.machineAM33;
+ case "X64":
+ return machineTypeOption.machineAMD64;
+ case "ARM":
+ return machineTypeOption.machineARM;
+ case "EBC":
+ return machineTypeOption.machineEBC;
+ case "IA-64":
+ return machineTypeOption.machineIA64;
+ case "M32R":
+ return machineTypeOption.machineM32R;
+ case "MIPS":
+ return machineTypeOption.machineMIPS;
+ case "MIPS16":
+ return machineTypeOption.machineMIPS16;
+ case "MIPSFPU":
+ return machineTypeOption.machineMIPSFPU;
+ case "MIPSFPU16":
+ return machineTypeOption.machineMIPSFPU16;
+ case "MIPS41XX":
+ return machineTypeOption.machineMIPSR41XX;
+ case "SH3":
+ return machineTypeOption.machineSH3;
+ case "SH3DSP":
+ return machineTypeOption.machineSH3DSP;
+ case "SH4":
+ return machineTypeOption.machineSH4;
+ case "SH5":
+ return machineTypeOption.machineSH5;
+ case "THUMB":
+ return machineTypeOption.machineTHUMB;
+ case "X86":
+ return machineTypeOption.machineX86;
+ default:
+ return machineTypeOption.machineNotSet;
+ }
+ }
+
+ public static bool ArraysEqual(Array array1, Array array2)
+ {
+ if (array1 == array2)
+ return true;
+
+ if (array1 == null || array2 == null)
+ return false;
+
+ if (array1.Length != array2.Length)
+ return false;
+
+ for (var i = 0; i < array1.Length; i++) {
+ if (!Equals(array1.GetValue(i), array2.GetValue(i)))
+ return false;
+ }
+ return true;
+ }
+
+ public static string FindFileInPATH(string fileName)
+ {
+ var envPATH = Environment.ExpandEnvironmentVariables("%PATH%");
+ var directories = envPATH.Split(';');
+ foreach (var directory in directories) {
+ var fullFilePath = directory;
+ if (!fullFilePath.EndsWith("\\", StringComparison.Ordinal))
+ fullFilePath += '\\';
+ fullFilePath += fileName;
+ if (File.Exists(fullFilePath))
+ return fullFilePath;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// This method copies the specified directory and all its child directories and files to
+ /// the specified destination. The destination directory is created if it does not exist.
+ /// </summary>
+ public static void CopyDirectory(string directory, string targetPath)
+ {
+ var sourceDir = new DirectoryInfo(directory);
+ if (!sourceDir.Exists)
+ return;
+
+ try {
+ if (!Directory.Exists(targetPath))
+ Directory.CreateDirectory(targetPath);
+
+ var files = sourceDir.GetFiles();
+ foreach (var file in files) {
+ try {
+ file.CopyTo(Path.Combine(targetPath, file.Name), true);
+ } catch { }
+ }
+ } catch { }
+
+ var subDirs = sourceDir.GetDirectories();
+ foreach (var subDir in subDirs)
+ CopyDirectory(subDir.FullName, Path.Combine(targetPath, subDir.Name));
+ }
+
+ /// <summary>
+ /// Performs an in-place expansion of MS Build properties in the form $(PropertyName)
+ /// and project item metadata in the form %(MetadataName).<para/>
+ /// Returns: 'true' if expansion was successful, 'false' otherwise<para/>
+ /// <paramref name="stringToExpand"/>: The string containing properties and/or metadata to
+ /// expand. This string is passed by ref and expansion is performed in-place.<para/>
+ /// <paramref name="project"/>: Current project.<para/>
+ /// <paramref name="configName"/>: Name of selected configuration (e.g. "Debug").<para/>
+ /// <paramref name="platformName"/>: Name of selected platform (e.g. "x64").<para/>
+ /// <paramref name="filePath"/>(optional): Evaluation context.<para/>
+ /// </summary>
+ public static bool ExpandString(
+ ref string stringToExpand,
+ EnvDTE.Project project,
+ string configName,
+ string platformName,
+ string filePath = null)
+ {
+ if (project == null
+ || string.IsNullOrEmpty(configName)
+ || string.IsNullOrEmpty(platformName))
+ return false;
+
+ var vcProject = project.Object as VCProject;
+
+ if (filePath == null) {
+ var vcConfig = (from VCConfiguration _config
+ in (IVCCollection)vcProject.Configurations
+ where _config.Name == configName + "|" + platformName
+ select _config).FirstOrDefault();
+ return ExpandString(ref stringToExpand, vcConfig);
+ } else {
+ var vcFile = (from VCFile _file in (IVCCollection)vcProject.Files
+ where _file.FullPath == filePath
+ select _file).FirstOrDefault();
+ if (vcFile == null)
+ return false;
+
+ var vcFileConfig = (from VCFileConfiguration _config
+ in (IVCCollection)vcFile.FileConfigurations
+ where _config.Name == configName + "|" + platformName
+ select _config).FirstOrDefault();
+ return ExpandString(ref stringToExpand, vcFileConfig);
+ }
+ }
+
+ /// <summary>
+ /// Performs an in-place expansion of MS Build properties in the form $(PropertyName)
+ /// and project item metadata in the form %(MetadataName).<para/>
+ /// Returns: 'true' if expansion was successful, 'false' otherwise<para/>
+ /// <paramref name="stringToExpand"/>: The string containing properties and/or metadata to
+ /// expand. This string is passed by ref and expansion is performed in-place.<para/>
+ /// <paramref name="config"/>: Either a VCConfiguration or VCFileConfiguration object to
+ /// use as provider of property expansion (through Evaluate()). Cannot be null.<para/>
+ /// </summary>
+ public static bool ExpandString(
+ ref string stringToExpand,
+ object config)
+ {
+ if (config == null)
+ return false;
+
+ /* try property expansion through VCConfiguration.Evaluate()
+ * or VCFileConfiguration.Evaluate() */
+ string expanded = stringToExpand;
+ VCProject vcProj = null;
+ VCFile vcFile = null;
+ string configName = "", platformName = "";
+ var vcConfig = config as VCConfiguration;
+ if (vcConfig != null) {
+ vcProj = vcConfig.project as VCProject;
+ configName = vcConfig.ConfigurationName;
+ var vcPlatform = vcConfig.Platform as VCPlatform;
+ if (vcPlatform != null)
+ platformName = vcPlatform.Name;
+ try {
+ expanded = vcConfig.Evaluate(expanded);
+ } catch { }
+ } else {
+ var vcFileConfig = config as VCFileConfiguration;
+ if (vcFileConfig == null)
+ return false;
+ vcFile = vcFileConfig.File as VCFile;
+ if (vcFile != null)
+ vcProj = vcFile.project as VCProject;
+ var vcProjConfig = vcFileConfig.ProjectConfiguration as VCConfiguration;
+ if (vcProjConfig != null) {
+ configName = vcProjConfig.ConfigurationName;
+ var vcPlatform = vcProjConfig.Platform as VCPlatform;
+ if (vcPlatform != null)
+ platformName = vcPlatform.Name;
+ }
+ try {
+ expanded = vcFileConfig.Evaluate(expanded);
+ } catch { }
+ }
+
+ /* fail-safe */
+ foreach (Match propNameMatch in Regex.Matches(expanded, @"\$\(([^\)]+)\)")) {
+ string propName = propNameMatch.Groups[1].Value;
+ string propValue = "";
+ switch (propName) {
+ case "Configuration":
+ case "ConfigurationName":
+ if (string.IsNullOrEmpty(configName))
+ return false;
+ propValue = configName;
+ break;
+ case "Platform":
+ case "PlatformName":
+ if (string.IsNullOrEmpty(platformName))
+ return false;
+ propValue = platformName;
+ break;
+ default:
+ return false;
+ }
+ expanded = expanded.Replace(string.Format("$({0})", propName), propValue);
+ }
+
+ /* because item metadata is not expanded in Evaluate() */
+ foreach (Match metaNameMatch in Regex.Matches(expanded, @"\%\(([^\)]+)\)")) {
+ string metaName = metaNameMatch.Groups[1].Value;
+ string metaValue = "";
+ switch (metaName) {
+ case "FullPath":
+ if (vcFile == null)
+ return false;
+ metaValue = vcFile.FullPath;
+ break;
+ case "RootDir":
+ if (vcFile == null)
+ return false;
+ metaValue = Path.GetPathRoot(vcFile.FullPath);
+ break;
+ case "Filename":
+ if (vcFile == null)
+ return false;
+ metaValue = Path.GetFileNameWithoutExtension(vcFile.FullPath);
+ break;
+ case "Extension":
+ if (vcFile == null)
+ return false;
+ metaValue = Path.GetExtension(vcFile.FullPath);
+ break;
+ case "RelativeDir":
+ if (vcProj == null || vcFile == null)
+ return false;
+ metaValue = Path.GetDirectoryName(GetRelativePath(
+ Path.GetDirectoryName(vcProj.ProjectFile),
+ vcFile.FullPath));
+ if (!metaValue.EndsWith("\\"))
+ metaValue += "\\";
+ if (metaValue.StartsWith(".\\"))
+ metaValue = metaValue.Substring(2);
+ break;
+ case "Directory":
+ if (vcFile == null)
+ return false;
+ metaValue = Path.GetDirectoryName(GetRelativePath(
+ Path.GetPathRoot(vcFile.FullPath),
+ vcFile.FullPath));
+ if (!metaValue.EndsWith("\\"))
+ metaValue += "\\";
+ if (metaValue.StartsWith(".\\"))
+ metaValue = metaValue.Substring(2);
+ break;
+ case "Identity":
+ if (vcProj == null || vcFile == null)
+ return false;
+ metaValue = GetRelativePath(
+ Path.GetDirectoryName(vcProj.ProjectFile),
+ vcFile.FullPath);
+ if (metaValue.StartsWith(".\\"))
+ metaValue = metaValue.Substring(2);
+ break;
+ case "RecursiveDir":
+ case "ModifiedTime":
+ case "CreatedTime":
+ case "AccessedTime":
+ return false;
+ default:
+ var vcFileConfig = config as VCFileConfiguration;
+ if (vcFileConfig == null)
+ return false;
+ var propStoreTool = vcFileConfig.Tool as IVCRulePropertyStorage;
+ if (propStoreTool == null)
+ return false;
+ try {
+ metaValue = propStoreTool.GetEvaluatedPropertyValue(metaName);
+ } catch {
+ return false;
+ }
+ break;
+ }
+ expanded = expanded.Replace(string.Format("%({0})", metaName), metaValue);
+ }
+
+ stringToExpand = expanded;
+ return true;
+ }
+
+ private static string GetRegistrySoftwareString(string subKeyName, string valueName)
+ {
+ var keyName = new StringBuilder();
+ keyName.Append(@"SOFTWARE\");
+ if (System.Environment.Is64BitOperatingSystem && IntPtr.Size == 4)
+ keyName.Append(@"WOW6432Node\");
+ keyName.Append(subKeyName);
+
+ try {
+ using (var key = Registry.LocalMachine.OpenSubKey(keyName.ToString(), false)) {
+ if (key == null)
+ return ""; //key not found
+
+ RegistryValueKind valueKind = key.GetValueKind(valueName);
+ if (valueKind != RegistryValueKind.String
+ && valueKind != RegistryValueKind.ExpandString) {
+ return ""; //wrong value kind
+ }
+
+ Object objValue = key.GetValue(valueName);
+ if (objValue == null)
+ return ""; //error getting value
+
+ return objValue.ToString();
+ }
+ } catch {
+ return "";
+ }
+ }
+
+ public static string GetWindows10SDKVersion()
+ {
+#if VS2019
+ // In Visual Studio 2019: WindowsTargetPlatformVersion=10.0
+ // will be treated as "use latest installed Windows 10 SDK".
+ // https://developercommunity.visualstudio.com/comments/407752/view.html
+ return "10.0";
+#else
+ string versionWin10SDK = HelperFunctions.GetRegistrySoftwareString(
+ @"Microsoft\Microsoft SDKs\Windows\v10.0", "ProductVersion");
+ if (string.IsNullOrEmpty(versionWin10SDK))
+ return versionWin10SDK;
+ while (versionWin10SDK.Split(new char[] { '.' }).Length < 4)
+ versionWin10SDK = versionWin10SDK + ".0";
+ return versionWin10SDK;
+#endif
+ }
+
+ static string _VCPath;
+ public static string VCPath
+ {
+ set { _VCPath = value; }
+ get
+ {
+ if (!string.IsNullOrEmpty(_VCPath))
+ return _VCPath;
+ else
+ return GetVCPathFromRegistry();
+ }
+ }
+
+ private static string GetVCPathFromRegistry()
+ {
+#if VS2019
+ Debug.Assert(false, "VCPath for VS2019 is not available through the registry");
+ string vcPath = string.Empty;
+#elif VS2017
+ string vsPath = GetRegistrySoftwareString(@"Microsoft\VisualStudio\SxS\VS7", "15.0");
+ if (string.IsNullOrEmpty(vsPath))
+ return "";
+ string vcPath = Path.Combine(vsPath, "VC");
+#elif VS2015
+ string vcPath = GetRegistrySoftwareString(@"Microsoft\VisualStudio\SxS\VC7", "14.0");
+ if (string.IsNullOrEmpty(vcPath))
+ return ""; //could not get registry key
+#elif VS2013
+ string vcPath = GetRegistrySoftwareString(@"Microsoft\VisualStudio\SxS\VC7", "12.0");
+ if (string.IsNullOrEmpty(vcPath))
+ return ""; //could not get registry key
+#endif
+ return vcPath;
+ }
+
+ public static bool SetVCVars(ProcessStartInfo startInfo)
+ {
+ return SetVCVars(null, startInfo);
+ }
+
+ public static bool SetVCVars(VersionInformation VersionInfo, ProcessStartInfo startInfo)
+ {
+ if (VersionInfo == null) {
+ VersionInfo = QtVersionManager.The().GetVersionInfo(
+ QtVersionManager.The().GetDefaultVersion());
+ }
+ bool isOS64Bit = System.Environment.Is64BitOperatingSystem;
+ bool isQt64Bit = VersionInfo.is64Bit();
+
+ string vcPath = VCPath;
+ if (vcPath == "")
+ return false;
+
+ string comspecPath = Environment.GetEnvironmentVariable("COMSPEC");
+#if (VS2017 || VS2019)
+ string vcVarsCmd = "";
+ string vcVarsArg = "";
+ if (isOS64Bit && isQt64Bit)
+ vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvars64.bat");
+ else if (!isOS64Bit && !isQt64Bit)
+ vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvars32.bat");
+ else if (isOS64Bit && !isQt64Bit)
+ vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvarsamd64_x86.bat");
+ else if (!isOS64Bit && isQt64Bit)
+ vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvarsx86_amd64.bat");
+#elif VS2015 || VS2013
+ string vcVarsCmd = Path.Combine(vcPath, "vcvarsall.bat");
+ string vcVarsArg = "";
+ if (isOS64Bit && isQt64Bit)
+ vcVarsArg = "amd64";
+ else if (!isOS64Bit && !isQt64Bit)
+ vcVarsArg = "x86";
+ else if (isOS64Bit && !isQt64Bit)
+ vcVarsArg = "amd64_x86";
+ else if (!isOS64Bit && isQt64Bit)
+ vcVarsArg = "x86_amd64";
+#endif
+ const string markSTX = ":@:@:@";
+ const string markEOL = ":#:#:#";
+ string command =
+ string.Format("/c \"{0}\" {1} && echo {2} && set", vcVarsCmd, vcVarsArg, markSTX);
+ var vcVarsStartInfo = new ProcessStartInfo(comspecPath, command);
+ vcVarsStartInfo.CreateNoWindow = true;
+ vcVarsStartInfo.UseShellExecute = false;
+ vcVarsStartInfo.RedirectStandardError = true;
+ vcVarsStartInfo.RedirectStandardOutput = true;
+
+ var process = Process.Start(vcVarsStartInfo);
+ StringBuilder stdOut = new StringBuilder();
+
+ process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
+ stdOut.AppendFormat("{0}\n{1}\n", e.Data, markEOL);
+ process.BeginOutputReadLine();
+
+ process.WaitForExit();
+ bool ok = (process.ExitCode == 0);
+ process.Close();
+ if (!ok)
+ return false;
+
+ SortedDictionary<string, List<string>> vcVars =
+ new SortedDictionary<string, List<string>>();
+ string[] split =
+ stdOut.ToString().Split(new string[] { "\n", "=", ";" }, StringSplitOptions.None);
+ int i = 0;
+ for (; i < split.Length && split[i].Trim() != markSTX; i++) {
+ //Skip to start of data
+ }
+ i++; //Advance to next item
+ for (; i < split.Length && split[i].Trim() != markEOL; i++) {
+ //Skip to end of line
+ }
+ i++; //Advance to next item
+ for (; i < split.Length; i++) {
+ //Process first item (variable name)
+ string key = split[i].ToUpper().Trim();
+ i++; //Advance to next item
+ List<string> vcVarValue = vcVars[key] = new List<string>();
+ for (; i < split.Length && split[i].Trim() != markEOL; i++) {
+ //Process items up to end of line (variable value(s))
+ vcVarValue.Add(split[i].Trim());
+ }
+ }
+
+ foreach (var vcVar in vcVars) {
+ if (vcVar.Value.Count == 1) {
+ startInfo.EnvironmentVariables[vcVar.Key] = vcVar.Value[0];
+ } else {
+ if (!startInfo.EnvironmentVariables.ContainsKey(vcVar.Key)) {
+ foreach (var vcVarValue in vcVar.Value) {
+ if (!string.IsNullOrWhiteSpace(vcVarValue)) {
+ startInfo.EnvironmentVariables[vcVar.Key] += vcVarValue + ";";
+ }
+ }
+ } else {
+ string[] startInfoVariableValues = startInfo.EnvironmentVariables[vcVar.Key]
+ .Split(new string[] { ";" }, StringSplitOptions.None);
+ foreach (var vcVarValue in vcVar.Value) {
+ if (!string.IsNullOrWhiteSpace(vcVarValue)
+ && !startInfoVariableValues.Any(s => s.Trim().Equals(
+ vcVarValue,
+ StringComparison.OrdinalIgnoreCase))) {
+ if (!startInfo.EnvironmentVariables[vcVar.Key].EndsWith(";"))
+ startInfo.EnvironmentVariables[vcVar.Key] += ";";
+ startInfo.EnvironmentVariables[vcVar.Key] += vcVarValue + ";";
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Rooted canonical path is the absolute path for the specified path string
+ /// (cf. Path.GetFullPath()) without a trailing path separator.
+ /// </summary>
+ static string RootedCanonicalPath(string path)
+ {
+ try {
+ return Path
+ .GetFullPath(path)
+ .TrimEnd(new char[] {
+ Path.DirectorySeparatorChar,
+ Path.AltDirectorySeparatorChar
+ });
+ } catch {
+ return "";
+ }
+ }
+
+ /// <summary>
+ /// If the given path is relative and a sub-path of the current directory, returns
+ /// a "relative canonical path", containing only the steps beyond the current directory.
+ /// Otherwise, returns the absolute ("rooted") canonical path.
+ /// </summary>
+ public static string CanonicalPath(string path)
+ {
+ string canonicalPath = RootedCanonicalPath(path);
+ if (!Path.IsPathRooted(path)) {
+ string currentCanonical = RootedCanonicalPath(".");
+ if (canonicalPath.StartsWith(currentCanonical,
+ StringComparison.InvariantCultureIgnoreCase)) {
+ return canonicalPath
+ .Substring(currentCanonical.Length)
+ .TrimStart(new char[] {
+ Path.DirectorySeparatorChar,
+ Path.AltDirectorySeparatorChar
+ });
+ } else {
+ return canonicalPath;
+ }
+ } else {
+ return canonicalPath;
+ }
+ }
+
+ public static bool PathEquals(string path1, string path2)
+ {
+ return (CanonicalPath(path1).Equals(CanonicalPath(path2),
+ StringComparison.InvariantCultureIgnoreCase));
+ }
+
+ public static bool PathIsRelativeTo(string path, string subPath)
+ {
+ return CanonicalPath(path).EndsWith(CanonicalPath(subPath),
+ StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ public static string Unquote(string text)
+ {
+ text = text.Trim();
+ if (string.IsNullOrEmpty(text)
+ || text.Length < 3
+ || !text.StartsWith("\"")
+ || !text.EndsWith("\"")) {
+ return text;
+ }
+ return text.Substring(1, text.Length - 2);
+ }
+
+ public static string NewProjectGuid()
+ {
+ return string.Format("{{{0}}}", Guid.NewGuid().ToString().ToUpper());
+ }
+ }
+}