Skip to content

Commit c9a2877

Browse files
authored
Redo PR 241: parsing complex scenarios of quoting and escaping. (#243)
* Redo PR 241: parsing complex scenarios of quoting and escaping. * add temporary logging to see how much of the new test is executing * Break up the new windows test in two to compare smaller baseline/output strings. * Try cl instead of gcc so that the CI machines don't find the tool in their path and report full (machine dependent) path for compiler path. * Undo previous mistake, we need gcc not cl but ensure preconfigure adds a path to these compilers inside our repository * Cover more interesting scenarios for compiler path while also ensuring uniformity between systems * Fix linux test baseline according to latest changes in the dryrun
1 parent ef33b87 commit c9a2877

13 files changed

+1107
-297
lines changed

src/parser.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -492,14 +492,14 @@ async function parseAnySwitchFromToolArguments(args: string, excludeArgs: string
492492
};
493493

494494
let stderr: any = (result: string): void => {
495-
logger.messageNoCR(`Error while running the compiler args parser script '${parseCompilerArgsScriptFile}'` +
496-
`for regions "${compilerArgRegions}": "${result}"`, "Normal");
495+
logger.message(`Error while running the compiler args parser script '${parseCompilerArgsScriptFile}'` +
496+
`for regions ("${compilerArgRegions})": "${result}"`, "Normal");
497497
};
498498

499499
// Running the compiler arguments parsing script can use the system locale.
500500
const result: util.SpawnProcessResult = await util.spawnChildProcess(runCommand, scriptArgs, util.getWorkspaceRoot(), false, stdout, stderr);
501501
if (result.returnCode !== 0) {
502-
logger.messageNoCR(`The compiler args parser script '${parseCompilerArgsScriptFile}' failed with error code ${result.returnCode}`, "Normal");
502+
logger.message(`The compiler args parser script '${parseCompilerArgsScriptFile}' failed with error code ${result.returnCode} for regions (${compilerArgRegions})`, "Normal");
503503
}
504504
} catch (error) {
505505
logger.message(error);
@@ -532,16 +532,30 @@ function parseMultipleSwitchFromToolArguments(args: string, sw: string, removeSu
532532
// (example): -DMY_DEFINE='"SOME_VALUE"'
533533

534534
function anythingBetweenQuotes(fullyQuoted: boolean): string {
535-
let anythingBetweenReverseQuote: string = '\\`[^\\`]*?\\`';
536-
let anythingBetweenSingleQuote: string = "\\'[^\\']*?\\'";
537-
let anythingBetweenDoubleQuote: string = '\\"[^\\"]*?\\"';
538-
539-
// If the switch is fully quoted with ', like ('-DMY_DEFINE="MyValue"'), don't allow single quotes
540-
// inside the switch value.
541-
// One example of what can be broken if we don't do this: gcc '-DDEF1=' '-DDef2=val2'
542-
// in which case DEF1 would be seen as DEF1=' ' instead of empty =
543-
let str: string = anythingBetweenReverseQuote + '|' + anythingBetweenDoubleQuote + (fullyQuoted ? "" : '|' + anythingBetweenSingleQuote);
544-
return str;
535+
// The basic pattern for anything between quotes accepts equally single quote, double quote or back tick.
536+
// One pattern that is accepted is to wrap between escaped quotes and allow inside anything (including non-escaped quotes) except escaped quotes.
537+
// Another pattern that is accepted is to wrap between non-escaped quotes and allow inside anything (including escaped quotes) except non-escaped quotes.
538+
// One problem with the "..." pattern is that a simple "\" (or anything ending with \") will not know if the backslash is part of the inside of quote-quote
539+
// or together with the following quote represents a \" and needs to look forward for another ending quote.
540+
// If there is another quote somewhere else later in the command line (another -D or a file name wrapped in quotes) everything until that first upcoming quote
541+
// will be included.
542+
// Example that doesn't work: -DSLASH_DEFINE="\" -DSOME_OTHER_SWITCH "drive:\folder\file.extension"
543+
// SLASH_DEFINE is equal to '\" -DSOME_OTHER_SWITCH '
544+
// Example that works: -DGIT_VERSION=" \" 1.2.3 \" "
545+
// GIT_VERSION is equal to ' \" 1.2.3 \" '
546+
// Unfortunately, we also can't identify this to log in the output channel for later analysis of more makefile switch and quoting user scenarios.
547+
// Fortunately, we didn't encounter the last scenario, only the first.
548+
function anythingBetweenQuotesBasicPattern(quoteChar: string): string {
549+
return '\\\\\\' + quoteChar + "((?!\\\\\\" + quoteChar + ").)*\\\\\\" + quoteChar + "|" + // \" anything(except \") \"
550+
"\\" + quoteChar + "(\\\\\\" + quoteChar + "|[^\\" + quoteChar + "])*?[^\\\\\\" + quoteChar + "]?\\" + quoteChar; // " anything (except ") "
551+
}
552+
553+
// If the switch is fully quoted with ', like ('-DMY_DEFINE="MyValue"'), don't allow single quotes
554+
// inside the switch value.
555+
// One example of what can be broken if we don't do this: gcc '-DDEF1=' '-DDef2=val2'
556+
// in which case DEF1 would be seen as DEF1=' ' instead of empty =
557+
let str: string = anythingBetweenQuotesBasicPattern("`") + '|' + anythingBetweenQuotesBasicPattern('"') + (fullyQuoted ? "" : '|' + anythingBetweenQuotesBasicPattern("'"));
558+
return str;
545559
}
546560

547561
function mainPattern(fullyQuoted: boolean): string {
@@ -584,7 +598,7 @@ function parseMultipleSwitchFromToolArguments(args: string, sw: string, removeSu
584598

585599
match = regexp.exec(args);
586600
while (match) {
587-
let matchIndex: number = (match[2].startsWith("'") && match[2].endsWith("'")) ? 8 : 18;
601+
let matchIndex: number = (match[2].startsWith("'") && match[2].endsWith("'")) ? 8 : 26;
588602
let result: string = match[matchIndex];
589603
if (result) {
590604
if (removeSurroundingQuotes) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
@echo off
22
set PATH="%cd%\bin\InterestingSmallMakefile\CL\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\12.34.56789\bin\Hostx86\x86";%PATH%
3+
set PATH=%cd%\bin\GitHub\compilers;%PATH%
34

src/test/fakeSuite/Repros/.vscode/settings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@
33
"makefile.buildLog": "./dummy_dryrun.log",
44
"makefile.makePath": "c:/some/other/fake/path",
55
"makefile.loggingLevel": "Debug",
6+
"makefile.additionalCompilerNames": ["MyOwnFakeCompiler"],
67
"makefile.configureOnOpen": false,
78
"makefile.configurations": [
9+
{
10+
"name": "complex_escaped_quotes",
11+
"buildLog": "./complex_escaped_quotes_dryrun.log"
12+
},
13+
{
14+
"name": "complex_escaped_quotes_winOnly",
15+
"buildLog": "./complex_escaped_quotes_winOnly_dryrun.log"
16+
},
817
{
918
"name": "InterestingSmallMakefile_windows_configDebug",
1019
"buildLog": "./InterestingSmallMakefile_windows_dryrunDebug.log"

src/test/fakeSuite/Repros/8cc_linux_baseline.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Found build log path setting "./8cc_linux_dryrun.log" defined for configuration
66
Resolving build log path to "{REPO:VSCODE-MAKEFILE-TOOLS}/src/test/fakeSuite/Repros/8cc_linux_dryrun.log"
77
Deduced command 'c:/some/other/fake/path ' for configuration 8cc_linux
88
Saving opened files before build.
9+
Loading configurations from cache is not necessary.
910
Preprocessing: "{REPO:VSCODE-MAKEFILE-TOOLS}/src/test/fakeSuite/Repros/8cc_linux_dryrun.log"
1011
Preprocess elapsed time: 0
1112
Parsing for IntelliSense.
@@ -183,7 +184,6 @@ Found the following 29 build targets defined in the makefile: 8cc;all;buffer.o;c
183184
Complete list of build targets: 8cc;all;buffer.o;clean;cleanobj;cpp.o;debug.o;dict.o;encoding.o;error.o;file.o;fulltest;gen.o;lex.o;main.o;map.o;parse.o;path.o;runtests;self;set.o;stage1;stage2;stage3;test;testtest;utiltest;utiltest.o;vector.o
184185
Parsing build targets elapsed time: 0
185186
Configure finished. The status for all the subphases that ran:
186-
loadFromCache: return code = -3, elapsed time = 0
187187
generateParseContent: return code = 0, elapsed time = 0
188188
preprocessParseContent: return code = 0, elapsed time = 0
189189
parseIntelliSense: return code = 0, elapsed time = 0

src/test/fakeSuite/Repros/InterestingSmallMakefile_windows_baseline.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Found build log path setting "./InterestingSmallMakefile_windows_dryrunDebug.log
88
Resolving build log path to "{REPO:VSCODE-MAKEFILE-TOOLS}\src\test\fakeSuite\Repros\InterestingSmallMakefile_windows_dryrunDebug.log"
99
Deduced command 'c:\some\other\fake\path.exe ' for configuration InterestingSmallMakefile_windows_configDebug
1010
Saving opened files before build.
11+
Loading configurations from cache is not necessary.
1112
Preprocessing: "{REPO:VSCODE-MAKEFILE-TOOLS}\src\test\fakeSuite\Repros\InterestingSmallMakefile_windows_dryrunDebug.log"
1213
Preprocess elapsed time: 0
1314
Parsing for IntelliSense.
@@ -193,7 +194,6 @@ Found the following 21 build targets defined in the makefile: Arch1_main.exe;Arc
193194
Complete list of build targets: Arch1_main.exe;Arch2_main.exe;Arch3_main.exe;Execute_Arch1;Execute_Arch2;Execute_Arch3;all;buildArch1;buildArch2;buildArch3;builds;clean;create_dirs;create_dirs_Arch1;create_dirs_Arch2;create_dirs_Arch3;install;rebuild;runs;simple;simple/simple.exe
194195
Parsing build targets elapsed time: 0
195196
Configure finished. The status for all the subphases that ran:
196-
loadFromCache: return code = -3, elapsed time = 0
197197
generateParseContent: return code = 0, elapsed time = 0
198198
preprocessParseContent: return code = 0, elapsed time = 0
199199
parseIntelliSense: return code = 0, elapsed time = 0

src/test/fakeSuite/Repros/bin/GitHub/compilers/gcc.exe

Whitespace-only changes.

0 commit comments

Comments
 (0)