/**************************************************************************** ** ** 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 QtProjectLib.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 QtProjectLib { 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 _sources = new HashSet(new [] { ".c", ".cpp", ".cxx"}, StringComparer.OrdinalIgnoreCase); static public bool IsSourceFile(string fileName) { return _sources.Contains(Path.GetExtension(fileName)); } static readonly HashSet _headers = new HashSet(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) { // 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; } /// /// Returns the normalized file path of a given file. /// /// file name 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; } /// /// Reads lines from a .pro file that is opened with a StreamReader /// and concatenates strings that end with a backslash. /// /// /// the composite string 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; } /// /// Reads a .pro file and returns true if it is a subdirs template. /// /// full name of .pro file to read /// true if this is a subdirs file 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; } /// /// Returns the relative path between a given file and a path. /// /// absolute path /// absolute file name 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; } /// /// Replaces a string in the commandLine, description, outputs and additional dependencies /// in all Custom build tools of the project /// /// Project /// String, which is going to be replaced /// String, which is going to replace the other one /// 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(); } /// /// 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. /// /// File configuration /// 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; } /// /// 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" /// /// Project Item which needs to have custom build tool 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; } } } /// /// 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. /// /// 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; } /// /// 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. /// /// Project /// public static void ToggleProjectKind(Project project) { 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); } /// /// 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. /// /// The project is needed to convert relative paths to absolute paths. private static void ReplaceDirectory(ref List 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; } /// /// Return true if the project is a Qt project, otherwise false. /// /// project /// public static bool IsQtProject(VCProject proj) { if (!IsQMakeProject(proj)) return false; 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; } /// /// Returns true if the specified project is a Qt Project. /// /// project 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; } /// /// Return true if the project is a QMake -tp vc project, otherwise false. /// /// project /// public static bool IsQMakeProject(VCProject proj) { if (proj == null) return false; var keyword = proj.keyword; if (keyword == null || !keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal)) return false; return true; } /// /// Returns true if the specified project is a QMake -tp vc Project. /// /// project 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(); 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(); foreach (var dep in newDeps) uniques[dep] = 1; var uniqueList = new List(uniques.Keys); linkerWrapper.AdditionalDependencies = uniqueList; } } /// /// 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. /// /// Start point of the deletion public static void DeleteEmptyParentDirs(VCFile file) { var dir = file.FullPath.Remove(file.FullPath.LastIndexOf(Path.DirectorySeparatorChar)); DeleteEmptyParentDirs(dir); } /// /// Deletes the directory if it is empty and every empty parent directory until the first, /// non-empty directory is found. /// /// Start point of the deletion 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 GetProjectFiles(Project pro, FilesToList filter) { var fileList = new List(); 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; } /// /// Removes a file reference from the project and moves the file to the "Deleted" folder. /// /// /// 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]; } /// /// 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. /// 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 ProjectsInSolution(DTE dteObject) { var projects = new List(); 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 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 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; } /// /// Translates the machine type given as command line argument to the linker /// to the internal enum type VCProjectEngine.machineTypeOption. /// 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; } /// /// 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. /// 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)); } /// /// Performs an in-place expansion of MS Build properties in the form $(PropertyName) /// and project item metadata in the form %(MetadataName). /// Returns: 'true' if expansion was successful, 'false' otherwise /// : The string containing properties and/or metadata to /// expand. This string is passed by ref and expansion is performed in-place. /// : Current project. /// : Name of selected configuration (e.g. "Debug"). /// : Name of selected platform (e.g. "x64"). /// (optional): Evaluation context. /// 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); } } /// /// Performs an in-place expansion of MS Build properties in the form $(PropertyName) /// and project item metadata in the form %(MetadataName). /// Returns: 'true' if expansion was successful, 'false' otherwise /// : The string containing properties and/or metadata to /// expand. This string is passed by ref and expansion is performed in-place. /// : Either a VCConfiguration or VCFileConfiguration object to /// use as provider of property expansion (through Evaluate()). Cannot be null. /// 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 ""; } } private static string GetVCPath() { #if 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) { bool isOS64Bit = System.Environment.Is64BitOperatingSystem; bool isQt64Bit = QtVersionManager.The().GetVersionInfo( QtVersionManager.The().GetDefaultVersion()).is64Bit(); string vcPath = GetVCPath(); if (vcPath == "") return false; string comspecPath = Environment.GetEnvironmentVariable("COMSPEC"); #if VS2017 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> vcVars = new SortedDictionary>(); 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 vcVarValue = vcVars[key] = new List(); 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; } /// /// Rooted canonical path is the absolute path for the specified path string /// (cf. Path.GetFullPath()) without a trailing path separator. /// static string RootedCanonicalPath(string path) { try { return Path .GetFullPath(path) .TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); } catch { return ""; } } /// /// 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. /// 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); } } }