| /****************************************************************************  | 
| **  | 
| ** 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.IO;  | 
| using System.Collections.Generic;  | 
| using System.Text;  | 
| using System.Text.RegularExpressions;  | 
| using System.Linq;  | 
| using System.Xml;  | 
| using System.Xml.Linq;  | 
| using Microsoft.Build.Construction;  | 
| using Microsoft.Build.Execution;  | 
| using Microsoft.Build.Evaluation;  | 
| using Microsoft.VisualStudio.Shell;  | 
|   | 
| namespace QtVsTools.Core  | 
| {  | 
|     using QtMsBuild;  | 
|     using SyntaxAnalysis;  | 
|   | 
|     using static HelperFunctions;  | 
|     using static SyntaxAnalysis.RegExpr;  | 
|   | 
|     public class MsBuildProject  | 
|     {  | 
|         class MsBuildXmlFile  | 
|         {  | 
|             public string filePath = "";  | 
|             public XDocument xml = null;  | 
|             public bool isDirty = false;  | 
|             public XDocument xmlCommitted = null;  | 
|             public bool isCommittedDirty = false;  | 
|         }  | 
|   | 
|         enum Files  | 
|         {  | 
|             Project = 0,  | 
|             Filters,  | 
|             User,  | 
|             Count  | 
|         }  | 
|   | 
|         readonly MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count];  | 
|   | 
|         MsBuildProject()  | 
|         {  | 
|             for (int i = 0; i < files.Length; i++)  | 
|                 files[i] = new MsBuildXmlFile();  | 
|         }  | 
|   | 
|         MsBuildXmlFile this[Files file]  | 
|         {  | 
|             get  | 
|             {  | 
|                 if ((int)file >= (int)Files.Count)  | 
|                     return files[0];  | 
|                 return files[(int)file];  | 
|             }  | 
|         }  | 
|   | 
|         public string ProjectXml  | 
|         {  | 
|             get  | 
|             {  | 
|                 var xml = this[Files.Project].xml;  | 
|                 if (xml == null)  | 
|                     return "";  | 
|                 return xml.ToString(SaveOptions.None);  | 
|             }  | 
|         }  | 
|   | 
|         private static readonly XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";  | 
|   | 
|         public static MsBuildProject Load(string pathToProject)  | 
|         {  | 
|             if (!File.Exists(pathToProject))  | 
|                 return null;  | 
|   | 
|             MsBuildProject project = new MsBuildProject();  | 
|   | 
|             project[Files.Project].filePath = pathToProject;  | 
|             if (!LoadXml(project[Files.Project]))  | 
|                 return null;  | 
|   | 
|             project[Files.Filters].filePath = pathToProject + ".filters";  | 
|             if (File.Exists(project[Files.Filters].filePath) && !LoadXml(project[Files.Filters]))  | 
|                 return null;  | 
|   | 
|             project[Files.User].filePath = pathToProject + ".user";  | 
|             if (File.Exists(project[Files.User].filePath) && !LoadXml(project[Files.User]))  | 
|                 return null;  | 
|   | 
|             return project;  | 
|         }  | 
|   | 
|         static bool LoadXml(MsBuildXmlFile xmlFile)  | 
|         {  | 
|             try {  | 
|                 var xmlText = File.ReadAllText(xmlFile.filePath, Encoding.UTF8);  | 
|                 using (var reader = XmlReader.Create(new StringReader(xmlText))) {  | 
|                     xmlFile.xml = XDocument.Load(reader, LoadOptions.SetLineInfo);  | 
|                 }  | 
|             } catch (Exception) {  | 
|                 return false;  | 
|             }  | 
|             xmlFile.xmlCommitted = new XDocument(xmlFile.xml);  | 
|             return true;  | 
|         }  | 
|   | 
|         public bool Save()  | 
|         {  | 
|             foreach (var file in files) {  | 
|                 if (file.isDirty) {  | 
|                     file.xml?.Save(file.filePath, SaveOptions.None);  | 
|                     file.isCommittedDirty = file.isDirty = false;  | 
|                 }  | 
|             }  | 
|             return true;  | 
|         }  | 
|   | 
|         void Commit()  | 
|         {  | 
|             foreach (var file in files.Where(x => x.xml != null)) {  | 
|                 if (file.isDirty) {  | 
|                     //file was modified: sync committed copy  | 
|                     file.xmlCommitted = new XDocument(file.xml);  | 
|                     file.isCommittedDirty = true;  | 
|                 } else {  | 
|                     //fail-safe: ensure non-dirty files are in sync with committed copy  | 
|                     file.xml = new XDocument(file.xmlCommitted);  | 
|                     file.isDirty = file.isCommittedDirty;  | 
|                 }  | 
|             }  | 
|         }  | 
|   | 
|         void Rollback()  | 
|         {  | 
|             foreach (var file in files.Where(x => x.xml != null)) {  | 
|                 file.xml = new XDocument(file.xmlCommitted);  | 
|                 file.isDirty = file.isCommittedDirty;  | 
|             }  | 
|         }  | 
|   | 
|         public string GetProperty(string property_name)  | 
|         {  | 
|             var xProperty = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Elements()  | 
|                 .Where(x => x.Name.LocalName == property_name)  | 
|                 .FirstOrDefault();  | 
|             if (xProperty == null)  | 
|                 return string.Empty;  | 
|             return xProperty.Value;  | 
|         }  | 
|   | 
|         public string GetProperty(string item_type, string property_name)  | 
|         {  | 
|             var xProperty = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + item_type)  | 
|                 .Elements()  | 
|                 .Where(x => x.Name.LocalName == property_name)  | 
|                 .FirstOrDefault();  | 
|             if (xProperty == null)  | 
|                 return string.Empty;  | 
|             return xProperty.Value;  | 
|         }  | 
|   | 
|         public IEnumerable<string> GetItems(string item_type)  | 
|         {  | 
|             return this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + item_type)  | 
|                 .Select((XElement x) => (string)x.Attribute("Include"));  | 
|         }  | 
|   | 
|         public bool EnableMultiProcessorCompilation()  | 
|         {  | 
|             var xClCompileDefs = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + "ClCompile");  | 
|             foreach (var xClCompileDef in xClCompileDefs)  | 
|                 xClCompileDef.Add(new XElement(ns + "MultiProcessorCompilation", "true"));  | 
|   | 
|             this[Files.Project].isDirty = true;  | 
|             Commit();  | 
|             return true;  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Parser for project configuration conditional expressions of the type:  | 
|         ///  | 
|         ///     '$(Configuration)|$(Platform)'=='_TOKEN_|_TOKEN_'  | 
|         ///  | 
|         /// </summary>  | 
|         Parser _ConfigCondition;  | 
|         Parser ConfigCondition  | 
|         {  | 
|             get  | 
|             {  | 
|                 if (_ConfigCondition == null) {  | 
|                     var config = new Token("Configuration", CharWord.Repeat());  | 
|                     var platform = new Token("Platform", CharWord.Repeat());  | 
|                     var expr = "'$(Configuration)|$(Platform)'=='" & config & "|" & platform & "'";  | 
|                     try {  | 
|                         _ConfigCondition = expr.Render();  | 
|                     } catch { }  | 
|                 }  | 
|                 return _ConfigCondition;  | 
|             }  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Parser for project format version string:  | 
|         ///  | 
|         ///     QtVS_vNNN  | 
|         ///  | 
|         /// </summary>  | 
|         Parser _ProjectFormatVersion;  | 
|         Parser ProjectFormatVersion  | 
|         {  | 
|             get  | 
|             {  | 
|                 if (_ProjectFormatVersion == null) {  | 
|                     var expr = "QtVS_v" & new Token("VERSION", Char['0', '9'].Repeat(3))  | 
|                     {  | 
|                         new Rule<int> { Capture(value => int.Parse(value)) }  | 
|                     };  | 
|                     try {  | 
|                         _ProjectFormatVersion = expr.Render();  | 
|                     } catch { }  | 
|                 }  | 
|                 return _ProjectFormatVersion;  | 
|             }  | 
|         }  | 
|   | 
|         int? ParseProjectFormatVersion(string text)  | 
|         {  | 
|             if (ProjectFormatVersion == null)  | 
|                 return null;  | 
|             try {  | 
|                 return ProjectFormatVersion.Parse(text)  | 
|                     .GetValues<int>("VERSION")  | 
|                     .First();  | 
|             } catch {  | 
|                 return null;  | 
|             }  | 
|         }  | 
|   | 
|         const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase;  | 
|         readonly StringComparer IGNORE_CASE_ = StringComparer.InvariantCultureIgnoreCase;  | 
|   | 
|         /// <summary>  | 
|         /// Converts project format version to the latest version:  | 
|         ///  * Set latest project version;  | 
|         ///  * Add QtSettings property group;  | 
|         ///  * Set QtInstall property;  | 
|         ///  * Remove hard-coded macros, include paths and libs related to Qt modules.  | 
|         ///  * Set QtModules property;  | 
|         /// </summary>  | 
|         /// <returns>true if successful</returns>  | 
|         public bool UpdateProjectFormatVersion()  | 
|         {  | 
|             if (ConfigCondition == null)  | 
|                 return false;  | 
|   | 
|             var defaultVersionName = QtVersionManager.The().GetDefaultVersion();  | 
|             var defaultVersion = QtVersionManager.The().GetVersionInfo(defaultVersionName);  | 
|   | 
|             // Get project configurations  | 
|             var configs = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + "ProjectConfiguration");  | 
|   | 
|             // Get project global properties  | 
|             var globals = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(x => (string)x.Attribute("Label") == "Globals")  | 
|                 .FirstOrDefault();  | 
|             if (globals == null)  | 
|                 return false;  | 
|   | 
|             // Set Qt project format version  | 
|             var projKeyword = globals  | 
|                 .Elements(ns + "Keyword")  | 
|                 .Where(x => x.Value.StartsWith(Resources.qtProjectKeyword)  | 
|                     || x.Value.StartsWith(Resources.qtProjectV2Keyword))  | 
|                 .FirstOrDefault();  | 
|             if (projKeyword == null)  | 
|                 return false;  | 
|             var oldVersion = ParseProjectFormatVersion(projKeyword.Value);  | 
|             if (oldVersion.HasValue && oldVersion.Value == Resources.qtProjectFormatVersion)  | 
|                 return true; // nothing to do!  | 
|   | 
|             projKeyword.SetValue(string.Format("QtVS_v{0}", Resources.qtProjectFormatVersion));  | 
|   | 
|             // Find import of qt.props  | 
|             var qtPropsImport = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ImportGroup")  | 
|                 .Elements(ns + "Import")  | 
|                 .Where(x => (string)x.Attribute("Project") == @"$(QtMsBuild)\qt.props")  | 
|                 .FirstOrDefault();  | 
|             if (qtPropsImport == null)  | 
|                 return false;  | 
|   | 
|             var uncategorizedPropertyGroups = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(pg => pg.Attribute("Label") == null)  | 
|                 .ToList();  | 
|   | 
|             var propertyGroups = new Dictionary<string, XElement>();  | 
|   | 
|             // Upgrading from <= v3.2?  | 
|             if (!oldVersion.HasValue  | 
|                 || oldVersion.Value < Resources.qtMinFormatVersion_PropertyEval) {  | 
|   | 
|                 // Find import of default Qt properties  | 
|                 var qtDefaultProps = this[Files.Project].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ImportGroup")  | 
|                     .Elements(ns + "Import")  | 
|                     .Where(pg => Path.GetFileName((string)pg.Attribute("Project"))  | 
|                         .Equals("qt_defaults.props", StringComparison.InvariantCultureIgnoreCase))  | 
|                     .Select(pg => pg.Parent)  | 
|                     .FirstOrDefault();  | 
|   | 
|                 // Create uncategorized property groups  | 
|                 foreach (var config in configs) {  | 
|                     string condition = string.Format("'$(Configuration)|$(Platform)'=='{0}'",  | 
|                         (string)config.Attribute("Include"));  | 
|                     var group = new XElement(ns + "PropertyGroup",  | 
|                                     new XAttribute("Condition", condition));  | 
|                     propertyGroups[condition] = group;  | 
|                     // Insert uncategorized groups after Qt defaults, if found  | 
|                     if (qtDefaultProps != null)  | 
|                         qtDefaultProps.AddAfterSelf(group);  | 
|                 }  | 
|   | 
|                 // Move uncategorized properties to newly created groups  | 
|                 foreach (var pg in uncategorizedPropertyGroups) {  | 
|                     foreach (var p in pg.Elements().ToList()) {  | 
|                         var condition = p.Attribute("Condition") ?? pg.Attribute("Condition");  | 
|                         if (condition != null && propertyGroups  | 
|                             .TryGetValue((string)condition, out XElement configPropertyGroup)) {  | 
|                             p.Remove();  | 
|                             p.SetAttributeValue("Condition", null);  | 
|                             configPropertyGroup.Add(p);  | 
|                         }  | 
|                     }  | 
|                     if (!pg.Elements().Any())  | 
|                         pg.Remove();  | 
|                 }  | 
|             }  | 
|   | 
|             // Upgrading from <= v3.1?  | 
|             if (!oldVersion.HasValue  | 
|                 || oldVersion.Value < Resources.qtMinFormatVersion_GlobalQtMsBuildProperty) {  | 
|   | 
|                 // Move Qt/MSBuild path to global property  | 
|                 var qtMsBuildProperty = globals  | 
|                     .ElementsAfterSelf(ns + "PropertyGroup")  | 
|                     .Elements(ns + "QtMsBuild")  | 
|                     .FirstOrDefault();  | 
|                 if (qtMsBuildProperty != null) {  | 
|                     var qtMsBuildPropertyGroup = qtMsBuildProperty.Parent;  | 
|                     qtMsBuildProperty.Remove();  | 
|                     qtMsBuildProperty.SetAttributeValue("Condition",  | 
|                         (string)qtMsBuildPropertyGroup.Attribute("Condition"));  | 
|                     globals.Add(qtMsBuildProperty);  | 
|                     qtMsBuildPropertyGroup.Remove();  | 
|                 }  | 
|             }  | 
|             if (oldVersion.HasValue  | 
|                 && oldVersion.Value > Resources.qtMinFormatVersion_Settings) {  | 
|                 this[Files.Project].isDirty = true;  | 
|                 Commit();  | 
|                 return true;  | 
|             }  | 
|   | 
|             // Upgrading from v3.0?  | 
|             Dictionary<string, XElement> oldQtInstall = null;  | 
|             Dictionary<string, XElement> oldQtSettings = null;  | 
|             if (oldVersion.HasValue && oldVersion.Value == Resources.qtMinFormatVersion_Settings) {  | 
|                 oldQtInstall = this[Files.Project].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "PropertyGroup")  | 
|                     .Elements(ns + "QtInstall")  | 
|                     .ToDictionary(x => (string)x.Parent.Attribute("Condition"));  | 
|                 oldQtInstall.Values.ToList()  | 
|                     .ForEach(x => x.Remove());  | 
|   | 
|                 oldQtSettings = this[Files.Project].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "PropertyGroup")  | 
|                     .Where(x => (string)x.Attribute("Label") == "QtSettings")  | 
|                     .ToDictionary(x => (string)x.Attribute("Condition"));  | 
|                 oldQtSettings.Values.ToList()  | 
|                     .ForEach(x => x.Remove());  | 
|             }  | 
|   | 
|             // Find location for import of qt.props and for the QtSettings property group:  | 
|             // (cf. ".vcxproj file elements" https://docs.microsoft.com/en-us/cpp/build/reference/vcxproj-file-structure?view=vs-2019#vcxproj-file-elements)  | 
|             XElement insertionPoint = null;  | 
|   | 
|             // * After the last UserMacros property group  | 
|             insertionPoint = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(x => (string)x.Attribute("Label") == "UserMacros")  | 
|                 .LastOrDefault();  | 
|   | 
|             // * After the last PropertySheets import group  | 
|             insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ImportGroup")  | 
|                 .Where(x => (string)x.Attribute("Label") == "PropertySheets")  | 
|                 .LastOrDefault();  | 
|   | 
|             // * Before the first ItemDefinitionGroup  | 
|             insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Select(x => x.ElementsBeforeSelf().Last())  | 
|                 .FirstOrDefault();  | 
|   | 
|             // * Before the first ItemGroup  | 
|             insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Select(x => x.ElementsBeforeSelf().Last())  | 
|                 .FirstOrDefault();  | 
|   | 
|             // * Before the import of Microsoft.Cpp.targets  | 
|             insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "Import")  | 
|                 .Where(x =>  | 
|                     (string)x.Attribute("Project") == @"$(VCTargetsPath)\Microsoft.Cpp.targets")  | 
|                 .Select(x => x.ElementsBeforeSelf().Last())  | 
|                 .FirstOrDefault();  | 
|   | 
|             // * At the end of the file  | 
|             insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements()  | 
|                 .LastOrDefault();  | 
|   | 
|             if (insertionPoint == null)  | 
|                 return false;  | 
|   | 
|             // Move import of qt.props to insertion point  | 
|             if (qtPropsImport.Parent.Elements().SingleOrDefault() == qtPropsImport)  | 
|                 qtPropsImport.Parent.Remove(); // Remove import group  | 
|             else  | 
|                 qtPropsImport.Remove(); // Remove import (group contains other imports)  | 
|             insertionPoint.AddAfterSelf(  | 
|                 new XElement(ns + "ImportGroup",  | 
|                     new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.props')"),  | 
|                     new XElement(ns + "Import",  | 
|                         new XAttribute("Project", @"$(QtMsBuild)\qt.props"))));  | 
|   | 
|             // Create QtSettings property group above import of qt.props  | 
|             var qtSettings = new List<XElement>();  | 
|             foreach (var config in configs) {  | 
|                 var configQtSettings = new XElement(ns + "PropertyGroup",  | 
|                         new XAttribute("Label", "QtSettings"),  | 
|                         new XAttribute("Condition",  | 
|                             string.Format("'$(Configuration)|$(Platform)'=='{0}'",  | 
|                             (string)config.Attribute("Include"))));  | 
|                 insertionPoint.AddAfterSelf(configQtSettings);  | 
|                 qtSettings.Add(configQtSettings);  | 
|             }  | 
|   | 
|             // Add uncategorized property groups  | 
|             foreach (XElement propertyGroup in propertyGroups.Values)  | 
|                 insertionPoint.AddAfterSelf(propertyGroup);  | 
|   | 
|             // Add import of default property values  | 
|             insertionPoint.AddAfterSelf(  | 
|                 new XElement(ns + "ImportGroup",  | 
|                     new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt_defaults.props')"),  | 
|                     new XElement(ns + "Import",  | 
|                         new XAttribute("Project", @"$(QtMsBuild)\qt_defaults.props"))));  | 
|   | 
|             //// Upgrading from v3.0: move Qt settings to newly created import groups  | 
|             if (oldVersion.HasValue && oldVersion.Value == Resources.qtMinFormatVersion_Settings) {  | 
|                 foreach (var configQtSettings in qtSettings) {  | 
|                     var configCondition = (string)configQtSettings.Attribute("Condition");  | 
|   | 
|                     if (oldQtInstall.TryGetValue(configCondition, out XElement oldConfigQtInstall))  | 
|                         configQtSettings.Add(oldConfigQtInstall);  | 
|   | 
|                     if (oldQtSettings.TryGetValue(configCondition, out XElement oldConfigQtSettings)) {  | 
|                         foreach (var qtSetting in oldConfigQtSettings.Elements())  | 
|                             configQtSettings.Add(qtSetting);  | 
|                     }  | 
|                 }  | 
|   | 
|                 this[Files.Project].isDirty = true;  | 
|                 Commit();  | 
|                 return true;  | 
|             }  | 
|   | 
|             //// Upgrading from v2.0  | 
|   | 
|             // Get project user properties (old format)  | 
|             var userProps = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ProjectExtensions")  | 
|                 .Elements(ns + "VisualStudio")  | 
|                 .Elements(ns + "UserProperties")  | 
|                 .FirstOrDefault();  | 
|   | 
|             // Copy Qt build reference to QtInstall project property  | 
|             this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(x => ((string)x.Attribute("Label")) == Resources.projLabelQtSettings)  | 
|                 .ToList()  | 
|                 .ForEach(config =>  | 
|                 {  | 
|                     string qtInstallValue = defaultVersionName;  | 
|                     if (userProps != null) {  | 
|                         string platform = null;  | 
|                         try {  | 
|                             platform = ConfigCondition  | 
|                                 .Parse((string)config.Attribute("Condition"))  | 
|                                 .GetValues<string>("Platform")  | 
|                                 .FirstOrDefault();  | 
|                         } catch { }  | 
|   | 
|                         if (!string.IsNullOrEmpty(platform)) {  | 
|                             var qtInstallName = string.Format("Qt5Version_x0020_{0}", platform);  | 
|                             qtInstallValue = (string)userProps.Attribute(qtInstallName);  | 
|                         }  | 
|                     }  | 
|                     if (!string.IsNullOrEmpty(qtInstallValue))  | 
|                         config.Add(new XElement(ns + "QtInstall", qtInstallValue));  | 
|                 });  | 
|   | 
|             // Get C++ compiler properties  | 
|             var compiler = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + "ClCompile");  | 
|   | 
|             // Get linker properties  | 
|             var linker = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + "Link");  | 
|   | 
|             var resourceCompiler = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + "ResourceCompile");  | 
|   | 
|             // Qt module names, to copy to QtModules property  | 
|             var moduleNames = new HashSet<string>();  | 
|   | 
|             // Qt module macros, to remove from compiler macros property  | 
|             var moduleDefines = new HashSet<string>();  | 
|   | 
|             // Qt module includes, to remove from compiler include directories property  | 
|             var moduleIncludePaths = new HashSet<string>();  | 
|   | 
|             // Qt module link libraries, to remove from liker dependencies property  | 
|             var moduleLibs = new HashSet<string>();  | 
|   | 
|             // Go through all known Qt modules and check which ones are currently being used  | 
|             foreach (var module in QtModules.Instance.GetAvailableModules(defaultVersion.qtMajor)) {  | 
|   | 
|                 if (IsModuleUsed(module, compiler, linker, resourceCompiler)) {  | 
|   | 
|                     // Qt module names, to copy to QtModules property  | 
|                     if (!string.IsNullOrEmpty(module.proVarQT))  | 
|                         moduleNames.UnionWith(module.proVarQT.Split(' '));  | 
|   | 
|                     // Qt module macros, to remove from compiler macros property  | 
|                     moduleDefines.UnionWith(module.Defines);  | 
|   | 
|                     // Qt module includes, to remove from compiler include directories property  | 
|                     moduleIncludePaths.UnionWith(  | 
|                         module.IncludePath.Select(x => Path.GetFileName(x)));  | 
|   | 
|                     // Qt module link libraries, to remove from liker dependencies property  | 
|                     moduleLibs.UnionWith(  | 
|                         module.AdditionalLibraries.Select(x => Path.GetFileName(x)));  | 
|                     moduleLibs.UnionWith(  | 
|                         module.AdditionalLibrariesDebug.Select(x => Path.GetFileName(x)));  | 
|                     moduleLibs.Add(module.LibRelease);  | 
|                     moduleLibs.Add(module.LibDebug);  | 
|   | 
|                     if (IsPrivateIncludePathUsed(module, compiler)) {  | 
|                         // Qt private module names, to copy to QtModules property  | 
|                         moduleNames.UnionWith(module.proVarQT.Split(' ')  | 
|                             .Select(x => string.Format("{0}-private", x)));  | 
|                     }  | 
|                 }  | 
|             }  | 
|   | 
|             // Remove Qt module macros from compiler properties  | 
|             foreach (var defines in compiler.Elements(ns + "PreprocessorDefinitions")) {  | 
|                 defines.SetValue(string.Join(";", defines.Value.Split(';')  | 
|                     .Where(x => !moduleDefines.Contains(x))));  | 
|             }  | 
|   | 
|             // Remove Qt module include paths from compiler properties  | 
|             foreach (var inclPath in compiler.Elements(ns + "AdditionalIncludeDirectories")) {  | 
|                 inclPath.SetValue(string.Join(";", inclPath.Value.Split(';')  | 
|                     .Select(x => Unquote(x))  | 
|                     // Exclude paths rooted on $(QTDIR)  | 
|                     .Where(x => !x.StartsWith("$(QTDIR)", IGNORE_CASE))));  | 
|             }  | 
|   | 
|             // Remove Qt module libraries from linker properties  | 
|             foreach (var libs in linker.Elements(ns + "AdditionalDependencies")) {  | 
|                 libs.SetValue(string.Join(";", libs.Value.Split(';')  | 
|                     .Where(x => !moduleLibs.Contains(Path.GetFileName(Unquote(x)), IGNORE_CASE_))));  | 
|             }  | 
|   | 
|             // Remove Qt lib path from linker properties  | 
|             foreach (var libs in linker.Elements(ns + "AdditionalLibraryDirectories")) {  | 
|                 libs.SetValue(string.Join(";", libs.Value.Split(';')  | 
|                     .Select(x => Unquote(x))  | 
|                     // Exclude paths rooted on $(QTDIR)  | 
|                     .Where(x => !x.StartsWith("$(QTDIR)", IGNORE_CASE))));  | 
|             }  | 
|   | 
|             // Remove Qt module macros from resource compiler properties  | 
|             foreach (var defines in resourceCompiler.Elements(ns + "PreprocessorDefinitions")) {  | 
|                 defines.SetValue(string.Join(";", defines.Value.Split(';')  | 
|                     .Where(x => !moduleDefines.Contains(x))));  | 
|             }  | 
|   | 
|             // Add Qt module names to QtModules project property  | 
|             this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(x => ((string)x.Attribute("Label")) == Resources.projLabelQtSettings)  | 
|                 .ToList()  | 
|                 .ForEach(x => x.Add(new XElement(ns + "QtModules", string.Join(";", moduleNames))));  | 
|   | 
|             // Remove project user properties (old format)  | 
|             if (userProps != null) {  | 
|                 userProps.Attributes().ToList().ForEach(userProp =>  | 
|                 {  | 
|                     if (userProp.Name.LocalName == "lupdateOptions"  | 
|                         || userProp.Name.LocalName == "lupdateOnBuild"  | 
|                         || userProp.Name.LocalName == "lreleaseOptions"  | 
|                         || userProp.Name.LocalName == "MocDir"  | 
|                         || userProp.Name.LocalName == "MocOptions"  | 
|                         || userProp.Name.LocalName == "RccDir"  | 
|                         || userProp.Name.LocalName == "UicDir"  | 
|                         || userProp.Name.LocalName.StartsWith("Qt5Version_x0020_")) {  | 
|                         userProp.Remove();  | 
|                     }  | 
|                 });  | 
|             }  | 
|   | 
|             // Remove old properties from .user file  | 
|             if (this[Files.User].xml != null) {  | 
|                 this[Files.User].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "PropertyGroup")  | 
|                     .Elements()  | 
|                     .ToList()  | 
|                     .ForEach(userProp =>  | 
|                     {  | 
|                         if (userProp.Name.LocalName == "QTDIR"  | 
|                             || userProp.Name.LocalName == "QmlDebug"  | 
|                             || userProp.Name.LocalName == "QmlDebugSettings"  | 
|                             || (userProp.Name.LocalName == "LocalDebuggerCommandArguments"  | 
|                                 && (string)userProp == "$(QmlDebug)"  | 
|                             )  | 
|                             || (userProp.Name.LocalName == "LocalDebuggerEnvironment"  | 
|                                 && (string)userProp == "PATH=$(QTDIR)\\bin%3b$(PATH)"  | 
|                             )  | 
|                         ) {  | 
|                             userProp.Remove();  | 
|                         }  | 
|                     });  | 
|                 this[Files.User].isDirty = true;  | 
|             }  | 
|   | 
|             // Convert OutputFile --> <tool>Dir + <tool>FileName  | 
|             var qtItems = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .SelectMany(x => x.Elements(ns + "ItemDefinitionGroup")  | 
|                     .Union(x.Elements(ns + "ItemGroup")))  | 
|                 .SelectMany(x => x.Elements(ns + "QtMoc")  | 
|                     .Union(x.Elements(ns + "QtRcc"))  | 
|                     .Union(x.Elements(ns + "QtUic")));  | 
|             foreach (var qtItem in qtItems) {  | 
|                 var outputFile = qtItem.Element(ns + "OutputFile");  | 
|                 if (outputFile != null) {  | 
|                     var qtTool = qtItem.Name.LocalName;  | 
|                     var outDir = Path.GetDirectoryName(outputFile.Value);  | 
|                     var outFileName = Path.GetFileName(outputFile.Value);  | 
|                     if (!string.IsNullOrEmpty(outDir))  | 
|                         qtItem.Add(new XElement(ns + qtTool + "Dir", outDir));  | 
|                     else  | 
|                         qtItem.Add(new XElement(ns + qtTool + "Dir", "$(ProjectDir)"));  | 
|                     qtItem.Add(new XElement(ns + qtTool + "FileName", outFileName));  | 
|                 }  | 
|             }  | 
|   | 
|             // Remove old properties from project items  | 
|             var oldQtProps = new[] { "QTDIR", "InputFile", "OutputFile" };  | 
|             var oldCppProps = new[] { "IncludePath", "Define", "Undefine" };  | 
|             var oldPropsAny = oldQtProps.Union(oldCppProps);  | 
|             this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Union(this[Files.Project].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ItemGroup"))  | 
|                 .Elements().ToList().ForEach(item =>  | 
|                 {  | 
|                     var itemName = item.Name.LocalName;  | 
|                     item.Elements().ToList().ForEach(itemProp =>  | 
|                     {  | 
|                         var propName = itemProp.Name.LocalName;  | 
|                         if ((itemName == "QtMoc" && oldPropsAny.Contains(propName))  | 
|                             || (itemName == "QtRcc" && oldQtProps.Contains(propName))  | 
|                             || (itemName == "QtUic" && oldQtProps.Contains(propName))  | 
|                             || (itemName == "QtRepc" && oldPropsAny.Contains(propName))  | 
|                         ) {  | 
|                             itemProp.Remove();  | 
|                         }  | 
|                     });  | 
|                 });  | 
|   | 
|             this[Files.Project].isDirty = true;  | 
|             Commit();  | 
|             return true;  | 
|         }  | 
|   | 
|         bool IsModuleUsed(  | 
|             QtModule module,  | 
|             IEnumerable<XElement> compiler,  | 
|             IEnumerable<XElement> linker,  | 
|             IEnumerable<XElement> resourceCompiler)  | 
|         {  | 
|             // Module .lib is present in linker additional dependencies  | 
|             if (linker.Elements(ns + "AdditionalDependencies")  | 
|                 .SelectMany(x => x.Value.Split(';'))  | 
|                 .Any(x => Path.GetFileName(Unquote(x)).Equals(module.LibRelease, IGNORE_CASE)  | 
|                     || Path.GetFileName(Unquote(x)).Equals(module.LibDebug, IGNORE_CASE))) {  | 
|                 return true;  | 
|             }  | 
|   | 
|             // Module macro is present in the compiler pre-processor definitions  | 
|             if (compiler.Elements(ns + "PreprocessorDefinitions")  | 
|                 .SelectMany(x => x.Value.Split(';'))  | 
|                 .Any(x => module.Defines.Contains(x))) {  | 
|                 return true;  | 
|             }  | 
|   | 
|             // Module macro is present in resource compiler pre-processor definitions  | 
|             if (resourceCompiler.Elements(ns + "PreprocessorDefinitions")  | 
|                 .SelectMany(x => x.Value.Split(';'))  | 
|                 .Any(x => module.Defines.Contains(x))) {  | 
|                 return true;  | 
|             }  | 
|   | 
|             // Module is not present  | 
|             return false;  | 
|         }  | 
|   | 
|         bool IsPrivateIncludePathUsed(  | 
|             QtModule module,  | 
|             IEnumerable<XElement> compiler)  | 
|         {  | 
|             // Module private header path is present in compiler include dirs  | 
|             var privateIncludePattern = new Regex(string.Format(  | 
|                 @"^\$\(QTDIR\)[\\\/]include[\\\/]{0}[\\\/]\d+\.\d+\.\d+",  | 
|                 module.LibraryPrefix));  | 
|             if (compiler.Elements(ns + "AdditionalIncludeDirectories")  | 
|                 .SelectMany(x => x.Value.Split(';'))  | 
|                 .Any(x => privateIncludePattern.IsMatch(x))) {  | 
|                 return true;  | 
|             }  | 
|   | 
|             // Private header path is not present  | 
|             return false;  | 
|         }  | 
|   | 
|         public bool SetDefaultWindowsSDKVersion(string winSDKVersion)  | 
|         {  | 
|             var xGlobals = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "PropertyGroup")  | 
|                 .Where(x => (string)x.Attribute("Label") == "Globals")  | 
|                 .FirstOrDefault();  | 
|             if (xGlobals == null)  | 
|                 return false;  | 
|             if (xGlobals.Element(ns + "WindowsTargetPlatformVersion") != null)  | 
|                 return true;  | 
|             xGlobals.Add(  | 
|                 new XElement(ns + "WindowsTargetPlatformVersion", winSDKVersion));  | 
|   | 
|             this[Files.Project].isDirty = true;  | 
|             Commit();  | 
|             return true;  | 
|         }  | 
|   | 
|         public bool AddQtMsBuildReferences()  | 
|         {  | 
|             var isQtMsBuildEnabled = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ImportGroup")  | 
|                 .Elements(ns + "Import")  | 
|                 .Where(x =>  | 
|                     x.Attribute("Project").Value == @"$(QtMsBuild)\qt.props")  | 
|                 .Any();  | 
|             if (isQtMsBuildEnabled)  | 
|                 return true;  | 
|   | 
|             var xImportCppProps = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "Import")  | 
|                 .Where(x =>  | 
|                     x.Attribute("Project").Value == @"$(VCTargetsPath)\Microsoft.Cpp.props")  | 
|                 .FirstOrDefault();  | 
|             if (xImportCppProps == null)  | 
|                 return false;  | 
|   | 
|             var xImportCppTargets = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "Import")  | 
|                 .Where(x =>  | 
|                     x.Attribute("Project").Value == @"$(VCTargetsPath)\Microsoft.Cpp.targets")  | 
|                 .FirstOrDefault();  | 
|             if (xImportCppTargets == null)  | 
|                 return false;  | 
|   | 
|             xImportCppProps.AddAfterSelf(  | 
|                 new XElement(ns + "PropertyGroup",  | 
|                     new XAttribute("Condition",  | 
|                         @"'$(QtMsBuild)'=='' " +  | 
|                         @"or !Exists('$(QtMsBuild)\qt.targets')"),  | 
|                     new XElement(ns + "QtMsBuild",  | 
|                         @"$(MSBuildProjectDirectory)\QtMsBuild")),  | 
|   | 
|                 new XElement(ns + "Target",  | 
|                     new XAttribute("Name", "QtMsBuildNotFound"),  | 
|                     new XAttribute("BeforeTargets", "CustomBuild;ClCompile"),  | 
|                     new XAttribute("Condition",  | 
|                         @"!Exists('$(QtMsBuild)\qt.targets') " +  | 
|                         @"or !Exists('$(QtMsBuild)\qt.props')"),  | 
|                     new XElement(ns + "Message",  | 
|                         new XAttribute("Importance", "High"),  | 
|                         new XAttribute("Text",  | 
|                             "QtMsBuild: could not locate qt.targets, qt.props; " +  | 
|                             "project may not build correctly."))),  | 
|   | 
|                 new XElement(ns + "ImportGroup",  | 
|                     new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.props')"),  | 
|                     new XElement(ns + "Import",  | 
|                         new XAttribute("Project", @"$(QtMsBuild)\qt.props"))));  | 
|   | 
|             xImportCppTargets.AddAfterSelf(  | 
|                 new XElement(ns + "ImportGroup",  | 
|                     new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.targets')"),  | 
|                     new XElement(ns + "Import",  | 
|                         new XAttribute("Project", @"$(QtMsBuild)\qt.targets"))));  | 
|   | 
|             this[Files.Project].isDirty = true;  | 
|             Commit();  | 
|             return true;  | 
|         }  | 
|   | 
|         delegate string ItemCommandLineReplacement(string itemName, string cmdLine);  | 
|   | 
|         bool SetCommandLines(  | 
|             QtMsBuildContainer qtMsBuild,  | 
|             IEnumerable<XElement> configurations,  | 
|             IEnumerable<XElement> customBuilds,  | 
|             string toolExec,  | 
|             string itemType,  | 
|             string workingDir,  | 
|             IEnumerable<ItemCommandLineReplacement> extraReplacements)  | 
|         {  | 
|             var query = from customBuild in customBuilds  | 
|                         let itemName = customBuild.Attribute("Include").Value  | 
|                         from config in configurations  | 
|                         from command in customBuild.Elements(ns + "Command")  | 
|                         where command.Attribute("Condition").Value  | 
|                             == string.Format(  | 
|                                 "'$(Configuration)|$(Platform)'=='{0}'",  | 
|                                 (string)config.Attribute("Include"))  | 
|                         select new { customBuild, itemName, config, command };  | 
|   | 
|             var projPath = this[Files.Project].filePath;  | 
|             bool error = false;  | 
|             using (var evaluator = new MSBuildEvaluator(this[Files.Project])) {  | 
|                 foreach (var row in query) {  | 
|   | 
|                     var configId = (string)row.config.Attribute("Include");  | 
|                     if (!row.command.Value.Contains(toolExec)) {  | 
|                         Messages.Print(string.Format(  | 
|                             "{0}: warning: [{1}] converting \"{2}\", configuration \"{3}\": " +  | 
|                             "tool not found: \"{4}\"; applying default options",  | 
|                             projPath, itemType, row.itemName, configId, toolExec));  | 
|                         continue;  | 
|                     }  | 
|   | 
|                     XElement item;  | 
|                     row.customBuild.Add(item =  | 
|                         new XElement(ns + itemType,  | 
|                             new XAttribute("Include", row.itemName),  | 
|                             new XAttribute("ConfigName", configId)));  | 
|                     var configName = (string)row.config.Element(ns + "Configuration");  | 
|                     var platformName = (string)row.config.Element(ns + "Platform");  | 
|   | 
|                     ///////////////////////////////////////////////////////////////////////////////  | 
|                     // Replace fixed values with VS macros  | 
|                     //  | 
|                     //   * Filename, e.g. foo.ui --> %(Filename)%(Extension)  | 
|                     var commandLine = row.command.Value  | 
|                         .Replace(Path.GetFileName(row.itemName), "%(Filename)%(Extension)",  | 
|                             StringComparison.InvariantCultureIgnoreCase);  | 
|                     //  | 
|                     //   * Context specific, e.g. ui_foo.h --> ui_%(FileName).h  | 
|                     foreach (var replace in extraReplacements)  | 
|                         commandLine = replace(row.itemName, commandLine);  | 
|                     //  | 
|                     //   * Configuration/platform, e.g. x64\Debug --> $(Platform)\$(Configuration)  | 
|                     //   * ignore any word other than the expected configuration, e.g. lrelease.exe  | 
|                     commandLine = Regex.Replace(commandLine, @"\b" + configName + @"\b",  | 
|                             "$(Configuration)", RegexOptions.IgnoreCase)  | 
|                         .Replace(platformName, "$(Platform)",  | 
|                             StringComparison.InvariantCultureIgnoreCase);  | 
|   | 
|                     evaluator.Properties.Clear();  | 
|                     foreach (var configProp in row.config.Elements())  | 
|                         evaluator.Properties.Add(configProp.Name.LocalName, (string)configProp);  | 
|                     if (!qtMsBuild.SetCommandLine(itemType, item, commandLine, evaluator)) {  | 
|                         int lineNumber = 1;  | 
|                         if (row.command is IXmlLineInfo errorLine && errorLine.HasLineInfo())  | 
|                             lineNumber = errorLine.LineNumber;  | 
|   | 
|                         Messages.Print(string.Format(  | 
|                             "{0}({1}): error: [{2}] converting \"{3}\", configuration \"{4}\": " +  | 
|                             "failed to convert custom build command",  | 
|                             projPath, lineNumber, itemType, row.itemName, configId));  | 
|   | 
|                         item.Remove();  | 
|                         error = true;  | 
|                     }  | 
|                 }  | 
|             }  | 
|   | 
|             return !error;  | 
|         }  | 
|   | 
|         IEnumerable<XElement> GetCustomBuilds(string toolExecName)  | 
|         {  | 
|             return this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + "CustomBuild")  | 
|                 .Where(x => x.Elements(ns + "Command")  | 
|                     .Where(y => (y.Value.Contains(toolExecName))).Any());  | 
|         }  | 
|   | 
|         void FinalizeProjectChanges(List<XElement> customBuilds, string itemTypeName)  | 
|         {  | 
|             customBuilds  | 
|                 .Elements().Where(  | 
|                     elem => elem.Name.LocalName != itemTypeName)  | 
|                 .ToList().ForEach(oldElem => oldElem.Remove());  | 
|   | 
|             customBuilds.Elements(ns + itemTypeName).ToList().ForEach(item =>  | 
|             {  | 
|                 item.Elements().ToList().ForEach(prop =>  | 
|                 {  | 
|                     string configName = prop.Parent.Attribute("ConfigName").Value;  | 
|                     prop.SetAttributeValue("Condition",  | 
|                         string.Format("'$(Configuration)|$(Platform)'=='{0}'", configName));  | 
|                     prop.Remove();  | 
|                     item.Parent.Add(prop);  | 
|                 });  | 
|                 item.Remove();  | 
|             });  | 
|   | 
|             customBuilds.ForEach(customBuild =>  | 
|             {  | 
|                 var filterCustomBuild = this[Files.Filters]?.xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ItemGroup")  | 
|                     .Elements(ns + "CustomBuild")  | 
|                     .Where(filterItem =>  | 
|                         filterItem.Attribute("Include").Value  | 
|                         == customBuild.Attribute("Include").Value)  | 
|                     .FirstOrDefault();  | 
|                 if (filterCustomBuild != null) {  | 
|                     filterCustomBuild.Name = ns + itemTypeName;  | 
|                     this[Files.Filters].isDirty = true;  | 
|                 }  | 
|   | 
|                 customBuild.Name = ns + itemTypeName;  | 
|                 this[Files.Project].isDirty = true;  | 
|             });  | 
|         }  | 
|   | 
|         string AddGeneratedFilesPath(string includePathList)  | 
|         {  | 
|             HashSet<string> includes = new HashSet<string> {  | 
|                     QtVSIPSettings.GetMocDirectory(),  | 
|                     QtVSIPSettings.GetRccDirectory(),  | 
|                     QtVSIPSettings.GetUicDirectory(),  | 
|                 };  | 
|             foreach (var includePath in includePathList.Split(new char[] { ';' }))  | 
|                 includes.Add(includePath);  | 
|             return string.Join<string>(";", includes);  | 
|         }  | 
|   | 
|         string CustomBuildMocInput(XElement cbt)  | 
|         {  | 
|             var commandLine = (string)cbt.Element(ns + "Command");  | 
|             Dictionary<QtMoc.Property, string> properties;  | 
|             using (var evaluator = new MSBuildEvaluator(this[Files.Project])) {  | 
|                 if (!QtMsBuildContainer.QtMocInstance.ParseCommandLine(  | 
|                     commandLine, evaluator, out properties)) {  | 
|                     return (string)cbt.Attribute("Include");  | 
|                 }  | 
|             }  | 
|             if (!properties.TryGetValue(QtMoc.Property.InputFile, out string ouputFile))  | 
|                 return (string)cbt.Attribute("Include");  | 
|             return ouputFile;  | 
|         }  | 
|   | 
|         bool RemoveGeneratedFiles(  | 
|             string projDir,  | 
|             List<CustomBuildEval> cbEvals,  | 
|             string configName,  | 
|             string itemName,  | 
|             Dictionary<string, List<XElement>> projItemsByPath,  | 
|             Dictionary<string, List<XElement>> filterItemsByPath)  | 
|         {  | 
|             //remove items with generated files  | 
|             bool hasGeneratedFiles = false;  | 
|             var cbEval = cbEvals  | 
|                 .Where(x => x.ProjectConfig == configName && x.Identity == itemName)  | 
|                 .FirstOrDefault();  | 
|             if (cbEval != null) {  | 
|                 var outputFiles = cbEval.Outputs  | 
|                     .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)  | 
|                     .Select(x => HelperFunctions.CanonicalPath(  | 
|                         Path.IsPathRooted(x) ? x : Path.Combine(projDir, x)));  | 
|                 var outputItems = new List<XElement>();  | 
|                 foreach (var outputFile in outputFiles) {  | 
|                     if (projItemsByPath.TryGetValue(outputFile, out List<XElement> mocOutput)) {  | 
|                         outputItems.AddRange(mocOutput);  | 
|                         hasGeneratedFiles |= hasGeneratedFiles ? true : mocOutput  | 
|                             .Where(x => !x.Elements(ns + "ExcludedFromBuild")  | 
|                                 .Where(y =>  | 
|                                     (string)y.Attribute("Condition") == string.Format(  | 
|                                         "'$(Configuration)|$(Platform)'=='{0}'", configName)  | 
|                                     && y.Value == "true")  | 
|                                 .Any())  | 
|                             .Any();  | 
|                     }  | 
|                     if (filterItemsByPath.TryGetValue(outputFile, out mocOutput))  | 
|                         outputItems.AddRange(mocOutput);  | 
|                 }  | 
|                 foreach (var item in outputItems.Where(x => x.Parent != null))  | 
|                     item.Remove();  | 
|             }  | 
|             return hasGeneratedFiles;  | 
|         }  | 
|   | 
|         public bool ConvertCustomBuildToQtMsBuild()  | 
|         {  | 
|             var cbEvals = EvaluateCustomBuild();  | 
|   | 
|             var qtMsBuild = new QtMsBuildContainer(new MsBuildConverterProvider());  | 
|             qtMsBuild.BeginSetItemProperties();  | 
|   | 
|             var projDir = Path.GetDirectoryName(this[Files.Project].filePath);  | 
|   | 
|             var configurations = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + "ProjectConfiguration");  | 
|   | 
|             var projItemsByPath = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements()  | 
|                 .Where(x => ((string)x.Attribute("Include"))  | 
|                     .IndexOfAny(Path.GetInvalidPathChars()) == -1)  | 
|                 .GroupBy(x => HelperFunctions.CanonicalPath(  | 
|                     Path.Combine(projDir, (string)x.Attribute("Include"))),  | 
|                     StringComparer.InvariantCultureIgnoreCase)  | 
|                 .ToDictionary(x => x.Key, x => new List<XElement>(x));  | 
|   | 
|             var filterItemsByPath = (this[Files.Filters].xml != null)  | 
|                 ? this[Files.Filters].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ItemGroup")  | 
|                     .Elements()  | 
|                     .Where(x => ((string)x.Attribute("Include"))  | 
|                         .IndexOfAny(Path.GetInvalidPathChars()) == -1)  | 
|                     .GroupBy(x => HelperFunctions.CanonicalPath(  | 
|                         Path.Combine(projDir, (string)x.Attribute("Include"))),  | 
|                         StringComparer.InvariantCultureIgnoreCase)  | 
|                     .ToDictionary(x => x.Key, x => new List<XElement>(x))  | 
|                 : new Dictionary<string, List<XElement>>();  | 
|   | 
|             var cppIncludePaths = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemDefinitionGroup")  | 
|                 .Elements(ns + "ClCompile")  | 
|                 .Elements(ns + "AdditionalIncludeDirectories");  | 
|   | 
|             //add generated files path to C++ additional include dirs  | 
|             foreach (var cppIncludePath in cppIncludePaths)  | 
|                 cppIncludePath.Value = AddGeneratedFilesPath((string)cppIncludePath);  | 
|   | 
|             // replace each set of .moc.cbt custom build steps  | 
|             // with a single .cpp custom build step  | 
|             var mocCbtCustomBuilds = GetCustomBuilds(QtMoc.ToolExecName)  | 
|                 .Where(x =>  | 
|                 ((string)x.Attribute("Include")).EndsWith(".cbt",  | 
|                 StringComparison.InvariantCultureIgnoreCase)  | 
|                 || ((string)x.Attribute("Include")).EndsWith(".moc",  | 
|                 StringComparison.InvariantCultureIgnoreCase))  | 
|                 .GroupBy(cbt => CustomBuildMocInput(cbt));  | 
|   | 
|             List<XElement> cbtToRemove = new List<XElement>();  | 
|             foreach (var cbtGroup in mocCbtCustomBuilds) {  | 
|   | 
|                 //create new CustomBuild item for .cpp  | 
|                 var newCbt = new XElement(ns + "CustomBuild",  | 
|                     new XAttribute("Include", cbtGroup.Key),  | 
|                     new XElement(ns + "FileType", "Document"));  | 
|   | 
|                 //add properties from .moc.cbt items  | 
|                 List<string> cbtPropertyNames = new List<string> {  | 
|                     "AdditionalInputs",  | 
|                     "Command",  | 
|                     "Message",  | 
|                     "Outputs",  | 
|                 };  | 
|                 foreach (var cbt in cbtGroup) {  | 
|                     var enabledProperties = cbt.Elements().Where(x =>  | 
|                         cbtPropertyNames.Contains(x.Name.LocalName)  | 
|                         && !x.Parent.Elements(ns + "ExcludedFromBuild").Where(y =>  | 
|                         (string)x.Attribute("Condition") == (string)y.Attribute("Condition"))  | 
|                         .Any());  | 
|                     foreach (var property in enabledProperties)  | 
|                         newCbt.Add(new XElement(property));  | 
|                     cbtToRemove.Add(cbt);  | 
|                 }  | 
|                 cbtGroup.First().AddBeforeSelf(newCbt);  | 
|   | 
|                 //remove ClCompile item (cannot have duplicate items)  | 
|                 var cppMocItems = this[Files.Project].xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ItemGroup")  | 
|                     .Elements(ns + "ClCompile")  | 
|                     .Where(x =>  | 
|                         cbtGroup.Key.Equals((string)x.Attribute("Include"),  | 
|                         StringComparison.InvariantCultureIgnoreCase));  | 
|                 foreach (var cppMocItem in cppMocItems)  | 
|                     cppMocItem.Remove();  | 
|   | 
|                 //change type of item in filter  | 
|                 cppMocItems = this[Files.Filters]?.xml  | 
|                     .Elements(ns + "Project")  | 
|                     .Elements(ns + "ItemGroup")  | 
|                     .Elements(ns + "ClCompile")  | 
|                     .Where(x =>  | 
|                         cbtGroup.Key.Equals((string)x.Attribute("Include"),  | 
|                         StringComparison.InvariantCultureIgnoreCase));  | 
|                 foreach (var cppMocItem in cppMocItems)  | 
|                     cppMocItem.Name = ns + "CustomBuild";  | 
|             }  | 
|   | 
|             //remove .moc.cbt CustomBuild items  | 
|             cbtToRemove.ForEach(x => x.Remove());  | 
|   | 
|             //convert moc custom build steps  | 
|             var mocCustomBuilds = GetCustomBuilds(QtMoc.ToolExecName);  | 
|             if (!SetCommandLines(qtMsBuild, configurations, mocCustomBuilds,  | 
|                 QtMoc.ToolExecName, QtMoc.ItemTypeName,  | 
|                 Path.GetDirectoryName(this[Files.Project].filePath),  | 
|                 new ItemCommandLineReplacement[]  | 
|                 {  | 
|                     (item, cmdLine) => cmdLine.Replace(  | 
|                         string.Format(@"\moc_{0}.cpp", Path.GetFileNameWithoutExtension(item)),  | 
|                         @"\moc_%(Filename).cpp", StringComparison.InvariantCultureIgnoreCase)  | 
|                     .Replace(  | 
|                         string.Format(" -o moc_{0}.cpp", Path.GetFileNameWithoutExtension(item)),  | 
|                         @" -o $(ProjectDir)\moc_%(Filename).cpp",  | 
|                             StringComparison.InvariantCultureIgnoreCase),  | 
|   | 
|                     (item, cmdLine) => cmdLine.Replace(  | 
|                         string.Format(@"\{0}.moc", Path.GetFileNameWithoutExtension(item)),  | 
|                         @"\%(Filename).moc", StringComparison.InvariantCultureIgnoreCase)  | 
|                     .Replace(  | 
|                         string.Format(" -o {0}.moc", Path.GetFileNameWithoutExtension(item)),  | 
|                         @" -o $(ProjectDir)\%(Filename).moc",  | 
|                             StringComparison.InvariantCultureIgnoreCase),  | 
|                 })) {  | 
|                 Rollback();  | 
|                 return false;  | 
|             }  | 
|             List<XElement> mocDisableDynamicSource = new List<XElement>();  | 
|             foreach (var qtMoc in mocCustomBuilds.Elements(ns + QtMoc.ItemTypeName)) {  | 
|                 var itemName = (string)qtMoc.Attribute("Include");  | 
|                 var configName = (string)qtMoc.Attribute("ConfigName");  | 
|   | 
|                 //remove items with generated files  | 
|                 var hasGeneratedFiles = RemoveGeneratedFiles(  | 
|                     projDir, cbEvals, configName, itemName,  | 
|                     projItemsByPath, filterItemsByPath);  | 
|   | 
|                 //set properties  | 
|                 qtMsBuild.SetItemProperty(qtMoc,  | 
|                     QtMoc.Property.ExecutionDescription, "Moc'ing %(Identity)...");  | 
|                 qtMsBuild.SetItemProperty(qtMoc,  | 
|                     QtMoc.Property.InputFile, "%(FullPath)");  | 
|                 if (!HelperFunctions.IsSourceFile(itemName)) {  | 
|                     qtMsBuild.SetItemProperty(qtMoc,  | 
|                         QtMoc.Property.DynamicSource, "output");  | 
|                     if (!hasGeneratedFiles)  | 
|                         mocDisableDynamicSource.Add(qtMoc);  | 
|                 } else {  | 
|                     qtMsBuild.SetItemProperty(qtMoc,  | 
|                         QtMoc.Property.DynamicSource, "input");  | 
|                 }  | 
|                 var includePath = qtMsBuild.GetPropertyChangedValue(  | 
|                     QtMoc.Property.IncludePath, itemName, configName);  | 
|                 if (!string.IsNullOrEmpty(includePath)) {  | 
|                     qtMsBuild.SetItemProperty(qtMoc,  | 
|                         QtMoc.Property.IncludePath, AddGeneratedFilesPath(includePath));  | 
|                 }  | 
|             }  | 
|   | 
|             //convert rcc custom build steps  | 
|             var rccCustomBuilds = GetCustomBuilds(QtRcc.ToolExecName);  | 
|             if (!SetCommandLines(qtMsBuild, configurations, rccCustomBuilds,  | 
|                 QtRcc.ToolExecName, QtRcc.ItemTypeName,  | 
|                 Path.GetDirectoryName(this[Files.Project].filePath),  | 
|                 new ItemCommandLineReplacement[]  | 
|                 {  | 
|                     (item, cmdLine) => cmdLine.Replace(  | 
|                         string.Format(@"\qrc_{0}.cpp", Path.GetFileNameWithoutExtension(item)),  | 
|                         @"\qrc_%(Filename).cpp", StringComparison.InvariantCultureIgnoreCase)  | 
|                     .Replace(  | 
|                         string.Format(" -o qrc_{0}.cpp", Path.GetFileNameWithoutExtension(item)),  | 
|                         @" -o $(ProjectDir)\qrc_%(Filename).cpp",  | 
|                             StringComparison.InvariantCultureIgnoreCase),  | 
|                 })) {  | 
|                 Rollback();  | 
|                 return false;  | 
|             }  | 
|             foreach (var qtRcc in rccCustomBuilds.Elements(ns + QtRcc.ItemTypeName)) {  | 
|                 var itemName = (string)qtRcc.Attribute("Include");  | 
|                 var configName = (string)qtRcc.Attribute("ConfigName");  | 
|   | 
|                 //remove items with generated files  | 
|                 RemoveGeneratedFiles(projDir, cbEvals, configName, itemName,  | 
|                     projItemsByPath, filterItemsByPath);  | 
|   | 
|                 //set properties  | 
|                 qtMsBuild.SetItemProperty(qtRcc,  | 
|                     QtRcc.Property.ExecutionDescription, "Rcc'ing %(Identity)...");  | 
|                 qtMsBuild.SetItemProperty(qtRcc,  | 
|                     QtRcc.Property.InputFile, "%(FullPath)");  | 
|             }  | 
|   | 
|             //convert repc custom build steps  | 
|             var repcCustomBuilds = GetCustomBuilds(QtRepc.ToolExecName);  | 
|             if (!SetCommandLines(qtMsBuild, configurations, repcCustomBuilds,  | 
|                 QtRepc.ToolExecName, QtRepc.ItemTypeName,  | 
|                 Path.GetDirectoryName(this[Files.Project].filePath),  | 
|                 new ItemCommandLineReplacement[] { })) {  | 
|                 Rollback();  | 
|                 return false;  | 
|             }  | 
|             foreach (var qtRepc in repcCustomBuilds.Elements(ns + QtRepc.ItemTypeName)) {  | 
|                 var itemName = (string)qtRepc.Attribute("Include");  | 
|                 var configName = (string)qtRepc.Attribute("ConfigName");  | 
|   | 
|                 //remove items with generated files  | 
|                 RemoveGeneratedFiles(projDir, cbEvals, configName, itemName,  | 
|                     projItemsByPath, filterItemsByPath);  | 
|   | 
|                 //set properties  | 
|                 qtMsBuild.SetItemProperty(qtRepc,  | 
|                     QtRepc.Property.ExecutionDescription, "Repc'ing %(Identity)...");  | 
|                 qtMsBuild.SetItemProperty(qtRepc,  | 
|                     QtRepc.Property.InputFile, "%(FullPath)");  | 
|             }  | 
|   | 
|   | 
|             //convert uic custom build steps  | 
|             var uicCustomBuilds = GetCustomBuilds(QtUic.ToolExecName);  | 
|             if (!SetCommandLines(qtMsBuild, configurations, uicCustomBuilds,  | 
|                 QtUic.ToolExecName, QtUic.ItemTypeName,  | 
|                 Path.GetDirectoryName(this[Files.Project].filePath),  | 
|                 new ItemCommandLineReplacement[]  | 
|                 {  | 
|                     (item, cmdLine) => cmdLine.Replace(  | 
|                         string.Format(@"\ui_{0}.h", Path.GetFileNameWithoutExtension(item)),  | 
|                         @"\ui_%(Filename).h", StringComparison.InvariantCultureIgnoreCase)  | 
|                     .Replace(  | 
|                         string.Format(" -o ui_{0}.h", Path.GetFileNameWithoutExtension(item)),  | 
|                         @" -o $(ProjectDir)\ui_%(Filename).h",  | 
|                             StringComparison.InvariantCultureIgnoreCase),  | 
|                 })) {  | 
|                 Rollback();  | 
|                 return false;  | 
|             }  | 
|             foreach (var qtUic in uicCustomBuilds.Elements(ns + QtUic.ItemTypeName)) {  | 
|                 var itemName = (string)qtUic.Attribute("Include");  | 
|                 var configName = (string)qtUic.Attribute("ConfigName");  | 
|   | 
|                 //remove items with generated files  | 
|                 RemoveGeneratedFiles(projDir, cbEvals, configName, itemName,  | 
|                     projItemsByPath, filterItemsByPath);  | 
|   | 
|                 //set properties  | 
|                 qtMsBuild.SetItemProperty(qtUic,  | 
|                     QtUic.Property.ExecutionDescription, "Uic'ing %(Identity)...");  | 
|                 qtMsBuild.SetItemProperty(qtUic,  | 
|                     QtUic.Property.InputFile, "%(FullPath)");  | 
|             }  | 
|   | 
|             qtMsBuild.EndSetItemProperties();  | 
|   | 
|             //disable dynamic C++ source for moc headers without generated files  | 
|             //(needed for the case of #include "moc_foo.cpp" in source file)  | 
|             foreach (var qtMoc in mocDisableDynamicSource) {  | 
|                 qtMsBuild.SetItemProperty(qtMoc,  | 
|                     QtMoc.Property.DynamicSource, "false");  | 
|             }  | 
|   | 
|             FinalizeProjectChanges(mocCustomBuilds.ToList(), QtMoc.ItemTypeName);  | 
|             FinalizeProjectChanges(rccCustomBuilds.ToList(), QtRcc.ItemTypeName);  | 
|             FinalizeProjectChanges(repcCustomBuilds.ToList(), QtRepc.ItemTypeName);  | 
|             FinalizeProjectChanges(uicCustomBuilds.ToList(), QtUic.ItemTypeName);  | 
|   | 
|             this[Files.Project].isDirty = this[Files.Filters].isDirty = true;  | 
|             Commit();  | 
|             return true;  | 
|         }  | 
|   | 
|         bool TryReplaceTextInPlace(ref string text, Regex findWhat, string newText)  | 
|         {  | 
|             var match = findWhat.Match(text);  | 
|             if (!match.Success)  | 
|                 return false;  | 
|             do {  | 
|                 text = text.Remove(match.Index, match.Length).Insert(match.Index, newText);  | 
|                 match = findWhat.Match(text, match.Index);  | 
|             } while (match.Success);  | 
|   | 
|             return true;  | 
|         }  | 
|   | 
|         void ReplaceText(XElement xElem, Regex findWhat, string newText)  | 
|         {  | 
|             var elemValue = (string)xElem;  | 
|             if (!string.IsNullOrEmpty(elemValue)  | 
|                 && TryReplaceTextInPlace(ref elemValue, findWhat, newText)) {  | 
|                 xElem.Value = elemValue;  | 
|             }  | 
|         }  | 
|   | 
|         void ReplaceText(XAttribute xAttr, Regex findWhat, string newText)  | 
|         {  | 
|             var attrValue = (string)xAttr;  | 
|             if (!string.IsNullOrEmpty(attrValue)  | 
|                 && TryReplaceTextInPlace(ref attrValue, findWhat, newText)) {  | 
|                 xAttr.Value = attrValue;  | 
|             }  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// All path separators  | 
|         /// </summary>  | 
|         static readonly char[] slashChars = new[]  | 
|         {  | 
|             Path.DirectorySeparatorChar,  | 
|             Path.AltDirectorySeparatorChar  | 
|         };  | 
|   | 
|         /// <summary>  | 
|         /// Pattern that matches one path separator char  | 
|         /// </summary>  | 
|         static readonly RegExpr slash = CharSet[slashChars];  | 
|   | 
|         /// <summary>  | 
|         /// Gets a RegExpr that matches a given path, regardless  | 
|         /// of case and varying directory separators  | 
|         /// </summary>  | 
|         static RegExpr GetPathPattern(string findWhatPath)  | 
|         {  | 
|             return  | 
|                 // Make pattern case-insensitive  | 
|                 IgnoreCase &  | 
|                 // Split path string by directory separators  | 
|                 findWhatPath.Split(slashChars, StringSplitOptions.RemoveEmptyEntries)  | 
|                 // Convert path parts to RegExpr (escapes regex special chars)  | 
|                 .Select(dirName => (RegExpr)dirName)  | 
|                 // Join all parts, separated by path separator pattern  | 
|                 .Aggregate((path, dirName) => path & slash & dirName);  | 
|         }  | 
|   | 
|         public void ReplacePath(string oldPath, string newPath)  | 
|         {  | 
|             Uri srcUri = new Uri(Path.GetFullPath(oldPath));  | 
|             Uri projUri = new Uri(this[Files.Project].filePath);  | 
|   | 
|             RegExpr absolutePath = GetPathPattern(srcUri.AbsolutePath);  | 
|             RegExpr relativePath = GetPathPattern(projUri.MakeRelativeUri(srcUri).OriginalString);  | 
|   | 
|             Regex findWhat = (absolutePath | relativePath).Render().Regex;  | 
|   | 
|             foreach (var xElem in this[Files.Project].xml.Descendants()) {  | 
|                 if (!xElem.HasElements)  | 
|                     ReplaceText(xElem, findWhat, newPath);  | 
|                 foreach (var xAttr in xElem.Attributes())  | 
|                     ReplaceText(xAttr, findWhat, newPath);  | 
|             }  | 
|             this[Files.Project].isDirty = true;  | 
|             Commit();  | 
|         }  | 
|   | 
|         class MSBuildEvaluator : IVSMacroExpander, IDisposable  | 
|         {  | 
|             private readonly MsBuildXmlFile projFile;  | 
|             private string tempProjFilePath;  | 
|             private XElement evaluateTarget;  | 
|             private XElement evaluateProperty;  | 
|             private ProjectRootElement projRoot;  | 
|             private readonly Dictionary<string, string> expansionCache;  | 
|   | 
|             public Dictionary<string, string> Properties  | 
|             {  | 
|                 get;  | 
|             }  | 
|   | 
|             public MSBuildEvaluator(MsBuildXmlFile projFile)  | 
|             {  | 
|                 this.projFile = projFile;  | 
|                 tempProjFilePath = string.Empty;  | 
|                 evaluateTarget = evaluateProperty = null;  | 
|                 expansionCache = new Dictionary<string, string>();  | 
|                 Properties = new Dictionary<string, string>();  | 
|             }  | 
|   | 
|             public void Dispose()  | 
|             {  | 
|                 if (evaluateTarget != null) {  | 
|                     evaluateTarget.Remove();  | 
|                     if (File.Exists(tempProjFilePath))  | 
|                         File.Delete(tempProjFilePath);  | 
|                 }  | 
|             }  | 
|   | 
|             string ExpansionCacheKey(string stringToExpand)  | 
|             {  | 
|                 var key = new StringBuilder();  | 
|                 foreach (var property in Properties)  | 
|                     key.AppendFormat("{0};{1};", property.Key, property.Value);  | 
|                 key.Append(stringToExpand);  | 
|                 return key.ToString();  | 
|             }  | 
|   | 
|             bool TryExpansionCache(string stringToExpand, out string expandedString)  | 
|             {  | 
|                 return expansionCache.TryGetValue(  | 
|                     ExpansionCacheKey(stringToExpand), out expandedString);  | 
|             }  | 
|   | 
|             void AddToExpansionCache(string stringToExpand, string expandedString)  | 
|             {  | 
|                 expansionCache[ExpansionCacheKey(stringToExpand)] = expandedString;  | 
|             }  | 
|   | 
|             public string ExpandString(string stringToExpand)  | 
|             {  | 
|                 if (TryExpansionCache(stringToExpand, out string expandedString))  | 
|                     return expandedString;  | 
|   | 
|                 if (evaluateTarget == null) {  | 
|                     projFile.xmlCommitted.Root.Add(evaluateTarget = new XElement(ns + "Target",  | 
|                         new XAttribute("Name", "MSBuildEvaluatorTarget"),  | 
|                         new XElement(ns + "PropertyGroup",  | 
|                             evaluateProperty = new XElement(ns + "MSBuildEvaluatorProperty"))));  | 
|                 }  | 
|                 if (stringToExpand != (string)evaluateProperty) {  | 
|                     evaluateProperty.SetValue(stringToExpand);  | 
|                     if (!string.IsNullOrEmpty(tempProjFilePath) && File.Exists(tempProjFilePath))  | 
|                         File.Delete(tempProjFilePath);  | 
|                     tempProjFilePath = Path.Combine(  | 
|                         Path.GetDirectoryName(projFile.filePath),  | 
|                         Path.GetRandomFileName());  | 
|                     if (File.Exists(tempProjFilePath))  | 
|                         File.Delete(tempProjFilePath);  | 
|                     projFile.xmlCommitted.Save(tempProjFilePath);  | 
|                     projRoot = ProjectRootElement.Open(tempProjFilePath);  | 
|                 }  | 
|                 var projInst = new ProjectInstance(projRoot, Properties,  | 
|                     null, new ProjectCollection());  | 
|                 var buildRequest = new BuildRequestData(  | 
|                     projInst, new string[] { "MSBuildEvaluatorTarget" },  | 
|                     null, BuildRequestDataFlags.ProvideProjectStateAfterBuild);  | 
|                 var buildResult = BuildManager.DefaultBuildManager.Build(  | 
|                     new BuildParameters(), buildRequest);  | 
|                 expandedString = buildResult.ProjectStateAfterBuild  | 
|                     .GetPropertyValue("MSBuildEvaluatorProperty");  | 
|   | 
|                 AddToExpansionCache(stringToExpand, expandedString);  | 
|                 return expandedString;  | 
|             }  | 
|         }  | 
|   | 
|         class CustomBuildEval  | 
|         {  | 
|             public string ProjectConfig { get; set; }  | 
|             public string Identity { get; set; }  | 
|             public string AdditionalInputs { get; set; }  | 
|             public string Outputs { get; set; }  | 
|             public string Message { get; set; }  | 
|             public string Command { get; set; }  | 
|         }  | 
|   | 
|         List<CustomBuildEval> EvaluateCustomBuild()  | 
|         {  | 
|             var eval = new List<CustomBuildEval>();  | 
|   | 
|             var pattern = new Regex(@"{([^}]+)}{([^}]+)}{([^}]+)}{([^}]+)}{([^}]+)}");  | 
|   | 
|             var projConfigs = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + "ProjectConfiguration");  | 
|   | 
|             using (var evaluator = new MSBuildEvaluator(this[Files.Project])) {  | 
|   | 
|                 foreach (var projConfig in projConfigs) {  | 
|   | 
|                     evaluator.Properties.Clear();  | 
|                     foreach (var configProp in projConfig.Elements())  | 
|                         evaluator.Properties.Add(configProp.Name.LocalName, (string)configProp);  | 
|   | 
|                     var expandedValue = evaluator.ExpandString(  | 
|                         "@(CustomBuild->'" +  | 
|                             "{%(Identity)}" +  | 
|                             "{%(AdditionalInputs)}" +  | 
|                             "{%(Outputs)}" +  | 
|                             "{%(Message)}" +  | 
|                             "{%(Command)}')");  | 
|   | 
|                     foreach (Match cbEval in pattern.Matches(expandedValue)) {  | 
|                         eval.Add(new CustomBuildEval  | 
|                         {  | 
|                             ProjectConfig = (string)projConfig.Attribute("Include"),  | 
|                             Identity = cbEval.Groups[1].Value,  | 
|                             AdditionalInputs = cbEval.Groups[2].Value,  | 
|                             Outputs = cbEval.Groups[3].Value,  | 
|                             Message = cbEval.Groups[4].Value,  | 
|                             Command = cbEval.Groups[5].Value,  | 
|                         });  | 
|                     }  | 
|                 }  | 
|             }  | 
|   | 
|             return eval;  | 
|         }  | 
|   | 
|         public bool BuildTarget(string target)  | 
|         {  | 
|             if (this[Files.Project].isDirty)  | 
|                 return false;  | 
|   | 
|             var configurations = this[Files.Project].xml  | 
|                 .Elements(ns + "Project")  | 
|                 .Elements(ns + "ItemGroup")  | 
|                 .Elements(ns + "ProjectConfiguration");  | 
|   | 
|             using (var buildManager = new BuildManager()) {  | 
|   | 
|                 foreach (var config in configurations) {  | 
|   | 
|                     var configProps = config.Elements()  | 
|                         .ToDictionary(x => x.Name.LocalName, x => x.Value);  | 
|   | 
|                     var projectInstance = new ProjectInstance(this[Files.Project].filePath,  | 
|                         new Dictionary<string, string>(configProps)  | 
|                         { { "QtVSToolsBuild", "true" } },  | 
|                         null, new ProjectCollection());  | 
|   | 
|                     var buildRequest = new BuildRequestData(projectInstance,  | 
|                         targetsToBuild: new[] { target },  | 
|                         hostServices: null,  | 
|                         flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);  | 
|   | 
|                     var result = buildManager.Build(new BuildParameters(), buildRequest);  | 
|                     if (result.OverallResult != BuildResultCode.Success)  | 
|                         return false;  | 
|   | 
|                 }  | 
|             }  | 
|             return true;  | 
|         }  | 
|   | 
|         static readonly Regex ConditionParser =  | 
|             new Regex(@"\'\$\(Configuration[^\)]*\)\|\$\(Platform[^\)]*\)\'\=\=\'([^\']+)\'");  | 
|   | 
|         class MsBuildConverterProvider : IPropertyStorageProvider  | 
|         {  | 
|             public string GetProperty(object propertyStorage, string itemType, string propertyName)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return "";  | 
|                 XElement item;  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") {  | 
|                     item = xmlPropertyStorage.Element(ns + itemType);  | 
|                     if (item == null)  | 
|                         return "";  | 
|                 } else {  | 
|                     item = xmlPropertyStorage;  | 
|                 }  | 
|                 var prop = item.Element(ns + propertyName);  | 
|                 if (prop == null)  | 
|                     return "";  | 
|                 return prop.Value;  | 
|             }  | 
|   | 
|             public bool SetProperty(  | 
|                 object propertyStorage,  | 
|                 string itemType,  | 
|                 string propertyName,  | 
|                 string propertyValue)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return false;  | 
|                 XElement item;  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") {  | 
|                     item = xmlPropertyStorage.Element(ns + itemType);  | 
|                     if (item == null)  | 
|                         xmlPropertyStorage.Add(item = new XElement(ns + itemType));  | 
|                 } else {  | 
|                     item = xmlPropertyStorage;  | 
|                 }  | 
|                 var prop = item.Element(ns + propertyName);  | 
|                 if (prop != null)  | 
|                     prop.Value = propertyValue;  | 
|                 else  | 
|                     item.Add(new XElement(ns + propertyName, propertyValue));  | 
|                 return true;  | 
|             }  | 
|   | 
|             public bool DeleteProperty(  | 
|                 object propertyStorage,  | 
|                 string itemType,  | 
|                 string propertyName)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return false;  | 
|                 XElement item;  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") {  | 
|                     item = xmlPropertyStorage.Element(ns + itemType);  | 
|                     if (item == null)  | 
|                         return true;  | 
|                 } else {  | 
|                     item = xmlPropertyStorage;  | 
|                 }  | 
|   | 
|                 var prop = item.Element(ns + propertyName);  | 
|                 if (prop != null)  | 
|                     prop.Remove();  | 
|                 return true;  | 
|             }  | 
|   | 
|             public string GetConfigName(object propertyStorage)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return "";  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") {  | 
|                     var configName = ConditionParser  | 
|                         .Match(xmlPropertyStorage.Attribute("Condition").Value);  | 
|                     if (!configName.Success || configName.Groups.Count <= 1)  | 
|                         return "";  | 
|                     return configName.Groups[1].Value;  | 
|                 }  | 
|                 return xmlPropertyStorage.Attribute("ConfigName").Value;  | 
|             }  | 
|   | 
|             public string GetItemType(object propertyStorage)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return "";  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup")  | 
|                     return "";  | 
|                 return xmlPropertyStorage.Name.LocalName;  | 
|             }  | 
|   | 
|             public string GetItemName(object propertyStorage)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return "";  | 
|                 if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup")  | 
|                     return "";  | 
|                 return xmlPropertyStorage.Attribute("Include").Value;  | 
|             }  | 
|   | 
|             public object GetParentProject(object propertyStorage)  | 
|             {  | 
|                 XElement xmlPropertyStorage = propertyStorage as XElement;  | 
|                 if (xmlPropertyStorage == null)  | 
|                     return "";  | 
|                 if (xmlPropertyStorage.Document == null)  | 
|                     return null;  | 
|                 return xmlPropertyStorage.Document.Root;  | 
|             }  | 
|   | 
|             public object GetProjectConfiguration(object project, string configName)  | 
|             {  | 
|                 XElement xmlProject = project as XElement;  | 
|                 if (xmlProject == null)  | 
|                     return null;  | 
|                 return xmlProject.Elements(ns + "ItemDefinitionGroup")  | 
|                     .Where(config => config.Attribute("Condition").Value.Contains(configName))  | 
|                     .FirstOrDefault();  | 
|             }  | 
|   | 
|             public IEnumerable<object> GetItems(  | 
|                 object project,  | 
|                 string itemType,  | 
|                 string configName = "")  | 
|             {  | 
|                 XElement xmlProject = project as XElement;  | 
|                 if (xmlProject == null)  | 
|                     return new List<object>();  | 
|                 return  | 
|                     xmlProject.Elements(ns + "ItemGroup")  | 
|                     .Elements(ns + "CustomBuild")  | 
|                     .Elements(ns + itemType)  | 
|                     .Where(item => (  | 
|                         configName == ""  | 
|                         || item.Attribute("ConfigName").Value == configName))  | 
|                     .GroupBy(item => item.Attribute("Include").Value)  | 
|                     .Select(item => item.First());  | 
|             }  | 
|         }  | 
|   | 
|     }  | 
| }  |