/****************************************************************************
|
**
|
** Copyright (C) 2022 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 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 Microsoft.VisualStudio.Shell;
|
using Microsoft.VisualStudio.VCProjectEngine;
|
#if VS2017
|
using Microsoft.Win32;
|
#endif
|
using EnvDTE;
|
|
using Process = System.Diagnostics.Process;
|
|
namespace QtVsTools.Core
|
{
|
using System.Runtime.Remoting.Contexts;
|
using EnvDTE80;
|
using QtMsBuild;
|
|
public static class HelperFunctions
|
{
|
static readonly HashSet<string> _sources = new HashSet<string>(new[] { ".c", ".cpp", ".cxx" },
|
StringComparer.OrdinalIgnoreCase);
|
public static bool IsSourceFile(string fileName)
|
{
|
return _sources.Contains(Path.GetExtension(fileName));
|
}
|
|
static readonly HashSet<string> _headers = new HashSet<string>(new[] { ".h", ".hpp", ".hxx" },
|
StringComparer.OrdinalIgnoreCase);
|
public static 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);
|
}
|
|
public static bool IsQmlFile(string fileName)
|
{
|
return ".qml".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase);
|
}
|
|
public static void SetDebuggingEnvironment(Project prj)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
SetDebuggingEnvironment(prj, string.Empty);
|
}
|
|
public static void SetDebuggingEnvironment(Project prj, string solutionConfig)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
SetDebuggingEnvironment(prj, "PATH=$(QTDIR)\\bin;$(PATH)", false, solutionConfig);
|
}
|
|
public static void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
SetDebuggingEnvironment(prj, envpath, overwrite, string.Empty);
|
}
|
|
public static void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite, string solutionConfig)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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.Print("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 Project ProjectFromSolution(DTE dteObject, string fullName)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
fullName = new FileInfo(fullName).FullName;
|
foreach (var p in ProjectsInSolution(dteObject)) {
|
if (p.FullName.Equals(fullName, StringComparison.OrdinalIgnoreCase))
|
return p;
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// Returns the normalized file path of a given file.
|
/// </summary>
|
/// <param name="name">file name</param>
|
public static string NormalizeFilePath(string name)
|
{
|
var fi = new FileInfo(name);
|
return fi.FullName;
|
}
|
|
public static 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;
|
}
|
|
public static 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>
|
private static 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>
|
public static 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>
|
/// 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>
|
public static VCCustomBuildTool GetCustomBuildTool(VCFileConfiguration config)
|
{
|
if (config.File is VCFile file
|
&& file.ItemType == "CustomBuild"
|
&& config.Tool is VCCustomBuildTool tool) {
|
try {
|
_ = tool.CommandLine;
|
} catch {
|
return null;
|
}
|
return tool;
|
}
|
return null;
|
}
|
|
/// <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>
|
public static void EnsureCustomBuildToolAvailable(ProjectItem projectItem)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
foreach (Property prop in projectItem.Properties) {
|
if (prop.Name == "ItemType") {
|
if ((string)prop.Value != "CustomBuild")
|
prop.Value = "CustomBuild";
|
break;
|
}
|
}
|
}
|
|
public static string GetQtDirFromQMakeProject(Project project)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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 VS tools project; false otherwise.
|
/// </summary>
|
/// <param name="proj">project</param>
|
public static bool IsVsToolsProject(Project proj)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread(); // C++ Project Type GUID
|
if (proj == null || proj.Kind != "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
|
return false;
|
return IsVsToolsProject(proj.Object as VCProject);
|
}
|
|
/// <summary>
|
/// Return true if the project is a VS tools project; false otherwise.
|
/// </summary>
|
/// <param name="proj">project</param>
|
public static bool IsVsToolsProject(VCProject proj)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
if (!IsQtProject(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>
|
/// Return true if the project is a Qt project; false otherwise.
|
/// </summary>
|
/// <param name="proj">project</param>
|
public static bool IsQtProject(Project proj)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread(); //C++ Project Type GUID
|
if (proj == null || proj.Kind != "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")
|
return false;
|
return IsQtProject(proj.Object as VCProject);
|
}
|
|
/// <summary>
|
/// Return true if the project is a Qt project; false otherwise.
|
/// </summary>
|
/// <param name="proj">project</param>
|
public static bool IsQtProject(VCProject proj)
|
{
|
if (proj == null)
|
return false;
|
var keyword = proj.keyword;
|
if (string.IsNullOrEmpty(keyword))
|
return false;
|
return keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal)
|
|| keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal);
|
}
|
|
/// <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",
|
"Q_NAMESPACE"
|
},
|
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"));
|
}
|
}
|
|
/// <summary>
|
/// Converts all directory separators of the path to the alternate character
|
/// directory separator. For instance, FromNativeSeparators("c:\\winnt\\system32")
|
/// returns "c:/winnt/system32".
|
/// </summary>
|
/// <param name="path">The path to convert.</param>
|
/// <returns>Returns path using '/' as file separator.</returns>
|
public static string FromNativeSeparators(string path)
|
{
|
return path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
}
|
|
/// <summary>
|
/// Converts all alternate directory separators characters of the path to the native
|
/// directory separator. For instance, ToNativeSeparators("c:/winnt/system32")
|
/// returns "c:\\winnt\\system32".
|
/// </summary>
|
/// <param name="path">The path to convert.</param>
|
/// <returns>Returns path using '\' as file separator.</returns>
|
public static string ToNativeSeparator(string path)
|
{
|
return path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
}
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
VCProject vcpro;
|
try {
|
vcpro = (VCProject)pro.Object;
|
} catch (Exception e) {
|
Messages.DisplayErrorMessage(e);
|
return null;
|
}
|
|
var fileList = new List<string>();
|
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 (IsConfExcludeFromSolution(config))
|
continue;
|
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;
|
case FilesToList.FL_QmlFiles:
|
if (IsQmlFile(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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
fileName = new FileInfo(fileName).FullName;
|
foreach (VCFile vcfile in (IVCCollection)vcpro.Files) {
|
if (vcfile.FullPath.Equals(fileName, StringComparison.OrdinalIgnoreCase)) {
|
vcpro.RemoveFile(vcfile);
|
QtProject.Create(vcpro)?.MoveFileToDeletedFolder(vcfile);
|
}
|
}
|
}
|
|
public static Project GetSelectedProject(DTE dteObject)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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 project)
|
return project;
|
return null;
|
}
|
|
public static Project GetActiveDocumentProject(DTE dteObject)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
return dteObject?.ActiveDocument?.ProjectItem?.ContainingProject;
|
}
|
|
public static Project GetSingleProjectInSolution(DTE dteObject)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
var projectList = ProjectsInSolution(dteObject);
|
if (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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
// can happen sometimes shortly after starting VS
|
if (ProjectsInSolution(dteObject).Count == 0)
|
return null;
|
|
var pro = GetSelectedProject(dteObject);
|
if (pro == null) {
|
if ((pro = GetSingleProjectInSolution(dteObject)) == null)
|
pro = GetActiveDocumentProject(dteObject);
|
}
|
return IsVsToolsProject(pro) ? pro : null;
|
}
|
|
public static VCFile[] GetSelectedFiles(DTE dteObject)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
return (Project)vcproj.Object;
|
}
|
|
public static List<Project> ProjectsInSolution(DTE dteObject)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
if (dteObject == null)
|
return new List<Project>();
|
|
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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
// 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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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;
|
}
|
|
/// <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)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
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 = "";
|
if (config is VCConfiguration vcConfig) {
|
vcProj = vcConfig.project as VCProject;
|
configName = vcConfig.ConfigurationName;
|
if (vcConfig.Platform is VCPlatform vcPlatform)
|
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;
|
if (vcFileConfig.ProjectConfiguration is VCConfiguration vcProjConfig) {
|
configName = vcProjConfig.ConfigurationName;
|
if (vcProjConfig.Platform is VCPlatform vcPlatform)
|
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;
|
}
|
|
#if VS2017
|
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 "";
|
}
|
}
|
#endif
|
|
public static string GetWindows10SDKVersion()
|
{
|
#if VS2019 || VS2022
|
// 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 VS2022
|
Debug.Assert(false, "VCPath for VS2022 is not available through the registry");
|
string vcPath = string.Empty;
|
#elif 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");
|
#endif
|
return vcPath;
|
}
|
|
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");
|
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");
|
|
Messages.Print($"vcvars: {vcVarsCmd}");
|
|
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 + ";";
|
}
|
}
|
}
|
}
|
}
|
|
// Get PATH
|
string envPath = startInfo.EnvironmentVariables["PATH"];
|
|
// Remove invalid chars
|
envPath = string.Join("", envPath.Split(Path.GetInvalidPathChars()));
|
|
// Split into list of paths
|
var paths = envPath
|
.Split(';')
|
.Where(x => !string.IsNullOrEmpty(x))
|
.Select(x => x.Trim());
|
|
// Check if cl.exe is in PATH
|
string clPath = paths
|
.Select(path => Path.Combine(path, "cl.exe"))
|
.Where(pathToCl => File.Exists(pathToCl))
|
.FirstOrDefault();
|
Messages.Print($"cl: {clPath ?? "NOT FOUND"}");
|
|
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 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());
|
}
|
|
public static string SafePath(string path)
|
{
|
if (string.IsNullOrEmpty(path))
|
return null;
|
path = path.Replace("\"", "");
|
if (!path.Contains(' '))
|
return path;
|
if (path.EndsWith("\\"))
|
path += "\\";
|
return string.Format("\"{0}\"", path);
|
}
|
|
public static bool IsConfExcludeFromSolution(VCFileConfiguration fileConfig)
|
{
|
ThreadHelper.ThrowIfNotOnUIThread();
|
|
var projectConfig = fileConfig.ProjectConfiguration as VCConfiguration;
|
var projectPlatform = projectConfig.Platform as VCPlatform;
|
var project = projectConfig.project as VCProject;
|
var projectEnv = project.Object as Project;
|
var dteEnv = projectEnv.DTE as DTE;
|
|
var contextList = new List<SolutionContext>();
|
foreach (SolutionConfiguration2 solutionCfg in dteEnv.Solution.SolutionBuild.SolutionConfigurations) {
|
if (solutionCfg.PlatformName == projectPlatform.Name && solutionCfg.Name == projectConfig.ConfigurationName) {
|
foreach (SolutionContext context in solutionCfg.SolutionContexts) {
|
if (context.ProjectName.Contains(project.Name + ".vcxproj")) {
|
// Данная коллекция будет содержать только один SolutionContext
|
// Возможно позже добавить проверку этого
|
contextList.Add(context);
|
}
|
}
|
}
|
}
|
return !contextList[0].ShouldBuild;
|
}
|
}
|
}
|