aboutsummaryrefslogtreecommitdiffstats
path: root/QtVsTools.Core/QtProject.cs
diff options
context:
space:
mode:
Diffstat (limited to 'QtVsTools.Core/QtProject.cs')
-rw-r--r--QtVsTools.Core/QtProject.cs529
1 files changed, 1 insertions, 528 deletions
diff --git a/QtVsTools.Core/QtProject.cs b/QtVsTools.Core/QtProject.cs
index b8425845..76c0f0b6 100644
--- a/QtVsTools.Core/QtProject.cs
+++ b/QtVsTools.Core/QtProject.cs
@@ -30,12 +30,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
+using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.VCProjectEngine;
-using EnvDTE;
namespace QtVsTools.Core
{
@@ -51,7 +50,6 @@ namespace QtVsTools.Core
private DTE dte;
private Project envPro;
private VCProject vcPro;
- private MocCmdChecker mocCmdChecker;
private static readonly Dictionary<Project, QtProject> instances = new Dictionary<Project, QtProject>();
private readonly QtMsBuildContainer qtMsBuild;
@@ -164,30 +162,6 @@ namespace QtVsTools.Core
return null;
}
- /// <summary>
- /// Returns the file name of the generated moc file relative to the
- /// project directory.
- /// </summary>
- /// The directory of the moc file depends on the file configuration.
- /// Every appearance of "$(ConfigurationName)" in the path will be
- /// replaced by the value of configName.
- /// <param name="file">full file name of either the header or the source file</param>
- /// <returns></returns>
- private string GetRelativeMocFilePath(string file, string configName = null,
- string platformName = null)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- var fileName = GetMocFileName(file);
- if (fileName == null)
- return null;
- var mocDir = QtVSIPSettings.GetMocDirectory(envPro, configName, platformName, file)
- + Path.DirectorySeparatorChar + fileName;
- if (HelperFunctions.IsAbsoluteFilePath(mocDir))
- mocDir = HelperFunctions.GetRelativePath(vcPro.ProjectDirectory, mocDir);
- return mocDir;
- }
-
public static int GetFormatVersion(VCProject vcPro)
{
ThreadHelper.ThrowIfNotOnUIThread();
@@ -296,175 +270,6 @@ namespace QtVsTools.Core
}
/// <summary>
- /// Surrounds the argument by double quotes.
- /// Makes sure, that the trailing double quote is not escaped by a backslash.
- /// Escapes all quotation mark characters in the argument
- ///
- /// This must follow the format recognized by CommandLineToArgvW:
- /// (https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw)
- ///
- /// CommandLineToArgvW has a special interpretation of backslash characters when they are
- /// followed by a quotation mark character ("). This interpretation assumes that any
- /// preceding argument is a valid file system path, or else it may behave unpredictably.
- ///
- /// This special interpretation controls the "in quotes" mode tracked by the parser. When
- /// this mode is off, whitespace terminates the current argument. When on, whitespace is
- /// added to the argument like all other characters.
- ///
- /// * 2n backslashes followed by a quotation mark produce n backslashes followed by
- /// begin/end quote. This does not become part of the parsed argument, but toggles the
- /// "in quotes" mode.
- ///
- /// * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes
- /// followed by a quotation mark literal ("). This does not toggle the "in quotes" mode.
- ///
- /// * n backslashes not followed by a quotation mark simply produce n backslashes.
- ///
- /// </summary>
- private static string SafelyQuoteCommandLineArgument(string arg)
- {
- var quotedArg = new StringBuilder();
- quotedArg.Append("\"");
-
- // Split argument by quotation mark characters
- // All argument parts except the last are followed by a quotation mark character
- var argParts = arg.Split(new char[] { '\"' });
- for (int i = 0; i < argParts.Length; ++i) {
- var part = argParts[i];
- quotedArg.Append(part);
-
- // Duplicate backslashes immediately preceding quotation mark character
- if (part.EndsWith("\\")) {
- quotedArg.Append(part.Reverse().TakeWhile(c => c == Path.DirectorySeparatorChar)
- .ToArray());
- }
- // Escape all quotation mark characters in argument
- if (i < argParts.Length - 1)
- quotedArg.Append("\\\"");
- }
-
- quotedArg.Append("\"");
- return quotedArg.ToString();
- }
-
- public string GetDefines(VCFileConfiguration conf)
- {
- var defines = string.Empty;
- if (conf.Tool is IVCRulePropertyStorage propsFile) {
- try {
- defines = propsFile.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
- } catch { }
- }
-
- var projectConfig = conf.ProjectConfiguration as VCConfiguration;
- if (string.IsNullOrEmpty(defines)
- && projectConfig?.Rules.Item("CL") is IVCRulePropertyStorage propsProject) {
- try {
- defines = propsProject.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
- } catch { }
- }
-
- if (string.IsNullOrEmpty(defines))
- return string.Empty;
-
- var defineList = defines.Split(
- new char[] { ';' },
- StringSplitOptions.RemoveEmptyEntries)
- .ToList();
-
- var preprocessorDefines = string.Empty;
- var alreadyAdded = new List<string>();
- var rxp = new Regex(@"\s|(\$\()");
- foreach (var define in defineList) {
- if (!alreadyAdded.Contains(define)) {
- var mustSurroundByDoubleQuotes = rxp.IsMatch(define);
- // Yes, a preprocessor definition can contain spaces or a macro name.
- // Example: PROJECTDIR=$(InputDir)
-
- if (mustSurroundByDoubleQuotes) {
- preprocessorDefines += " ";
- preprocessorDefines += SafelyQuoteCommandLineArgument("-D" + define);
- } else {
- preprocessorDefines += " -D" + define;
- }
- alreadyAdded.Add(define);
- }
- }
- return preprocessorDefines;
- }
-
- private string GetIncludes(VCFileConfiguration conf)
- {
- var includeList = GetIncludesFromCompilerTool(CompilerToolWrapper.Create(conf));
-
- var projectConfig = conf.ProjectConfiguration as VCConfiguration;
- includeList.AddRange(GetIncludesFromCompilerTool(CompilerToolWrapper.Create(projectConfig)));
-
- if (projectConfig.PropertySheets is IVCCollection propertySheets) {
- foreach (VCPropertySheet sheet in propertySheets)
- includeList.AddRange(GetIncludesFromPropertySheet(sheet));
- }
-
- var includes = string.Empty;
- var alreadyAdded = new List<string>();
- foreach (var include in includeList) {
- if (!alreadyAdded.Contains(include)) {
- var incl = HelperFunctions.NormalizeRelativeFilePath(include);
- if (incl.Length > 0)
- includes += " " + SafelyQuoteCommandLineArgument("-I" + incl);
- alreadyAdded.Add(include);
- }
- }
- return includes;
- }
-
- private List<string> GetIncludesFromPropertySheet(VCPropertySheet sheet)
- {
- var includeList = GetIncludesFromCompilerTool(CompilerToolWrapper.Create(sheet));
- if (sheet.PropertySheets is IVCCollection propertySheets) {
- foreach (VCPropertySheet subSheet in propertySheets)
- includeList.AddRange(GetIncludesFromPropertySheet(subSheet));
- }
- return includeList;
- }
-
- private static List<string> GetIncludesFromCompilerTool(CompilerToolWrapper compiler)
- {
- try {
- if (!string.IsNullOrEmpty(compiler.GetAdditionalIncludeDirectories())) {
- var includes = compiler.GetAdditionalIncludeDirectoriesList();
- return new List<string>(includes);
- }
- } catch { }
- return new List<string>();
- }
-
- private string GetPCHMocOptions(VCFile file, CompilerToolWrapper compiler)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- // As .moc files are included, we should not add anything there
- if (!HelperFunctions.IsHeaderFile(file.Name))
- return string.Empty;
-
- var additionalMocOptions = "\"-f" + HelperFunctions.FromNativeSeparators(compiler
- .GetPrecompiledHeaderThrough()) + "\" ";
- //Get mocDir without .\\ at the beginning of it
- var mocDir = QtVSIPSettings.GetMocDirectory(envPro);
- if (mocDir.StartsWith(".\\", StringComparison.Ordinal))
- mocDir = mocDir.Substring(2);
-
- //Get the absolute path
- mocDir = vcPro.ProjectDirectory + mocDir;
- var fullPathGeneric = Path.Combine(
- Path.GetDirectoryName(file.FullPath), "%(Filename)%(Extension)");
- var relPathToFile = HelperFunctions.FromNativeSeparators(HelperFunctions
- .GetRelativePath(mocDir, fullPathGeneric));
- additionalMocOptions += "\"-f" + relPathToFile + "\"";
- return additionalMocOptions;
- }
-
- /// <summary>
/// Adds a moc step to a given file for this project.
/// </summary>
/// <param name="file">file</param>
@@ -1092,338 +897,6 @@ namespace QtVsTools.Core
}
}
- private static VCFileConfiguration GetVCFileConfigurationByName(VCFile file, string configName)
- {
- foreach (VCFileConfiguration cfg in (IVCCollection)file.FileConfigurations) {
- if (cfg.Name == configName)
- return cfg;
- }
- return null;
- }
-
- /// <summary>
- /// Searches for the generated file inside the "Generated Files" filter.
- /// The function looks for the given filename and uses the fileConfig's
- /// ConfigurationName and Platform if moc directory contains $(ConfigurationName)
- /// and/or $(PlatformName).
- /// Otherwise it just uses the "Generated Files" filter
- /// </summary>
- /// <param name="fileName"></param>
- /// <param name="fileConfig"></param>
- /// <returns></returns>
- private VCFile GetGeneratedMocFile(string fileName, VCFileConfiguration fileConfig)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- if (QtVSIPSettings.HasDifferentMocFilePerConfig(envPro)
- || QtVSIPSettings.HasDifferentMocFilePerPlatform(envPro)) {
- var projectConfig = (VCConfiguration)fileConfig.ProjectConfiguration;
- var configName = projectConfig.ConfigurationName;
- var platformName = ((VCPlatform)projectConfig.Platform).Name;
- var generatedFiles = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier);
- if (generatedFiles == null)
- return null;
- foreach (VCFilter filt in (IVCCollection)generatedFiles.Filters) {
- if (filt.Name == configName + "_" + platformName ||
- filt.Name == configName || filt.Name == platformName) {
- foreach (VCFile filtFile in (IVCCollection)filt.Files) {
- if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, fileName))
- return filtFile;
- }
- }
- }
-
- //If a project from the an AddIn prior to 1.1.0 was loaded, the generated files are located directly
- //in the generated files filter.
- var relativeMocPath = QtVSIPSettings.GetMocDirectory(
- envPro,
- configName,
- platformName,
- fileConfig.File as VCFile)
- + Path.DirectorySeparatorChar + fileName;
- //Remove .\ at the beginning of the mocPath
- if (relativeMocPath.StartsWith(".\\", StringComparison.Ordinal))
- relativeMocPath = relativeMocPath.Remove(0, 2);
- foreach (VCFile filtFile in (IVCCollection)generatedFiles.Files) {
- if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, relativeMocPath))
- return filtFile;
- }
- } else {
- var generatedFiles = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier);
- foreach (VCFile filtFile in (IVCCollection)generatedFiles.Files) {
- if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, fileName))
- return filtFile;
- }
- }
- return null;
- }
-
- public void RefreshQtMocIncludePath()
- {
- foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
- var propsClCompile = config.Rules.Item("CL") as IVCRulePropertyStorage;
- var ruleName = GetRuleName(config, QtMoc.ItemTypeName);
- var propsQtMoc = config.Rules.Item(ruleName) as IVCRulePropertyStorage;
- if (propsClCompile == null || propsQtMoc == null)
- continue;
- propsQtMoc.SetPropertyValue(QtMoc.Property.IncludePath.ToString(),
- propsClCompile.GetUnevaluatedPropertyValue("AdditionalIncludeDirectories"));
- }
- }
-
- public void RefreshQtMocDefine()
- {
- foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {
- var propsClCompile = config.Rules.Item("CL") as IVCRulePropertyStorage;
- var ruleName = GetRuleName(config, QtMoc.ItemTypeName);
- var propsQtMoc = config.Rules.Item(ruleName) as IVCRulePropertyStorage;
- if (propsClCompile == null || propsQtMoc == null)
- continue;
- propsQtMoc.SetPropertyValue(QtMoc.Property.Define.ToString(),
- propsClCompile.GetUnevaluatedPropertyValue("PreprocessorDefinitions"));
- }
- }
-
- public void RefreshMocSteps()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- // Ignore when using shared compiler properties
- if (GetFormatVersion(vcPro) < Resources.qtMinFormatVersion_ClProperties) {
- // TODO: It would be nice if we can inform the user he's on an old project.
- //if (QtVsToolsPackage.Instance.Options.UpdateProjectFormat)
- // Notifications.UpdateProjectFormat.Show();
- }
- }
-
- public void RefreshMocStep(VCFile vcfile)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- RefreshMocStep(vcfile, true);
- }
-
- /// <summary>
- /// Updates the moc command line for the given header or source file
- /// containing the Q_OBJECT macro.
- /// If the function is called from a property change for a single file
- /// (singleFile = true) we may have to look for the according header
- /// file and refresh the moc step for this file, if it contains Q_OBJECT.
- /// </summary>
- /// <param name="vcfile"></param>
- private void RefreshMocStep(VCFile vcfile, bool singleFile)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- var isHeaderFile = HelperFunctions.IsHeaderFile(vcfile.FullPath);
- if (!isHeaderFile && !HelperFunctions.IsSourceFile(vcfile.FullPath))
- return;
-
- if (mocCmdChecker == null)
- mocCmdChecker = new MocCmdChecker();
-
- foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) {
- try {
- string commandLine = "";
- VCCustomBuildTool tool = null;
- VCFile mocable = null;
- var customBuildConfig = config;
- if (isHeaderFile || vcfile.ItemType == QtMoc.ItemTypeName) {
- mocable = vcfile;
- if (vcfile.ItemType == "CustomBuild")
- tool = HelperFunctions.GetCustomBuildTool(config);
- } else {
- var mocFileName = GetMocFileName(vcfile.FullPath);
- var mocFile = GetGeneratedMocFile(mocFileName, config);
- if (mocFile == null)
- continue;
-
- var mocFileConfig = GetVCFileConfigurationByName(mocFile, config.Name);
- if (vcfile.ItemType == "CustomBuild")
- tool = HelperFunctions.GetCustomBuildTool(mocFileConfig);
- mocable = mocFile;
- // It is possible that the function was called from a source file's property change, it is possible that
- // we have to obtain the tool from the according header file
- if ((vcfile.ItemType != "CustomBuild" || tool == null) && singleFile) {
- var headerName = vcfile.FullPath.Remove(vcfile.FullPath.LastIndexOf('.')) + ".h";
- mocFileName = GetMocFileName(headerName);
- mocFile = GetGeneratedMocFile(mocFileName, config);
- if (mocFile != null) {
- mocable = GetFileFromProject(headerName);
- customBuildConfig = GetVCFileConfigurationByName(mocable, config.Name);
- if (mocable.ItemType == "CustomBuild")
- tool = HelperFunctions.GetCustomBuildTool(customBuildConfig);
- }
- }
- }
-
- if (mocable.ItemType == "CustomBuild") {
- if (tool != null)
- commandLine = tool.CommandLine;
- } else if (mocable.ItemType == QtMoc.ItemTypeName) {
- commandLine = qtMsBuild.GenerateQtMocCommandLine(customBuildConfig);
- } else {
- continue;
- }
-
- if ((mocable.ItemType == "CustomBuild" && tool == null)
- || commandLine.IndexOf(
- "moc.exe",
- StringComparison.OrdinalIgnoreCase) == -1)
- continue;
-
- VCFile srcMocFile, cppFile;
- if (vcfile.ItemType == QtMoc.ItemTypeName
- && HelperFunctions.IsSourceFile(vcfile.ItemName)) {
- srcMocFile = cppFile = vcfile;
- } else {
- srcMocFile = GetSourceFileForMocStep(mocable);
- cppFile = GetCppFileForMocStep(mocable);
- }
- if (srcMocFile == null)
- continue;
- var mocableIsCPP = (srcMocFile == cppFile);
-
- var cppItemType = (cppFile != null) ? cppFile.ItemType : "";
- if (cppFile != null && cppItemType != "ClCompile")
- cppFile.ItemType = "ClCompile";
-
- string pchParameters = null;
- VCFileConfiguration defineIncludeConfig = null;
- CompilerToolWrapper compiler = null;
- if (cppFile == null) {
- // No file specific defines/includes
- // but at least the project defines/includes are added
- defineIncludeConfig = config;
- compiler = CompilerToolWrapper.Create(config.ProjectConfiguration as VCConfiguration);
- } else {
- defineIncludeConfig = GetVCFileConfigurationByName(cppFile, config.Name);
- compiler = CompilerToolWrapper.Create(defineIncludeConfig);
- }
-
- if (compiler != null && compiler.GetUsePrecompiledHeader() != pchOption.pchNone)
- pchParameters = GetPCHMocOptions(srcMocFile, compiler);
-
- var outputFileName = QtVSIPSettings.GetMocDirectory(envPro)
- + Path.DirectorySeparatorChar;
- if (mocableIsCPP) {
- outputFileName += ProjectMacros.Name;
- outputFileName += ".moc";
- } else {
- outputFileName += "moc_";
- outputFileName += ProjectMacros.Name;
- outputFileName += ".cpp";
- }
-
- var newCmdLine = mocCmdChecker.NewCmdLine(commandLine,
- GetIncludes(defineIncludeConfig),
- GetDefines(defineIncludeConfig),
- QtVSIPSettings.GetMocOptions(envPro), srcMocFile.RelativePath,
- pchParameters,
- outputFileName);
-
- if (cppFile != null && cppItemType != "ClCompile")
- cppFile.ItemType = cppItemType;
-
- // The tool's command line automatically gets a trailing "\r\n".
- // We have to remove it to make the check below work.
- var origCommandLine = commandLine;
- if (origCommandLine.EndsWith("\r\n", StringComparison.Ordinal))
- origCommandLine = origCommandLine.Substring(0, origCommandLine.Length - 2);
-
- if (newCmdLine != null && newCmdLine != origCommandLine) {
- // We have to delete the old moc file in order to trigger custom build step.
- var configName = config.Name.Remove(config.Name.IndexOf('|'));
- var platformName = config.Name.Substring(config.Name.IndexOf('|') + 1);
- var projectPath = envPro.FullName.Remove(envPro.FullName.LastIndexOf(Path
- .DirectorySeparatorChar));
- var mocRelPath = GetRelativeMocFilePath(srcMocFile.FullPath, configName, platformName);
- var mocPath = Path.Combine(projectPath, mocRelPath);
- if (File.Exists(mocPath))
- File.Delete(mocPath);
- if (mocable.ItemType == "CustomBuild") {
- tool.CommandLine = newCmdLine;
- } else {
- qtMsBuild.SetQtMocCommandLine(
- customBuildConfig, newCmdLine, new VCMacroExpander(config));
- }
- }
- } catch {
- Messages.Print("ERROR: failed to refresh moc step for " + vcfile.ItemName);
- }
- }
- }
-
- public void OnExcludedFromBuildChanged(VCFile vcFile, VCFileConfiguration vcFileCfg)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- // Update the ExcludedFromBuild flags of the mocced file
- // according to the ExcludedFromBuild flag of the mocable source file.
- var moccedFileName = GetMocFileName(vcFile.Name);
- if (string.IsNullOrEmpty(moccedFileName))
- return;
-
- var moccedFile = GetGeneratedMocFile(moccedFileName, vcFileCfg);
-
- if (moccedFile != null) {
- VCFile cppFile = null;
- if (HelperFunctions.IsHeaderFile(vcFile.Name))
- cppFile = GetCppFileForMocStep(vcFile);
-
- var moccedFileConfig = GetVCFileConfigurationByName(moccedFile, vcFileCfg.Name);
- if (moccedFileConfig != null) {
- if (cppFile != null && IsMoccedFileIncluded(cppFile)) {
- if (!moccedFileConfig.ExcludedFromBuild)
- moccedFileConfig.ExcludedFromBuild = true;
- } else if (moccedFileConfig.ExcludedFromBuild != vcFileCfg.ExcludedFromBuild) {
- moccedFileConfig.ExcludedFromBuild = vcFileCfg.ExcludedFromBuild;
- }
- }
- }
- }
-
- /// <summary>
- /// Helper function for RefreshMocStep.
- /// </summary>
- /// <param name="file"></param>
- /// <returns></returns>
- private VCFile GetSourceFileForMocStep(VCFile file)
- {
- if (HelperFunctions.IsHeaderFile(file.Name))
- return file;
- var fileName = file.Name;
- if (HelperFunctions.IsMocFile(fileName)) {
- fileName = fileName.Substring(0, fileName.Length - 4) + ".cpp";
- if (fileName.Length > 0) {
- foreach (VCFile f in (IVCCollection)vcPro.Files) {
- if (f.FullPath.EndsWith(Path.DirectorySeparatorChar + fileName, StringComparison.OrdinalIgnoreCase))
- return f;
- }
- }
- }
- return null;
- }
-
- /// <summary>
- /// Helper function for Refresh/UpdateMocStep.
- /// </summary>
- /// <param name="file"></param>
- /// <returns></returns>
- private VCFile GetCppFileForMocStep(VCFile file)
- {
- string fileName = file.Name;
- if (fileName.EndsWith(".moc.cbt", StringComparison.OrdinalIgnoreCase))
- fileName = fileName.Remove(fileName.LastIndexOf('.'));
- if (HelperFunctions.IsHeaderFile(fileName) || HelperFunctions.IsMocFile(fileName)) {
- fileName = fileName.Remove(fileName.LastIndexOf('.')) + ".cpp";
- foreach (VCFile f in (IVCCollection)vcPro.Files) {
- if (f.FullPath.EndsWith(Path.DirectorySeparatorChar + fileName, StringComparison.OrdinalIgnoreCase))
- return f;
- }
- }
- return null;
- }
-
public bool HasPlatform(string platformName)
{
foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) {