diff --git a/llvm/docs/TestingGuide.rst b/llvm/docs/TestingGuide.rst --- a/llvm/docs/TestingGuide.rst +++ b/llvm/docs/TestingGuide.rst @@ -612,6 +612,13 @@ Example: ``Windows %errc_ENOENT: no such file or directory`` +``%if feature {} %else {}`` + + Conditional substitution: if ``feature`` is available it expands to + ````, otherwise it expands to ````. + ``%else {}`` is optional and treated like ``%else {}`` + if not present. + **LLVM-specific substitutions:** ``%shlibext`` diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py --- a/llvm/utils/lit/lit/TestRunner.py +++ b/llvm/utils/lit/lit/TestRunner.py @@ -48,7 +48,10 @@ # This regex captures ARG. ARG must not contain a right parenthesis, which # terminates %dbg. ARG must not contain quotes, in which ARG might be enclosed # during expansion. -kPdbgRegex = '%dbg\\(([^)\'"]*)\\)' +# +# COMMAND that follows %dbg(ARG) is also captured. COMMAND can be +# empty as a result of conditinal substitution. +kPdbgRegex = '%dbg\\(([^)\'"]*)\\)(.*)' class ShellEnvironment(object): @@ -899,7 +902,11 @@ def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): cmds = [] for i, ln in enumerate(commands): - ln = commands[i] = re.sub(kPdbgRegex, ": '\\1'; ", ln) + match = re.match(kPdbgRegex, ln) + if match: + command = match.group(2) + ln = commands[i] = \ + match.expand(": '\\1'; \\2" if command else ": '\\1'") try: cmds.append(ShUtil.ShParser(ln, litConfig.isWindows, test.config.pipefail).parse()) @@ -987,7 +994,12 @@ f = open(script, mode, **open_kwargs) if isWin32CMDEXE: for i, ln in enumerate(commands): - commands[i] = re.sub(kPdbgRegex, "echo '\\1' > nul && ", ln) + match = re.match(kPdbgRegex, ln) + if match: + command = match.group(2) + commands[i] = \ + match.expand("echo '\\1' > nul && " if command + else "echo '\\1' > nul") if litConfig.echo_all_commands: f.write('@echo on\n') else: @@ -995,7 +1007,11 @@ f.write('\n@if %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands)) else: for i, ln in enumerate(commands): - commands[i] = re.sub(kPdbgRegex, ": '\\1'; ", ln) + match = re.match(kPdbgRegex, ln) + if match: + command = match.group(2) + commands[i] = match.expand(": '\\1'; \\2" if command + else ": '\\1'") if test.config.pipefail: f.write(b'set -o pipefail;' if mode == 'wb' else 'set -o pipefail;') if litConfig.echo_all_commands: @@ -1179,7 +1195,7 @@ def _caching_re_compile(r): return re.compile(r) -def applySubstitutions(script, substitutions, recursion_limit=None): +def applySubstitutions(script, substitutions, conditions={}, recursion_limit=None): """ Apply substitutions to the script. Allow full regular expression syntax. Replace each matching occurrence of regular expression pattern a with @@ -1213,6 +1229,18 @@ # seems reasonable. ln = _caching_re_compile(a).sub(str(b), escape(ln)) + if_regex = r'%if\s+(.+?)\s+{(.*?)}(?:\s+%else\s+{(.*?)})?' + if_match = _caching_re_compile(if_regex).search(ln) + if if_match: + cond = if_match.group(1) + branch_if = if_match.group(2) + branch_else = if_match.group(3) + if BooleanExpression.evaluate(cond, conditions): + result = branch_if or '' + else: + result = branch_else or '' + ln = ln[:if_match.start()] + result + ln[if_match.end():] + # Strip the trailing newline and any extra whitespace. return ln.strip() @@ -1610,7 +1638,8 @@ substitutions = list(extra_substitutions) substitutions += getDefaultSubstitutions(test, tmpDir, tmpBase, normalize_slashes=useExternalSh) - script = applySubstitutions(script, substitutions, + conditions = { feature: True for feature in test.config.available_features } + script = applySubstitutions(script, substitutions, conditions, recursion_limit=test.config.recursiveExpansionLimit) return _runShTest(test, litConfig, useExternalSh, script, tmpBase) diff --git a/llvm/utils/lit/tests/Inputs/shtest-if-else/lit.cfg b/llvm/utils/lit/tests/Inputs/shtest-if-else/lit.cfg new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-if-else/lit.cfg @@ -0,0 +1,8 @@ +import lit.formats +config.name = 'shtest-if-else' +config.test_format = lit.formats.ShTest() +config.test_source_root = None +config.test_exec_root = None +config.suffixes = ['.txt'] +config.recursiveExpansionLimit = 2 +config.available_features.add("feature") diff --git a/llvm/utils/lit/tests/Inputs/shtest-if-else/test.txt b/llvm/utils/lit/tests/Inputs/shtest-if-else/test.txt new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/shtest-if-else/test.txt @@ -0,0 +1,34 @@ +# RUN: %if feature { echo "test-1" } + +# RUN: %if nofeature { echo "fail" } + +# RUN: %if nofeature { echo "fail" } %else { echo "test-2" } + +# Spaces inside curly braces are not ignored +# RUN: echo test-%if feature { 3 } %else { fail }-test +# RUN: echo test-%if feature { 4 4 } %else { fail }-test + +# RUN: %if feature \ +# RUN: { echo \ +# RUN: "test-5" \ +# RUN: } + +# RUN: %if nofeature \ +# RUN: { echo "fail" } \ +# RUN: %else \ +# RUN: { echo "test-6" } + +# RUN: echo "test%if feature {} %else {}-7" + +# RUN: echo %%if feature { echo "test-8" } + +# RUN: echo %if feature { %if feature {"test-9"} } + +# RUN: echo %if feature && !nofeature { "test-10" } +# RUN: echo %if feature && nofeature { "fail" } %else { "test-11" } + +# RUN: echo %if {{fea.+}} { "test-12" } %else { "fail" } + +# RUN: echo XX %if feature {YY} ZZ +# RUN: echo AA %if feature {BB} %else {CC} DD +# RUN: echo AA %if nofeature {BB} %else {CC} DD diff --git a/llvm/utils/lit/tests/shtest-if-else.py b/llvm/utils/lit/tests/shtest-if-else.py new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/shtest-if-else.py @@ -0,0 +1,27 @@ +# RUN: %{lit} -v --show-all %{inputs}/shtest-if-else | FileCheck %s +# END. + +# CHECK: -- Testing: +# CHECK-NEXT: PASS: shtest-if-else :: test.txt (1 of 1) +# CHECK-NEXT: Script: +# CHECK-NEXT: -- +# CHECK-NEXT: 'RUN: at line 1'; echo "test-1" +# CHECK-NEXT: 'RUN: at line 3' +# CHECK-NOT: fail +# CHECK-NEXT: 'RUN: at line 5'; echo "test-2" +# CHECK-NEXT: 'RUN: at line 8'; echo test- 3 -test +# CHECK-NEXT: 'RUN: at line 9'; echo test- 4 4 -test +# CHECK-NEXT: 'RUN: at line 11'; echo "test-5" +# CHECK-NEXT: 'RUN: at line 16'; echo "test-6" +# CHECK-NEXT: 'RUN: at line 21'; echo "test-7" +# CHECK-NEXT: 'RUN: at line 23'; echo %if feature { echo "test-8" } +# CHECK-NEXT: 'RUN: at line 25'; echo "test-9" +# CHECK-NEXT: 'RUN: at line 27'; echo "test-10" +# CHECK-NEXT: 'RUN: at line 28'; echo "test-11" +# CHECK-NEXT: 'RUN: at line 30'; echo "test-12" +# CHECK-NEXT: 'RUN: at line 32'; echo XX YY ZZ +# CHECK-NEXT: 'RUN: at line 33'; echo AA BB DD +# CHECK-NEXT: 'RUN: at line 34'; echo AA CC DD +# CHECK-NEXT: -- +# CHECK-NEXT: Exit Code: 0 +