Commit d576fc38 authored by Krzysztof Gogolewski's avatar Krzysztof Gogolewski

Python 3 support, second attempt (Trac #9184)

Summary:
This is a fixup of https://phabricator.haskell.org/D233

The only difference is in findTFiles (first commit), which
previously broke Windows runner; now I translated literally
instead attempting to improve it, and checked it works.

Test Plan:
I used validate under 2,3 on Linux and under 2 on msys2.
On Windows I've seen a large number of failures, but they don't
seem to be connected with the patch.

Reviewers: hvr, simonmar, thomie, austin

Reviewed By: austin

Subscribers: thomie, carter, ezyang, simonmar

Differential Revision: https://phabricator.haskell.org/D310

GHC Trac Issues: #9184
parent abfbb0d6
...@@ -148,21 +148,17 @@ config.way_rts_flags = { ...@@ -148,21 +148,17 @@ config.way_rts_flags = {
# Useful classes of ways that can be used with only_ways() and # Useful classes of ways that can be used with only_ways() and
# expect_broken_for(). # expect_broken_for().
prof_ways = map (lambda x: x[0], \ prof_ways = [x[0] for x in config.way_flags('dummy_name').items()
filter(lambda x: '-prof' in x[1], \ if '-prof' in x[1]]
config.way_flags('dummy_name').items()))
threaded_ways = map (lambda x: x[0], \ threaded_ways = [x[0] for x in config.way_flags('dummy_name').items()
filter(lambda x: '-threaded' in x[1] or 'ghci' == x[0], \ if '-threaded' in x[1] or 'ghci' == x[0]]
config.way_flags('dummy_name').items()))
opt_ways = map (lambda x: x[0], \ opt_ways = [x[0] for x in config.way_flags('dummy_name').items()
filter(lambda x: '-O' in x[1], \ if '-O' in x[1]]
config.way_flags('dummy_name').items()))
llvm_ways = map (lambda x: x[0], \ llvm_ways = [x[0] for x in config.way_flags('dummy_name').items()
filter(lambda x: '-fllvm' in x[1], \ if '-fflvm' in x[1]]
config.way_flags('dummy_name').items()))
def get_compiler_info(): def get_compiler_info():
# This should really not go through the shell # This should really not go through the shell
...@@ -192,7 +188,7 @@ def get_compiler_info(): ...@@ -192,7 +188,7 @@ def get_compiler_info():
if re.match(".*_p(_.*|$)", rtsInfoDict["RTS way"]): if re.match(".*_p(_.*|$)", rtsInfoDict["RTS way"]):
config.compiler_profiled = True config.compiler_profiled = True
config.run_ways = filter(lambda x: x != 'ghci', config.run_ways) config.run_ways = [x for x in config.run_ways if x != 'ghci']
else: else:
config.compiler_profiled = False config.compiler_profiled = False
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# (c) Simon Marlow 2002 # (c) Simon Marlow 2002
# #
from __future__ import print_function
import sys import sys
import os import os
import string import string
...@@ -21,6 +23,11 @@ try: ...@@ -21,6 +23,11 @@ try:
except: except:
pass pass
PYTHON3 = sys.version_info >= (3, 0)
if PYTHON3:
print("*** WARNING: running testsuite using Python 3.\n"
"*** Python 3 support is experimental. See Trac #9184.")
from testutil import * from testutil import *
from testglobals import * from testglobals import *
...@@ -52,12 +59,12 @@ opts, args = getopt.getopt(sys.argv[1:], "e:", long_options) ...@@ -52,12 +59,12 @@ opts, args = getopt.getopt(sys.argv[1:], "e:", long_options)
for opt,arg in opts: for opt,arg in opts:
if opt == '--config': if opt == '--config':
execfile(arg) exec(open(arg).read())
# -e is a string to execute from the command line. For example: # -e is a string to execute from the command line. For example:
# testframe -e 'config.compiler=ghc-5.04' # testframe -e 'config.compiler=ghc-5.04'
if opt == '-e': if opt == '-e':
exec arg exec(arg)
if opt == '--rootdir': if opt == '--rootdir':
config.rootdirs.append(arg) config.rootdirs.append(arg)
...@@ -83,9 +90,9 @@ for opt,arg in opts: ...@@ -83,9 +90,9 @@ for opt,arg in opts:
sys.stderr.write("ERROR: requested way \'" + sys.stderr.write("ERROR: requested way \'" +
arg + "\' does not exist\n") arg + "\' does not exist\n")
sys.exit(1) sys.exit(1)
config.other_ways = filter(neq(arg), config.other_ways) config.other_ways = [w for w in config.other_ways if w != arg]
config.run_ways = filter(neq(arg), config.run_ways) config.run_ways = [w for w in config.run_ways if w != arg]
config.compile_ways = filter(neq(arg), config.compile_ways) config.compile_ways = [w for w in config.compile_ways if w != arg]
if opt == '--threads': if opt == '--threads':
config.threads = int(arg) config.threads = int(arg)
...@@ -117,17 +124,17 @@ if config.use_threads == 1: ...@@ -117,17 +124,17 @@ if config.use_threads == 1:
maj = int(re.sub('[^0-9].*', '', str(maj))) maj = int(re.sub('[^0-9].*', '', str(maj)))
min = int(re.sub('[^0-9].*', '', str(min))) min = int(re.sub('[^0-9].*', '', str(min)))
pat = int(re.sub('[^0-9].*', '', str(pat))) pat = int(re.sub('[^0-9].*', '', str(pat)))
if (maj, min, pat) < (2, 5, 2): if (maj, min) < (2, 6):
print "Warning: Ignoring request to use threads as python version < 2.5.2" print("Python < 2.6 is not supported")
config.use_threads = 0 sys.exit(1)
# We also need to disable threads for python 2.7.2, because of # We also need to disable threads for python 2.7.2, because of
# this bug: http://bugs.python.org/issue13817 # this bug: http://bugs.python.org/issue13817
elif (maj, min, pat) == (2, 7, 2): elif (maj, min, pat) == (2, 7, 2):
print "Warning: Ignoring request to use threads as python version is 2.7.2" print("Warning: Ignoring request to use threads as python version is 2.7.2")
print "See http://bugs.python.org/issue13817 for details." print("See http://bugs.python.org/issue13817 for details.")
config.use_threads = 0 config.use_threads = 0
if windows: if windows:
print "Warning: Ignoring request to use threads as running on Windows" print("Warning: Ignoring request to use threads as running on Windows")
config.use_threads = 0 config.use_threads = 0
config.cygwin = False config.cygwin = False
...@@ -180,10 +187,10 @@ else: ...@@ -180,10 +187,10 @@ else:
h.close() h.close()
if v != '': if v != '':
os.environ['LC_ALL'] = v os.environ['LC_ALL'] = v
print "setting LC_ALL to", v print("setting LC_ALL to", v)
else: else:
print 'WARNING: No UTF8 locale found.' print('WARNING: No UTF8 locale found.')
print 'You may get some spurious test failures.' print('You may get some spurious test failures.')
# This has to come after arg parsing as the args can change the compiler # This has to come after arg parsing as the args can change the compiler
get_compiler_info() get_compiler_info()
...@@ -230,7 +237,7 @@ if config.use_threads: ...@@ -230,7 +237,7 @@ if config.use_threads:
if config.timeout == -1: if config.timeout == -1:
config.timeout = int(read_no_crs(config.top + '/timeout/calibrate.out')) config.timeout = int(read_no_crs(config.top + '/timeout/calibrate.out'))
print 'Timeout is ' + str(config.timeout) print('Timeout is ' + str(config.timeout))
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# The main dude # The main dude
...@@ -240,40 +247,44 @@ if config.rootdirs == []: ...@@ -240,40 +247,44 @@ if config.rootdirs == []:
t_files = findTFiles(config.rootdirs) t_files = findTFiles(config.rootdirs)
print 'Found', len(t_files), '.T files...' print('Found', len(t_files), '.T files...')
t = getTestRun() t = getTestRun()
# Avoid cmd.exe built-in 'date' command on Windows # Avoid cmd.exe built-in 'date' command on Windows
t.start_time = time.localtime() t.start_time = time.localtime()
print 'Beginning test run at', time.strftime("%c %Z",t.start_time) print('Beginning test run at', time.strftime("%c %Z",t.start_time))
# set stdout to unbuffered (is this the best way to do it?)
sys.stdout.flush() sys.stdout.flush()
sys.stdout = os.fdopen(sys.__stdout__.fileno(), "w", 0) if PYTHON3:
# in Python 3, we output text, which cannot be unbuffered
sys.stdout = os.fdopen(sys.__stdout__.fileno(), "w")
else:
# set stdout to unbuffered (is this the best way to do it?)
sys.stdout = os.fdopen(sys.__stdout__.fileno(), "w", 0)
# First collect all the tests to be run # First collect all the tests to be run
for file in t_files: for file in t_files:
if_verbose(2, '====> Scanning %s' % file) if_verbose(2, '====> Scanning %s' % file)
newTestDir(os.path.dirname(file)) newTestDir(os.path.dirname(file))
try: try:
execfile(file) exec(open(file).read())
except: except Exception:
print '*** framework failure: found an error while executing ', file, ':' print('*** framework failure: found an error while executing ', file, ':')
t.n_framework_failures = t.n_framework_failures + 1 t.n_framework_failures = t.n_framework_failures + 1
traceback.print_exc() traceback.print_exc()
if config.list_broken: if config.list_broken:
global brokens global brokens
print '' print('')
print 'Broken tests:' print('Broken tests:')
print (' '.join(map (lambda (b, d, n) : '#' + str(b) + '(' + d + '/' + n + ')', brokens))) print(' '.join(map (lambda bdn: '#' + str(bdn[0]) + '(' + bdn[1] + '/' + bdn[2] + ')', brokens)))
print '' print('')
if t.n_framework_failures != 0: if t.n_framework_failures != 0:
print 'WARNING:', str(t.n_framework_failures), 'framework failures!' print('WARNING:', str(t.n_framework_failures), 'framework failures!')
print '' print('')
else: else:
# Now run all the tests # Now run all the tests
if config.use_threads: if config.use_threads:
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
# (c) Simon Marlow 2002 # (c) Simon Marlow 2002
# #
# This allows us to use the "with X:" syntax with python 2.5: from __future__ import print_function
from __future__ import with_statement
import shutil import shutil
import sys import sys
...@@ -16,7 +15,6 @@ import time ...@@ -16,7 +15,6 @@ import time
import datetime import datetime
import copy import copy
import glob import glob
import types
from math import ceil, trunc from math import ceil, trunc
have_subprocess = False have_subprocess = False
...@@ -24,15 +22,17 @@ try: ...@@ -24,15 +22,17 @@ try:
import subprocess import subprocess
have_subprocess = True have_subprocess = True
except: except:
print "Warning: subprocess not found, will fall back to spawnv" print("Warning: subprocess not found, will fall back to spawnv")
from string import join
from testglobals import * from testglobals import *
from testutil import * from testutil import *
if config.use_threads: if config.use_threads:
import threading import threading
import thread try:
import thread
except ImportError: # Python 3
import _thread as thread
global wantToStop global wantToStop
wantToStop = False wantToStop = False
...@@ -99,7 +99,7 @@ def reqlib( lib ): ...@@ -99,7 +99,7 @@ def reqlib( lib ):
have_lib = {} have_lib = {}
def _reqlib( name, opts, lib ): def _reqlib( name, opts, lib ):
if have_lib.has_key(lib): if lib in have_lib:
got_it = have_lib[lib] got_it = have_lib[lib]
else: else:
if have_subprocess: if have_subprocess:
...@@ -284,7 +284,7 @@ def _stats_num_field( name, opts, field, expecteds ): ...@@ -284,7 +284,7 @@ def _stats_num_field( name, opts, field, expecteds ):
if field in opts.stats_range_fields: if field in opts.stats_range_fields:
framework_fail(name, 'duplicate-numfield', 'Duplicate ' + field + ' num_field check') framework_fail(name, 'duplicate-numfield', 'Duplicate ' + field + ' num_field check')
if type(expecteds) is types.ListType: if type(expecteds) is list:
for (b, expected, dev) in expecteds: for (b, expected, dev) in expecteds:
if b: if b:
opts.stats_range_fields[field] = (expected, dev) opts.stats_range_fields[field] = (expected, dev)
...@@ -512,9 +512,10 @@ def two_normalisers(f, g): ...@@ -512,9 +512,10 @@ def two_normalisers(f, g):
# Function for composing two opt-fns together # Function for composing two opt-fns together
def executeSetups(fs, name, opts): def executeSetups(fs, name, opts):
if type(fs) is types.ListType: if type(fs) is list:
# If we have a list of setups, then execute each one # If we have a list of setups, then execute each one
map (lambda f : executeSetups(f, name, opts), fs) for f in fs:
executeSetups(f, name, opts)
else: else:
# fs is a single function, so just apply it # fs is a single function, so just apply it
fs(name, opts) fs(name, opts)
...@@ -625,8 +626,7 @@ def test_common_work (name, opts, func, args): ...@@ -625,8 +626,7 @@ def test_common_work (name, opts, func, args):
all_ways = ['normal'] all_ways = ['normal']
# A test itself can request extra ways by setting opts.extra_ways # A test itself can request extra ways by setting opts.extra_ways
all_ways = all_ways + filter(lambda way: way not in all_ways, all_ways = all_ways + [way for way in opts.extra_ways if way not in all_ways]
opts.extra_ways)
t.total_test_cases = t.total_test_cases + len(all_ways) t.total_test_cases = t.total_test_cases + len(all_ways)
...@@ -639,7 +639,7 @@ def test_common_work (name, opts, func, args): ...@@ -639,7 +639,7 @@ def test_common_work (name, opts, func, args):
and way not in getTestOpts().omit_ways and way not in getTestOpts().omit_ways
# Which ways we are asked to skip # Which ways we are asked to skip
do_ways = filter (ok_way,all_ways) do_ways = list(filter (ok_way,all_ways))
# In fast mode, we skip all but one way # In fast mode, we skip all but one way
if config.fast and len(do_ways) > 0: if config.fast and len(do_ways) > 0:
...@@ -658,8 +658,8 @@ def test_common_work (name, opts, func, args): ...@@ -658,8 +658,8 @@ def test_common_work (name, opts, func, args):
if getTestOpts().cleanup != '' and (config.clean_only or do_ways != []): if getTestOpts().cleanup != '' and (config.clean_only or do_ways != []):
pretest_cleanup(name) pretest_cleanup(name)
clean(map (lambda suff: name + suff, clean([name + suff for suff in [
['', '.exe', '.exe.manifest', '.genscript', '', '.exe', '.exe.manifest', '.genscript',
'.stderr.normalised', '.stdout.normalised', '.stderr.normalised', '.stdout.normalised',
'.run.stderr.normalised', '.run.stdout.normalised', '.run.stderr.normalised', '.run.stdout.normalised',
'.comp.stderr.normalised', '.comp.stdout.normalised', '.comp.stderr.normalised', '.comp.stdout.normalised',
...@@ -667,12 +667,13 @@ def test_common_work (name, opts, func, args): ...@@ -667,12 +667,13 @@ def test_common_work (name, opts, func, args):
'.stats', '.comp.stats', '.stats', '.comp.stats',
'.hi', '.o', '.prof', '.exe.prof', '.hc', '.hi', '.o', '.prof', '.exe.prof', '.hc',
'_stub.h', '_stub.c', '_stub.o', '_stub.h', '_stub.c', '_stub.o',
'.hp', '.exe.hp', '.ps', '.aux', '.hcr', '.eventlog'])) '.hp', '.exe.hp', '.ps', '.aux', '.hcr', '.eventlog']])
if func == multi_compile or func == multi_compile_fail: if func == multi_compile or func == multi_compile_fail:
extra_mods = args[1] extra_mods = args[1]
clean(map (lambda (f,x): replace_suffix(f, 'o'), extra_mods)) clean([replace_suffix(fx[0],'o') for fx in extra_mods])
clean(map (lambda (f,x): replace_suffix(f, 'hi'), extra_mods)) clean([replace_suffix(fx[0], 'hi') for fx in extra_mods])
clean(getTestOpts().clean_files) clean(getTestOpts().clean_files)
...@@ -712,7 +713,7 @@ def test_common_work (name, opts, func, args): ...@@ -712,7 +713,7 @@ def test_common_work (name, opts, func, args):
files_written_not_removed[name] = [f] files_written_not_removed[name] = [f]
except: except:
pass pass
except Exception, e: except Exception as e:
framework_fail(name, 'runTest', 'Unhandled exception: ' + str(e)) framework_fail(name, 'runTest', 'Unhandled exception: ' + str(e))
def clean(strs): def clean(strs):
...@@ -724,19 +725,19 @@ def clean_full_path(name): ...@@ -724,19 +725,19 @@ def clean_full_path(name):
try: try:
# Remove files... # Remove files...
os.remove(name) os.remove(name)
except OSError, e1: except OSError as e1:
try: try:
# ... and empty directories # ... and empty directories
os.rmdir(name) os.rmdir(name)
except OSError, e2: except OSError as e2:
# We don't want to fail here, but we do want to know # We don't want to fail here, but we do want to know
# what went wrong, so print out the exceptions. # what went wrong, so print out the exceptions.
# ENOENT isn't a problem, though, as we clean files # ENOENT isn't a problem, though, as we clean files
# that don't necessarily exist. # that don't necessarily exist.
if e1.errno != errno.ENOENT: if e1.errno != errno.ENOENT:
print e1 print(e1)
if e2.errno != errno.ENOENT: if e2.errno != errno.ENOENT:
print e2 print(e2)
def do_test(name, way, func, args): def do_test(name, way, func, args):
full_name = name + '(' + way + ')' full_name = name + '(' + way + ')'
...@@ -761,7 +762,7 @@ def do_test(name, way, func, args): ...@@ -761,7 +762,7 @@ def do_test(name, way, func, args):
framework_fail(name, way, 'pre-command exception') framework_fail(name, way, 'pre-command exception')
try: try:
result = apply(func, [name,way] + args) result = func(*[name,way] + args)
finally: finally:
if config.use_threads: if config.use_threads:
t.lock.acquire() t.lock.acquire()
...@@ -892,7 +893,8 @@ def run_command( name, way, cmd ): ...@@ -892,7 +893,8 @@ def run_command( name, way, cmd ):
def ghci_script( name, way, script ): def ghci_script( name, way, script ):
# filter out -fforce-recomp from compiler_always_flags, because we're # filter out -fforce-recomp from compiler_always_flags, because we're
# actually testing the recompilation behaviour in the GHCi tests. # actually testing the recompilation behaviour in the GHCi tests.
flags = filter(lambda f: f != '-fforce-recomp', getTestOpts().compiler_always_flags) flags = [f for f in getTestOpts().compiler_always_flags if f != '-fforce-recomp']
flags.append(getTestOpts().extra_hc_opts) flags.append(getTestOpts().extra_hc_opts)
if getTestOpts().outputdir != None: if getTestOpts().outputdir != None:
flags.extend(["-outputdir", getTestOpts().outputdir]) flags.extend(["-outputdir", getTestOpts().outputdir])
...@@ -900,10 +902,10 @@ def ghci_script( name, way, script ): ...@@ -900,10 +902,10 @@ def ghci_script( name, way, script ):
# We pass HC and HC_OPTS as environment variables, so that the # We pass HC and HC_OPTS as environment variables, so that the
# script can invoke the correct compiler by using ':! $HC $HC_OPTS' # script can invoke the correct compiler by using ':! $HC $HC_OPTS'
cmd = "HC='" + config.compiler + "' " + \ cmd = "HC='" + config.compiler + "' " + \
"HC_OPTS='" + join(flags,' ') + "' " + \ "HC_OPTS='" + ' '.join(flags) + "' " + \
"'" + config.compiler + "'" + \ "'" + config.compiler + "'" + \
' --interactive -v0 -ignore-dot-ghci ' + \ ' --interactive -v0 -ignore-dot-ghci ' + \
join(flags,' ') ' '.join(flags)
getTestOpts().stdin = script getTestOpts().stdin = script
return simple_run( name, way, cmd, getTestOpts().extra_run_opts ) return simple_run( name, way, cmd, getTestOpts().extra_run_opts )
...@@ -967,7 +969,7 @@ def do_compile( name, way, should_fail, top_mod, extra_mods, extra_hc_opts ): ...@@ -967,7 +969,7 @@ def do_compile( name, way, should_fail, top_mod, extra_mods, extra_hc_opts ):
return passed() return passed()
def compile_cmp_asm( name, way, extra_hc_opts ): def compile_cmp_asm( name, way, extra_hc_opts ):
print 'Compile only, extra args = ', extra_hc_opts print('Compile only, extra args = ', extra_hc_opts)
pretest_cleanup(name) pretest_cleanup(name)
result = simple_build( name + '.cmm', way, '-keep-s-files -O ' + extra_hc_opts, 0, '', 0, 0, 0) result = simple_build( name + '.cmm', way, '-keep-s-files -O ' + extra_hc_opts, 0, '', 0, 0, 0)
...@@ -1049,7 +1051,7 @@ def checkStats(name, way, stats_file, range_fields): ...@@ -1049,7 +1051,7 @@ def checkStats(name, way, stats_file, range_fields):
for (field, (expected, dev)) in range_fields.items(): for (field, (expected, dev)) in range_fields.items():
m = re.search('\("' + field + '", "([0-9]+)"\)', contents) m = re.search('\("' + field + '", "([0-9]+)"\)', contents)
if m == None: if m == None:
print 'Failed to find field: ', field print('Failed to find field: ', field)
result = failBecause('no such stats field') result = failBecause('no such stats field')
val = int(m.group(1)) val = int(m.group(1))
...@@ -1059,12 +1061,12 @@ def checkStats(name, way, stats_file, range_fields): ...@@ -1059,12 +1061,12 @@ def checkStats(name, way, stats_file, range_fields):
deviation = round(((float(val) * 100)/ expected) - 100, 1) deviation = round(((float(val) * 100)/ expected) - 100, 1)
if val < lowerBound: if val < lowerBound:
print field, 'value is too low:' print(field, 'value is too low:')
print '(If this is because you have improved GHC, please' print('(If this is because you have improved GHC, please')
print 'update the test so that GHC doesn\'t regress again)' print('update the test so that GHC doesn\'t regress again)')
result = failBecause('stat too good') result = failBecause('stat too good')
if val > upperBound: if val > upperBound:
print field, 'value is too high:' print(field, 'value is too high:')
result = failBecause('stat not good enough') result = failBecause('stat not good enough')
if val < lowerBound or val > upperBound or config.verbose >= 4: if val < lowerBound or val > upperBound or config.verbose >= 4:
...@@ -1072,9 +1074,11 @@ def checkStats(name, way, stats_file, range_fields): ...@@ -1072,9 +1074,11 @@ def checkStats(name, way, stats_file, range_fields):
valLen = len(valStr) valLen = len(valStr)
expectedStr = str(expected) expectedStr = str(expected)
expectedLen = len(expectedStr) expectedLen = len(expectedStr)
length = max(map (lambda x : len(str(x)), [expected, lowerBound, upperBound, val])) length = max(len(str(x)) for x in [expected, lowerBound, upperBound, val])
def display(descr, val, extra): def display(descr, val, extra):
print descr, string.rjust(str(val), length), extra print(descr, str(val).rjust(length), extra)
display(' Expected ' + full_name + ' ' + field + ':', expected, '+/-' + str(dev) + '%') display(' Expected ' + full_name + ' ' + field + ':', expected, '+/-' + str(dev) + '%')
display(' Lower bound ' + full_name + ' ' + field + ':', lowerBound, '') display(' Lower bound ' + full_name + ' ' + field + ':', lowerBound, '')
display(' Upper bound ' + full_name + ' ' + field + ':', upperBound, '') display(' Upper bound ' + full_name + ' ' + field + ':', upperBound, '')
...@@ -1149,15 +1153,15 @@ def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf, ...@@ -1149,15 +1153,15 @@ def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf,
comp_flags = copy.copy(getTestOpts().compiler_always_flags) comp_flags = copy.copy(getTestOpts().compiler_always_flags)
if noforce: if noforce:
comp_flags = filter(lambda f: f != '-fforce-recomp', comp_flags) comp_flags = [f for f in comp_flags if f != '-fforce-recomp']
if getTestOpts().outputdir != None: if getTestOpts().outputdir != None:
comp_flags.extend(["-outputdir", getTestOpts().outputdir]) comp_flags.extend(["-outputdir", getTestOpts().outputdir])
cmd = 'cd ' + getTestOpts().testdir + " && " + cmd_prefix + "'" \ cmd = 'cd ' + getTestOpts().testdir + " && " + cmd_prefix + "'" \
+ config.compiler + "' " \ + config.compiler + "' " \
+ join(comp_flags,' ') + ' ' \ + ' '.join(comp_flags) + ' ' \
+ to_do + ' ' + srcname + ' ' \ + to_do + ' ' + srcname + ' ' \
+ join(config.way_flags(name)[way],' ') + ' ' \ + ' '.join(config.way_flags(name)[way]) + ' ' \
+ extra_hc_opts + ' ' \ + extra_hc_opts + ' ' \
+ opts.extra_hc_opts + ' ' \ + opts.extra_hc_opts + ' ' \
+ '>' + errname + ' 2>&1' + '>' + errname + ' 2>&1'
...@@ -1166,7 +1170,7 @@ def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf, ...@@ -1166,7 +1170,7 @@ def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf,
if result != 0 and not should_fail: if result != 0 and not should_fail: