aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2019-01-18 18:00:46 +0100
committerMiguel Costa <miguel.costa@qt.io>2019-01-21 11:38:06 +0000
commit4d33c9b97a557713dce4c1eca6e42785a07f6bec (patch)
tree114cf4448593d8db100be6d98767f2a70adfcda1
parentda4f743763ab1515e46c8919cebd174d3dc32907 (diff)
Fix incorrect generation of custom build args
Will now correctly apply the format of character escaping when adding quotation marks to an argument of a custom build command. Previously, quotation marks that were already part of the argument were not correctly escaped, leading to incorrect argument strings being passed to Qt tools (e.g. moc argument to define macro with a string constant). Also fixed the incorrect expansion of project properties when generating custom build args. These should be generated with unexpanded property references; property expansion should only occur at build time. Task-number: QTVSADDINBUG-583 Change-Id: I04f2ec2d849cbb3de01c7b1b20fa202a596fafb1 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/qtprojectlib/QtProject.cs55
1 files changed, 45 insertions, 10 deletions
diff --git a/src/qtprojectlib/QtProject.cs b/src/qtprojectlib/QtProject.cs
index e6a3f54c..bf7bf135 100644
--- a/src/qtprojectlib/QtProject.cs
+++ b/src/qtprojectlib/QtProject.cs
@@ -609,17 +609,52 @@ namespace QtProjectLib
/// <summary>
/// Surrounds the argument by double quotes.
/// Makes sure, that the trailing double quote is not escaped by a backslash.
- /// Such an escaping backslash may also appear as a macro value.
+ /// 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)
{
- arg = "\"" + arg;
- if (arg.EndsWith("\\", StringComparison.Ordinal))
- arg += "."; // make sure, that we don't escape the trailing double quote
- else if (arg.EndsWith(")", StringComparison.Ordinal))
- arg += "\\."; // macro value could end with backslash. That would escape the trailing double quote.
- arg += "\"";
- return 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 == '\\').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)
@@ -630,12 +665,12 @@ namespace QtProjectLib
var propsProject = projectConfig.Rules.Item("CL") as IVCRulePropertyStorage;
if (propsFile != null) {
try {
- defines = propsFile.GetEvaluatedPropertyValue("PreprocessorDefinitions");
+ defines = propsFile.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
} catch { }
}
if (string.IsNullOrEmpty(defines) && propsProject != null) {
try {
- defines = propsProject.GetEvaluatedPropertyValue("PreprocessorDefinitions");
+ defines = propsProject.GetUnevaluatedPropertyValue("PreprocessorDefinitions");
} catch { }
}