| | |
| | | /****************************************************************************
|
| | | **
|
| | | ** Copyright (C) 2017 The Qt Company Ltd.
|
| | | ** Copyright (C) 2022 The Qt Company Ltd.
|
| | | ** Contact: https://www.qt.io/licensing/
|
| | | **
|
| | | ** This file is part of the Qt VS Tools.
|
| | |
| | | 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 QtVsTools.Core.QtMsBuild;
|
| | | using System.Text.RegularExpressions;
|
| | | using Microsoft.Build.Construction;
|
| | | using Microsoft.Build.Execution;
|
| | | using Microsoft.Build.Evaluation;
|
| | | using QtVsTools.VisualStudio;
|
| | | using QtVsTools.SyntaxAnalysis;
|
| | | using EnvDTE;
|
| | | using Microsoft.VisualStudio.Shell;
|
| | |
|
| | | namespace QtVsTools.Core
|
| | | {
|
| | | using QtMsBuild;
|
| | | using SyntaxAnalysis;
|
| | |
|
| | | using static HelperFunctions;
|
| | | using static RegExpr;
|
| | | using static SyntaxAnalysis.RegExpr;
|
| | |
|
| | | public class MsBuildProject
|
| | | {
|
| | |
| | | User,
|
| | | Count
|
| | | }
|
| | | MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count];
|
| | |
|
| | | readonly MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count];
|
| | |
|
| | | MsBuildProject()
|
| | | {
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | private static XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
|
| | | private static readonly XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
|
| | |
|
| | | public static MsBuildProject Load(string pathToProject)
|
| | | {
|
| | |
| | | if (ConfigCondition == null)
|
| | | return false;
|
| | |
|
| | | // Get default Qt dir
|
| | | string defaultQtDir = null;
|
| | | var defaultVersionName = QtVersionManager.The().GetDefaultVersion();
|
| | | var defaultVersion = QtVersionManager.The().GetVersionInfo(defaultVersionName);
|
| | | if (defaultVersion != null)
|
| | | defaultQtDir = defaultVersion.qtDir;
|
| | |
|
| | | // Get project configurations
|
| | | var configs = this[Files.Project].xml
|
| | |
| | | .FirstOrDefault();
|
| | | if (globals == null)
|
| | | return false;
|
| | |
|
| | | // Get project configuration properties
|
| | | var configProps = this[Files.Project].xml
|
| | | .Elements(ns + "Project")
|
| | | .Elements(ns + "PropertyGroup")
|
| | | .Where(pg =>
|
| | | (string)pg.Attribute("Label") == "Configuration"
|
| | | && pg.Attribute("Condition") != null)
|
| | | .ToDictionary(pg => (string)pg.Attribute("Condition"));
|
| | |
|
| | | // Set Qt project format version
|
| | | var projKeyword = globals
|
| | |
| | | foreach (var pg in uncategorizedPropertyGroups) {
|
| | | foreach (var p in pg.Elements().ToList()) {
|
| | | var condition = p.Attribute("Condition") ?? pg.Attribute("Condition");
|
| | | XElement configPropertyGroup = null;
|
| | | if (condition != null)
|
| | | propertyGroups.TryGetValue((string)condition, out configPropertyGroup);
|
| | | if (configPropertyGroup != null) {
|
| | | if (condition != null && propertyGroups
|
| | | .TryGetValue((string)condition, out XElement configPropertyGroup)) {
|
| | | p.Remove();
|
| | | p.SetAttributeValue("Condition", null);
|
| | | configPropertyGroup.Add(p);
|
| | |
| | | foreach (var configQtSettings in qtSettings) {
|
| | | var configCondition = (string)configQtSettings.Attribute("Condition");
|
| | |
|
| | | XElement oldConfigQtInstall;
|
| | | if (oldQtInstall.TryGetValue(configCondition, out oldConfigQtInstall))
|
| | | if (oldQtInstall.TryGetValue(configCondition, out XElement oldConfigQtInstall))
|
| | | configQtSettings.Add(oldConfigQtInstall);
|
| | |
|
| | | XElement oldConfigQtSettings;
|
| | | if (oldQtSettings.TryGetValue(configCondition, out oldConfigQtSettings)) {
|
| | | if (oldQtSettings.TryGetValue(configCondition, out XElement oldConfigQtSettings)) {
|
| | | foreach (var qtSetting in oldConfigQtSettings.Elements())
|
| | | configQtSettings.Add(qtSetting);
|
| | | }
|
| | |
| | | .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>();
|
| | |
|
| | |
| | | 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()) {
|
| | | foreach (var module in QtModules.Instance.GetAvailableModules(defaultVersion.qtMajor)) {
|
| | |
|
| | | if (IsModuleUsed(module, compiler, linker)) {
|
| | | if (IsModuleUsed(module, compiler, linker, resourceCompiler)) {
|
| | |
|
| | | // Qt module names, to copy to QtModules property
|
| | | if (!string.IsNullOrEmpty(module.proVarQT))
|
| | |
| | | .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
|
| | |
| | | bool IsModuleUsed(
|
| | | QtModule module,
|
| | | IEnumerable<XElement> compiler,
|
| | | IEnumerable<XElement> linker)
|
| | | IEnumerable<XElement> linker,
|
| | | IEnumerable<XElement> resourceCompiler)
|
| | | {
|
| | | // Module .lib is present in linker additional dependencies
|
| | | if (linker.Elements(ns + "AdditionalDependencies")
|
| | |
| | | return true;
|
| | | }
|
| | |
|
| | | // Module macro is present in pre-processor definitions
|
| | | // 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;
|
| | |
| | | commandLine = replace(row.itemName, commandLine);
|
| | | //
|
| | | // * Configuration/platform, e.g. x64\Debug --> $(Platform)\$(Configuration)
|
| | | commandLine = commandLine
|
| | | .Replace(configName, "$(Configuration)",
|
| | | StringComparison.InvariantCultureIgnoreCase)
|
| | | // * 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.Add(configProp.Name.LocalName, (string)configProp);
|
| | | if (!qtMsBuild.SetCommandLine(itemType, item, commandLine, evaluator)) {
|
| | | int lineNumber = 1;
|
| | | var errorLine = row.command as IXmlLineInfo;
|
| | | if (errorLine != null && errorLine.HasLineInfo())
|
| | | if (row.command is IXmlLineInfo errorLine && errorLine.HasLineInfo())
|
| | | lineNumber = errorLine.LineNumber;
|
| | |
|
| | | Messages.Print(string.Format(
|
| | |
| | | return (string)cbt.Attribute("Include");
|
| | | }
|
| | | }
|
| | | string ouputFile;
|
| | | if (!properties.TryGetValue(QtMoc.Property.InputFile, out ouputFile))
|
| | | if (!properties.TryGetValue(QtMoc.Property.InputFile, out string ouputFile))
|
| | | return (string)cbt.Attribute("Include");
|
| | | return ouputFile;
|
| | | }
|
| | |
| | | Path.IsPathRooted(x) ? x : Path.Combine(projDir, x)));
|
| | | var outputItems = new List<XElement>();
|
| | | foreach (var outputFile in outputFiles) {
|
| | | List<XElement> mocOutput = null;
|
| | | if (projItemsByPath.TryGetValue(outputFile, out mocOutput)) {
|
| | | if (projItemsByPath.TryGetValue(outputFile, out List<XElement> mocOutput)) {
|
| | | outputItems.AddRange(mocOutput);
|
| | | hasGeneratedFiles |= hasGeneratedFiles ? true : mocOutput
|
| | | .Where(x => !x.Elements(ns + "ExcludedFromBuild")
|
| | |
| | |
|
| | | class MSBuildEvaluator : IVSMacroExpander, IDisposable
|
| | | {
|
| | | MsBuildXmlFile projFile;
|
| | | string tempProjFilePath;
|
| | | XElement evaluateTarget;
|
| | | XElement evaluateProperty;
|
| | | ProjectRootElement projRoot;
|
| | | public Dictionary<string, string> expansionCache;
|
| | | 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;
|
| | | private set;
|
| | | }
|
| | |
|
| | | public MSBuildEvaluator(MsBuildXmlFile projFile)
|
| | |
| | |
|
| | | public string ExpandString(string stringToExpand)
|
| | | {
|
| | | string expandedString;
|
| | | if (TryExpansionCache(stringToExpand, out expandedString))
|
| | | if (TryExpansionCache(stringToExpand, out string expandedString))
|
| | | return expandedString;
|
| | |
|
| | | if (evaluateTarget == null) {
|
| | |
| | | return true;
|
| | | }
|
| | |
|
| | | static Regex ConditionParser =
|
| | | static readonly Regex ConditionParser =
|
| | | new Regex(@"\'\$\(Configuration[^\)]*\)\|\$\(Platform[^\)]*\)\'\=\=\'([^\']+)\'");
|
| | |
|
| | | class MsBuildConverterProvider : IPropertyStorageProvider
|