Index: llvm/utils/lit/lit/TestRunner.py =================================================================== --- llvm/utils/lit/lit/TestRunner.py +++ llvm/utils/lit/lit/TestRunner.py @@ -1082,6 +1082,7 @@ else: if bashPath: command = [bashPath, script] + #print command, cwd, test.config.environment else: command = ['/bin/sh', script] if litConfig.useValgrind: @@ -1515,22 +1516,30 @@ return lit.Test.Result(status, output) -def executeShTest(test, litConfig, useExternalSh, - extra_substitutions=[]): +def extractScript(test, litConfig, useExternalSh, extra_substitutions): if test.config.unsupported: - return lit.Test.Result(Test.UNSUPPORTED, 'Test is unsupported') + return lit.Test.Result(Test.UNSUPPORTED, 'Test is unsupported'), '' script = parseIntegratedTestScript(test) if isinstance(script, lit.Test.Result): - return script + return script, '' if litConfig.noExecute: - return lit.Test.Result(Test.PASS) + return lit.Test.Result(Test.PASS), '' tmpDir, tmpBase = getTempPaths(test) substitutions = list(extra_substitutions) substitutions += getDefaultSubstitutions(test, tmpDir, tmpBase, normalize_slashes=useExternalSh) script = applySubstitutions(script, substitutions) + return script, tmpBase + + +def executeShTest(test, litConfig, useExternalSh, + extra_substitutions=[]): + script, tmpBase = extractScript( + test, litConfig, useExternalSh, extra_substitutions) + if isinstance(script, lit.Test.Result): + return script # Re-run failed tests up to test_retry_attempts times. attempts = 1 Index: llvm/utils/lit/lit/run.py =================================================================== --- llvm/utils/lit/lit/run.py +++ llvm/utils/lit/lit/run.py @@ -55,6 +55,125 @@ return _execute_test_impl(test, self.lit_config, self.parallelism_semaphores) + def execute_tests_using_ninja(self, jobs, max_time): + import lit.TestRunner + import pipes + import re + if sys.platform == 'win32': + sys.path.append('/src/ninja/misc') + else: + sys.path.append('/Users/thakis/src/ninja/misc') + import ninja_syntax + writer = ninja_syntax.Writer(open('lit.ninja', 'w')) + # Writes a build.ninja with commands for all tests and runs ninja + # to execute them. + for i, test in enumerate(self.tests): + if not isinstance(test.config.test_format, lit.formats.ShTest): + test.setResult(lit.Test.Result(lit.Test.PASS, '')) # FIXME + continue + script, tmpBase = lit.TestRunner.extractScript( + test, self.lit_config, True, []) + if isinstance(script, lit.Test.Result): + test.result = script + continue + + # Create the output directory if it does not already exist. + lit.util.mkdir_p(os.path.dirname(tmpBase)) + + r = 'r%04d' % i + # FIXME: duplication with TestRunner.executeScript() + # FIXME: windows + execdir = os.path.dirname(test.getExecPath()) + command = '' + if sys.platform == 'win32': + command = 'cmd /c "' + for var, val in test.config.environment.iteritems(): + # Need export, else LLD_VERSION doesn't make it through. + # The export means LIT_PRESERVES_TMP=1 needs to be set while + # running lit, else it's gone by the time ninja runs + # (...or this here must run ninja, probably better anyhoo) + if sys.platform == 'win32': + # FIXME: quoting + command += 'set %s=%s&& ' % (var, val) + else: + command += 'export %s=%s; ' % (var, pipes.quote(val)) + if sys.platform != 'win32' and test.config.pipefail: + command += 'set -o pipefail; ' + if sys.platform == 'win32': + command += 'cd %s&&' % execdir # no space before &&, else set + + for j in range(len(script)): + # Convert from lit quoting to cmd.exe quoting. + # Want: + # /manifestdependency:"foo='bar'" => + # /manifestdependency:^"foo='bar'^" + # But: + # echo -e '.section .bss,,"bw",discard' => + # echo -e ^".section .bss,,\"bw\",discard^" + # Also: + # echo -e "PT_LOAD FLAGS(0x4 | 0x1);" => + # echo -e ^"PT_LOAD FLAGS^(0x4 ^| 0x1^);^" + # (always0836 / ELF/linkerscript/outsections-addr.s and + # always1355 / ELF/version-script-extern.s good inputs) + # But don't want to quote | when part of a command pipe. + tok, new = re.split("('|\")", script[j]), "" + quot, dquot = False, False + for t in tok: + if quot: + if t == "'": + t = '^"' + quot = False + elif t == '"': + dquot = not dquot + t = '\\"' + else: + if t == "'" and not dquot: + quot = True + t = '^"' + elif t == '"': + dquot = not dquot + t = '^"' + if quot or dquot: + t = t.replace('|', '^|').replace('<', '^<')\ + .replace('&', '^&') + if not (quot and dquot): + t = t.replace('(', '^(').replace(')', '^)') + new += t + script[j] = new + + # cmd.exe has a "mkdir" built-in, but we need gnuwin mkdir's + # features, so replace |mkdir| with |"mkdir"| to get the + # non-builtin version. + # Also, at least my gnuwin32 mkdir doesn't work if a path + # starts with c:/, it needs to be either / or c:\ + if 'mkdir ' in script[j]: + script[j] = script[j].replace('mkdir ', '^"mkdir^" ' )\ + .replace('C:/', 'C:\\') + # Same for |echo -e| + if 'echo ' in script[j]: + script[j] = script[j].replace('echo ', '^"echo^" ' ) + + # cmd.exe doesn't know >& foo, use > foo 2>&1 instead. + script[j] = re.sub(r' >& (\S+)', r' > \1 2>&1 ', script[j]) + # /dev/null is called nul + script[j] = script[j].replace('/dev/null', 'nul' ) + command += ' (' + ') && ('.join(script) + ')' + command += '"' + else: + command += 'cd %s;' % execdir + command += ' { ' + '; } && { '.join(script) + '; }' + # FIXME: tests require bash for e.g. `echo -e` - unfortunate :-/ + command = '/bin/bash -c %s' % pipes.quote(command) + + if sys.platform != 'win32' or len(command) < 8000: + writer.rule(r, command, description=test.getFullName()) + writer.build('always%04d' % i, r) + else: + print 'skipping', i + + # FIXME: ...well... + test.setResult(lit.Test.Result(lit.Test.PASS, '')) + def execute_tests_in_pool(self, jobs, max_time): # We need to issue many wait calls, so compute the final deadline and # subtract time.time() from that as we go along. @@ -149,7 +268,8 @@ result = worker_run_one_test(test_index, test) self.consume_test_result(result) else: - self.execute_tests_in_pool(jobs, max_time) + #self.execute_tests_in_pool(jobs, max_time) + self.execute_tests_using_ninja(jobs, max_time) # Mark any tests that weren't run as UNRESOLVED. for test in self.tests: