directory 1.2.2.0 → 1.2.2.1
raw patch · 8 files changed
+4331/−81 lines, 8 filesbuild-type:Customsetup-changedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- Setup.hs +2/−0
- changelog.md +5/−0
- directory.cabal +5/−3
- tools/dispatch-tests.hs +12/−8
- tools/ghc-test-framework.LICENSE +44/−0
- tools/ghc-test-framework.shar +4253/−0
- tools/ghc.patch +0/−62
- tools/run-tests +10/−8
Setup.hs view
@@ -1,6 +1,8 @@ module Main (main) where import Distribution.Simple+import Distribution.Simple.Program (simpleProgram) main :: IO () main = defaultMainWithHooks autoconfUserHooks+ { hookedPrograms = [simpleProgram "python", simpleProgram "which"] }
changelog.md view
@@ -1,6 +1,11 @@ Changelog for the [`directory`][1] package ========================================== +## 1.2.2.1 (Apr 2015)++ * [#24](https://github.com/haskell/directory/issues/24):+ Fix dependency problem on NixOS when building with tests+ ## 1.2.2.0 (Mar 2015) * Bundled with GHC 7.10.1
directory.cabal view
@@ -1,5 +1,5 @@ name: directory-version: 1.2.2.0+version: 1.2.2.1 -- NOTE: Don't forget to update ./changelog.md license: BSD3 license-file: LICENSE@@ -10,7 +10,7 @@ This library provides a basic set of operations for manipulating files and directories in a portable way. category: System-build-type: Configure+build-type: Custom cabal-version: >= 1.10 tested-with: GHC>=7.4.1 @@ -45,7 +45,8 @@ tests/getPermissions001.stdout-i386-unknown-openbsd tests/getPermissions001.stdout-mingw tests/getPermissions001.stdout-x86_64-unknown-openbsd- tools/ghc.patch+ tools/ghc-test-framework.LICENSE+ tools/ghc-test-framework.shar tools/run-tests source-repository head@@ -86,6 +87,7 @@ other-extensions: CPP ForeignFunctionInterface ghc-options: -Wall hs-source-dirs: tools+ build-tools: python, which main-is: dispatch-tests.hs type: exitcode-stdio-1.0 build-depends: base, directory
tools/dispatch-tests.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} module Main (main) where+import Prelude (($), (.), (=<<), (==), Eq, IO, Int, String,+ id, fromIntegral, otherwise, unwords)+import Data.Functor ((<$>))+import Data.Monoid ((<>), mconcat) import Foreign (Ptr) import Foreign.C (CChar(..), CInt(..), withCString)-import Data.Functor ((<$>))-import System.Directory () -- to make sure `directory` is built beforehand+import System.Directory () -- to ensure `directory` is built beforehand import System.Environment (getArgs) import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith) @@ -19,7 +22,7 @@ ExitFailure _ -> [] args <- getArgs- let command : arguments = prefix ++ ["sh", "tools/run-tests"] ++ args+ let command : arguments = prefix <> ["sh", "tools/run-tests"] <> args exitWith =<< normalizeExitCode <$> rawSystem command arguments makeExitCode :: Int -> ExitCode@@ -48,15 +51,16 @@ -- the arcane quoting rules require us to add an extra set of quotes -- around the entire thing: see `help cmd` or look at -- https://superuser.com/a/238813- "\"" ++ unwords (quote <$> cmdArgs) ++ "\""- where quote s = "\"" ++ replaceElem '"' "\"\"" s ++ "\""+ let quote s = "\"" <> replaceElem '"' "\"\"" s <> "\""+ in (\ s -> "\"" <> s <> "\"") $ #else- unwords (quote <$> cmdArgs)- where quote s = "'" ++ replaceElem '\'' "'\\''" s ++ "'"+ let quote s = "'" <> replaceElem '\'' "'\\''" s <> "'"+ in id $ #endif+ unwords (quote <$> cmdArgs) replaceElem :: Eq a => a -> [a] -> [a] -> [a]-replaceElem match repl = concat . (replace <$>)+replaceElem match repl = mconcat . (replace <$>) where replace c | c == match = repl | otherwise = [c]
+ tools/ghc-test-framework.LICENSE view
@@ -0,0 +1,44 @@+The Glasgow Haskell Compiler License++Copyright 2002, The University Court of the University of Glasgow. +All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.+ +- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.+ +- Neither name of the University nor the names of its contributors may be+used to endorse or promote products derived from this software without+specific prior written permission. ++THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF+GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+DAMAGE.++================================================================================+The GHC testsuite contains several files under different licenses, explained+here:++ The following tests are under the GPLv3, or (at your option) any later version:++ - tests/indexed-types/should_compile/T3787.hs+ - tests/typecheck/should_compile/T4524.hs+ - tests/simplCore/should_run/T3591.hs++ You should have received a copy of the GNU General Public License along with+ the GHC testsuite, in LICENSE.GPL
+ tools/ghc-test-framework.shar view
@@ -0,0 +1,4253 @@+#!/bin/sh+# from ghc commit 6b96eeb72a17e35c59830952732170d29d99a598++mkdir -p 'ghc-test-framework/config'+sed "s/^ //" <<"EOF" >'ghc-test-framework/config/bad.ps'+ This is a bad postscript file+EOF+chmod 644 'ghc-test-framework/config/bad.ps'++mkdir -p 'ghc-test-framework/config'+sed "s/^ //" <<"EOF" >'ghc-test-framework/config/ghc'+ import os+ import re++ # Testsuite configuration setup for GHC+ #+ # This file is Python source+ #+ config.compiler_type = 'ghc'+ config.compiler = 'ghc'+ config.compiler_always_flags = ghc_compiler_always_flags.split()++ config.hp2ps = 'hp2ps'+ config.hpc = 'hpc'+ config.gs = 'gs'+ config.confdir = '.'++ # By default, the 'normal' and 'hpc' ways are enabled. In addition, certain+ # ways are enabled automatically if this GHC supports them. Ways that fall in+ # this group are 'optasm', 'optllvm', 'profasm', 'threaded1', 'threaded2',+ # 'profthreaded', 'ghci', and whichever of 'static/dyn' is not this GHC's+ # default mode. Other ways should be set explicitly from .T files.+ config.compile_ways = ['normal', 'hpc']+ config.run_ways = ['normal', 'hpc']++ # ways that are not enabled by default, but can always be invoked explicitly+ config.other_ways = ['prof',+ 'prof_hc_hb','prof_hb',+ 'prof_hd','prof_hy','prof_hr',+ 'threaded1_ls', 'threaded2_hT',+ 'llvm', 'debugllvm',+ 'profllvm', 'profoptllvm', 'profthreadedllvm',+ 'debug']++ if (ghc_with_native_codegen == 1):+ config.compile_ways.append('optasm')+ config.run_ways.append('optasm')++ config.compiler_debugged = ghc_debugged++ if (ghc_with_vanilla == 1):+ config.have_vanilla = True++ if (ghc_with_dynamic == 1):+ config.have_dynamic = True++ if (ghc_with_profiling == 1):+ config.have_profiling = True+ config.compile_ways.append('profasm')+ config.run_ways.append('profasm')++ if (ghc_with_interpreter == 1):+ config.have_interp = True+ config.run_ways.append('ghci')++ config.unregisterised = (ghc_unregisterised == 1)++ if (ghc_with_threaded_rts == 1):+ config.run_ways.append('threaded1')+ if (ghc_with_smp == 1):+ config.have_smp = True+ config.run_ways.append('threaded2')++ if (ghc_with_dynamic_rts == 1):+ config.have_shared_libs = True++ config.ghc_dynamic_by_default = ghc_dynamic_by_default+ if ghc_dynamic_by_default and ghc_with_vanilla == 1:+ config.run_ways.append('static')+ else:+ if (ghc_with_dynamic_rts == 1):+ config.run_ways.append('dyn')++ config.ghc_dynamic = ghc_dynamic++ if (ghc_with_profiling == 1 and ghc_with_threaded_rts == 1):+ config.run_ways.append('profthreaded')++ if (ghc_with_llvm == 1):+ config.compile_ways.append('optllvm')+ config.run_ways.append('optllvm')++ config.in_tree_compiler = in_tree_compiler+ config.clean_only = clean_only++ config.way_flags = lambda name : {+ 'normal' : [],+ 'g1' : [],+ 'optasm' : ['-O', '-fasm'],+ 'llvm' : ['-fllvm'],+ 'optllvm' : ['-O', '-fllvm'],+ 'debugllvm' : ['-fllvm', '-keep-llvm-files'],+ 'prof' : ['-prof', '-static', '-auto-all', '-fasm'],+ 'profasm' : ['-O', '-prof', '-static', '-auto-all'],+ 'profthreaded' : ['-O', '-prof', '-static', '-auto-all', '-threaded'],+ 'ghci' : ['--interactive', '-v0', '-ignore-dot-ghci', '+RTS', '-I0.1', '-RTS'],+ 'threaded1' : ['-threaded', '-debug'],+ 'threaded1_ls' : ['-threaded', '-debug'],+ 'threaded2' : ['-O', '-threaded', '-eventlog'],+ 'threaded2_hT' : ['-O', '-threaded'],+ 'hpc' : ['-O', '-fhpc', '-hpcdir', '.hpc.' + name ],+ 'prof_hc_hb' : ['-O', '-prof', '-static', '-auto-all'],+ 'prof_hb' : ['-O', '-prof', '-static', '-auto-all'],+ 'prof_hd' : ['-O', '-prof', '-static', '-auto-all'],+ 'prof_hy' : ['-O', '-prof', '-static', '-auto-all'],+ 'prof_hr' : ['-O', '-prof', '-static', '-auto-all'],+ 'dyn' : ['-O', '-dynamic'],+ 'static' : ['-O', '-static'],+ 'debug' : ['-O', '-g', '-dannot-lint'],+ # llvm variants...+ 'profllvm' : ['-prof', '-static', '-auto-all', '-fllvm'],+ 'profoptllvm' : ['-O', '-prof', '-static', '-auto-all', '-fllvm'],+ 'profthreadedllvm' : ['-O', '-prof', '-static', '-auto-all', '-threaded', '-fllvm'],+ }++ config.way_rts_flags = { + 'normal' : [],+ 'g1' : ['-G1'],+ 'optasm' : [],+ 'llvm' : [],+ 'optllvm' : [],+ 'debugllvm' : [],+ 'prof' : ['-p'],+ 'profasm' : ['-hc', '-p'], # test heap profiling too+ 'profthreaded' : ['-p'],+ 'ghci' : [],+ 'threaded1' : [],+ 'threaded1_ls' : ['-ls'],+ 'threaded2' : ['-N2 -ls'],+ 'threaded2_hT' : ['-N2', '-hT'],+ 'hpc' : [],+ 'prof_hc_hb' : ['-hc -hbvoid'],+ 'prof_hb' : ['-hb'],+ 'prof_hd' : ['-hd'],+ 'prof_hy' : ['-hy'],+ 'prof_hr' : ['-hr'],+ 'dyn' : [],+ 'static' : [],+ 'debug' : [],+ # llvm variants...+ 'profllvm' : ['-p'],+ 'profoptllvm' : ['-hc', '-p'],+ 'profthreadedllvm' : ['-p'],+ }++ # Useful classes of ways that can be used with only_ways() and+ # expect_broken_for().++ prof_ways = [x[0] for x in config.way_flags('dummy_name').items()+ if '-prof' in x[1]]++ threaded_ways = [x[0] for x in config.way_flags('dummy_name').items()+ if '-threaded' in x[1] or 'ghci' == x[0]]++ opt_ways = [x[0] for x in config.way_flags('dummy_name').items()+ if '-O' in x[1]]++ llvm_ways = [x[0] for x in config.way_flags('dummy_name').items()+ if '-fflvm' in x[1]]++ def get_compiler_info():+ # This should really not go through the shell+ h = os.popen(config.compiler + ' --info', 'r')+ s = h.read()+ s = re.sub('[\r\n]', '', s)+ h.close()+ compilerInfoDict = dict(eval(s))+ h = os.popen(config.compiler + ' +RTS --info', 'r')+ s = h.read()+ s = re.sub('[\r\n]', '', s)+ h.close()+ rtsInfoDict = dict(eval(s))++ # We use a '/'-separated path for libdir, even on Windows+ config.libdir = re.sub('\\\\','/',compilerInfoDict['LibDir'])++ v = compilerInfoDict["Project version"].split('-')+ config.compiler_version = v[0]+ config.compiler_maj_version = re.sub('^([0-9]+\.[0-9]+).*',r'\1', v[0])+ config.compiler_tags = v[1:]++ # -fno-ghci-history was added in 7.3+ if version_ge(config.compiler_version, '7.3'):+ config.compiler_always_flags = \+ config.compiler_always_flags + ['-fno-ghci-history']++ if re.match(".*_p(_.*|$)", rtsInfoDict["RTS way"]):+ config.compiler_profiled = True+ config.run_ways = [x for x in config.run_ways if x != 'ghci']+ else:+ config.compiler_profiled = False++ try:+ config.package_conf_cache_file = compilerInfoDict["Global Package DB"] + '/package.cache'+ except:+ config.package_conf_cache_file = ''++ try:+ if compilerInfoDict["GHC Dynamic"] == "YES":+ ghcDynamic = True+ elif compilerInfoDict["GHC Dynamic"] == "NO":+ ghcDynamic = False+ else:+ raise 'Bad value for "GHC Dynamic"'+ except KeyError:+ # GHC < 7.7 doesn't have a "GHC Dynamic" field+ ghcDynamic = False++ if ghcDynamic:+ config.ghc_th_way_flags = "-dynamic"+ config.ghci_way_flags = "-dynamic"+ config.ghc_th_way = "dyn"+ config.ghc_plugin_way = "dyn"+ else:+ config.ghc_th_way_flags = "-static"+ config.ghci_way_flags = "-static"+ config.ghc_th_way = "normal"+ config.ghc_plugin_way = "normal"++EOF+chmod 644 'ghc-test-framework/config/ghc'++mkdir -p 'ghc-test-framework/config'+sed "s/^ //" <<"EOF" >'ghc-test-framework/config/good.ps'+ % this is a good postscript file+EOF+chmod 644 'ghc-test-framework/config/good.ps'++mkdir -p 'ghc-test-framework/driver'+sed "s/^ //" <<"EOF" >'ghc-test-framework/driver/runtests.py'+ # + # (c) Simon Marlow 2002+ #++ from __future__ import print_function++ import sys+ import os+ import string+ import getopt+ import platform+ import time+ import re++ # We don't actually need subprocess in runtests.py, but:+ # * We do need it in testlibs.py+ # * We can't import testlibs.py until after we have imported ctypes+ # * If we import ctypes before subprocess on cygwin, then sys.exit(0)+ # says "Aborted" and we fail with exit code 134.+ # So we import it here first, so that the testsuite doesn't appear to fail.+ try:+ import subprocess+ except:+ 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 testglobals import *++ # Readline sometimes spews out ANSI escapes for some values of TERM,+ # which result in test failures. Thus set TERM to a nice, simple, safe+ # value.+ os.environ['TERM'] = 'vt100'++ global config+ config = getConfig() # get it from testglobals++ # -----------------------------------------------------------------------------+ # cmd-line options++ long_options = [+ "config=", # config file+ "rootdir=", # root of tree containing tests (default: .)+ "output-summary=", # file in which to save the (human-readable) summary+ "only=", # just this test (can be give multiple --only= flags)+ "way=", # just this way+ "skipway=", # skip this way+ "threads=", # threads to run simultaneously+ "check-files-written", # check files aren't written by multiple tests+ "verbose=", # verbose (0,1,2 so far)+ "skip-perf-tests", # skip performance tests+ ]++ opts, args = getopt.getopt(sys.argv[1:], "e:", long_options)+ + for opt,arg in opts:+ if opt == '--config':+ exec(open(arg).read())++ # -e is a string to execute from the command line. For example:+ # testframe -e 'config.compiler=ghc-5.04'+ if opt == '-e':+ exec(arg)++ if opt == '--rootdir':+ config.rootdirs.append(arg)++ if opt == '--output-summary':+ config.output_summary = arg++ if opt == '--only':+ config.only.append(arg)++ if opt == '--way':+ if (arg not in config.run_ways and arg not in config.compile_ways and arg not in config.other_ways):+ sys.stderr.write("ERROR: requested way \'" ++ arg + "\' does not exist\n")+ sys.exit(1)+ config.cmdline_ways = [arg] + config.cmdline_ways+ if (arg in config.other_ways):+ config.run_ways = [arg] + config.run_ways+ config.compile_ways = [arg] + config.compile_ways++ if opt == '--skipway':+ if (arg not in config.run_ways and arg not in config.compile_ways and arg not in config.other_ways):+ sys.stderr.write("ERROR: requested way \'" ++ arg + "\' does not exist\n")+ sys.exit(1)+ config.other_ways = [w for w in config.other_ways if w != arg]+ config.run_ways = [w for w in config.run_ways if w != arg]+ config.compile_ways = [w for w in config.compile_ways if w != arg]++ if opt == '--threads':+ config.threads = int(arg)+ config.use_threads = 1++ if opt == '--check-files-written':+ config.check_files_written = True++ if opt == '--skip-perf-tests':+ config.skip_perf_tests = True++ if opt == '--verbose':+ if arg not in ["0","1","2","3","4"]:+ sys.stderr.write("ERROR: requested verbosity %s not supported, use 0,1,2,3 or 4" % arg)+ sys.exit(1)+ config.verbose = int(arg)+++ if config.use_threads == 1:+ # Trac #1558 says threads don't work in python 2.4.4, but do+ # in 2.5.2. Probably >= 2.5 is sufficient, but let's be+ # conservative here.+ # Some versions of python have things like '1c1' for some of+ # these components (see trac #3091), but int() chokes on the+ # 'c1', so we drop it.+ (maj, min, pat) = platform.python_version_tuple()+ # We wrap maj, min, and pat in str() to work around a bug in python+ # 2.6.1+ maj = int(re.sub('[^0-9].*', '', str(maj)))+ min = int(re.sub('[^0-9].*', '', str(min)))+ pat = int(re.sub('[^0-9].*', '', str(pat)))+ if (maj, min) < (2, 6):+ print("Python < 2.6 is not supported")+ sys.exit(1)+ # We also need to disable threads for python 2.7.2, because of+ # this bug: http://bugs.python.org/issue13817+ elif (maj, min, pat) == (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.")+ config.use_threads = 0+ if windows:+ print("Warning: Ignoring request to use threads as running on Windows")+ config.use_threads = 0++ config.cygwin = False+ config.msys = False++ if windows:+ h = os.popen('uname -s', 'r')+ v = h.read()+ h.close()+ if v.startswith("CYGWIN"):+ config.cygwin = True+ elif v.startswith("MINGW") or v.startswith("MSYS"):+ # msys gives "MINGW32"+ # msys2 gives "MINGW_NT-6.2" or "MSYS_NT-6.3"+ config.msys = True+ else:+ raise Exception("Can't detect Windows terminal type")++ # Try to use UTF8+ if windows:+ import ctypes+ # Windows Python provides windll, mingw python provides cdll.+ if hasattr(ctypes, 'windll'):+ mydll = ctypes.windll+ else:+ mydll = ctypes.cdll++ # This actually leaves the terminal in codepage 65001 (UTF8) even+ # after python terminates. We ought really remember the old codepage+ # and set it back.+ if mydll.kernel32.SetConsoleCP(65001) == 0:+ raise Exception("Failure calling SetConsoleCP(65001)")+ if mydll.kernel32.SetConsoleOutputCP(65001) == 0:+ raise Exception("Failure calling SetConsoleOutputCP(65001)")+ else:+ # Try and find a utf8 locale to use+ # First see if we already have a UTF8 locale+ h = os.popen('locale | grep LC_CTYPE | grep -i utf', 'r')+ v = h.read()+ h.close()+ if v == '':+ # We don't, so now see if 'locale -a' works+ h = os.popen('locale -a', 'r')+ v = h.read()+ h.close()+ if v != '':+ # If it does then use the first utf8 locale that is available+ h = os.popen('locale -a | grep -i "utf8\|utf-8" 2>/dev/null', 'r')+ v = h.readline().strip()+ h.close()+ if v != '':+ os.environ['LC_ALL'] = v+ print("setting LC_ALL to", v)+ else:+ print('WARNING: No UTF8 locale found.')+ print('You may get some spurious test failures.')++ # This has to come after arg parsing as the args can change the compiler+ get_compiler_info()++ # Can't import this earlier as we need to know if threading will be+ # enabled or not+ from testlib import *++ # On Windows we need to set $PATH to include the paths to all the DLLs+ # in order for the dynamic library tests to work.+ if windows or darwin:+ pkginfo = getStdout([config.ghc_pkg, 'dump'])+ topdir = config.libdir+ for line in pkginfo.split('\n'):+ if line.startswith('library-dirs:'):+ path = line.rstrip()+ path = re.sub('^library-dirs: ', '', path)+ path = re.sub('\\$topdir', topdir, path)+ if path.startswith('"'):+ path = re.sub('^"(.*)"$', '\\1', path)+ path = re.sub('\\\\(.)', '\\1', path)+ if windows:+ if config.cygwin:+ # On cygwin we can't put "c:\foo" in $PATH, as : is a+ # field separator. So convert to /cygdrive/c/foo instead.+ # Other pythons use ; as the separator, so no problem.+ path = re.sub('([a-zA-Z]):', '/cygdrive/\\1', path)+ path = re.sub('\\\\', '/', path)+ os.environ['PATH'] = os.pathsep.join([path, os.environ.get("PATH", "")])+ else:+ # darwin+ os.environ['DYLD_LIBRARY_PATH'] = os.pathsep.join([path, os.environ.get("DYLD_LIBRARY_PATH", "")])++ global testopts_local+ testopts_local.x = TestOptions()++ if config.use_threads:+ t.lock = threading.Lock()+ t.thread_pool = threading.Condition(t.lock)+ t.lockFilesWritten = threading.Lock()+ t.running_threads = 0++ # if timeout == -1 then we try to calculate a sensible value+ if config.timeout == -1:+ config.timeout = int(read_no_crs(config.top + '/timeout/calibrate.out'))++ print('Timeout is ' + str(config.timeout))++ # -----------------------------------------------------------------------------+ # The main dude++ if config.rootdirs == []:+ config.rootdirs = ['.']++ t_files = findTFiles(config.rootdirs)++ print('Found', len(t_files), '.T files...')++ t = getTestRun()++ # Avoid cmd.exe built-in 'date' command on Windows+ t.start_time = time.localtime()++ print('Beginning test run at', time.strftime("%c %Z",t.start_time))++ sys.stdout.flush()+ 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+ for file in t_files:+ if_verbose(2, '====> Scanning %s' % file)+ newTestDir(os.path.dirname(file))+ try:+ exec(open(file).read())+ except Exception:+ print('*** framework failure: found an error while executing ', file, ':')+ t.n_framework_failures = t.n_framework_failures + 1+ traceback.print_exc()++ if config.list_broken:+ global brokens+ print('')+ print('Broken tests:')+ print(' '.join(map (lambda bdn: '#' + str(bdn[0]) + '(' + bdn[1] + '/' + bdn[2] + ')', brokens)))+ print('')++ if t.n_framework_failures != 0:+ print('WARNING:', str(t.n_framework_failures), 'framework failures!')+ print('')+ else:+ # Now run all the tests+ if config.use_threads:+ t.running_threads=0+ for oneTest in parallelTests:+ if stopping():+ break+ oneTest()+ if config.use_threads:+ t.thread_pool.acquire()+ while t.running_threads>0:+ t.thread_pool.wait()+ t.thread_pool.release()+ config.use_threads = False+ for oneTest in aloneTests:+ if stopping():+ break+ oneTest()+ + summary(t, sys.stdout)++ if config.output_summary != '':+ summary(t, open(config.output_summary, 'w'))++ sys.exit(0)++EOF+chmod 644 'ghc-test-framework/driver/runtests.py'++mkdir -p 'ghc-test-framework/driver'+sed "s/^ //" <<"EOF" >'ghc-test-framework/driver/testglobals.py'+ # + # (c) Simon Marlow 2002+ #++ # -----------------------------------------------------------------------------+ # Configuration info++ # There is a single global instance of this structure, stored in the+ # variable config below. The fields of the structure are filled in by+ # the appropriate config script(s) for this compiler/platform, in+ # ../config.+ # + # Bits of the structure may also be filled in from the command line,+ # via the build system, using the '-e' option to runtests.++ class TestConfig:+ def __init__(self):++ # Where the testsuite root is+ self.top = ''++ # Directories below which to look for test description files (foo.T)+ self.rootdirs = []++ # Run these tests only (run all tests if empty)+ self.only = []++ # Accept new output which differs from the sample?+ self.accept = 0++ # File in which to save the summary+ self.output_summary = ''++ # File in which to save the times+ self.times_file = ''++ # What platform are we running on?+ self.platform = ''+ self.os = ''+ self.arch = ''++ # What is the wordsize (in bits) of this platform?+ self.wordsize = ''++ # Verbosity level+ self.verbose = 3++ # run the "fast" version of the test suite+ self.fast = 0++ self.list_broken = False++ # Compiler type (ghc, hugs, nhc, etc.)+ self.compiler_type = ''++ # Path to the compiler+ self.compiler = ''+ # and ghc-pkg+ self.ghc_pkg = ''++ # Compiler version info+ self.compiler_version = ''+ self.compiler_maj_version = ''+ self.compiler_tags = []++ # Flags we always give to this compiler+ self.compiler_always_flags = []+ + # Which ways to run tests (when compiling and running respectively)+ # Other ways are added from the command line if we have the appropriate+ # libraries.+ self.compile_ways = []+ self.run_ways = []+ self.other_ways = []++ # The ways selected via the command line.+ self.cmdline_ways = []++ # Lists of flags for each way+ self.way_flags = {}+ self.way_rts_flags = {}++ # Do we have vanilla libraries?+ self.have_vanilla = False++ # Do we have dynamic libraries?+ self.have_dynamic = False++ # Do we have profiling support?+ self.have_profiling = False++ # Do we have interpreter support?+ self.have_interp = False++ # Do we have shared libraries?+ self.have_shared_libs = False++ # Do we have SMP support?+ self.have_smp = False++ # Are we testing an in-tree compiler?+ self.in_tree_compiler = True++ # the timeout program+ self.timeout_prog = ''+ self.timeout = 300+ + # threads+ self.threads = 1+ self.use_threads = 0++ # Should we check for files being written more than once?+ self.check_files_written = False++ # Should we skip performance tests+ self.skip_perf_tests = False++ global config+ config = TestConfig()++ def getConfig():+ return config++ # -----------------------------------------------------------------------------+ # Information about the current test run++ class TestRun:+ def __init__(self):+ self.start_time = None+ self.total_tests = 0+ self.total_test_cases = 0+ self.n_framework_failures = 0+ self.framework_failures = {}+ self.n_tests_skipped = 0+ self.tests_skipped = {}+ self.n_expected_passes = 0+ self.expected_passes = {}+ self.n_expected_failures = 0+ self.expected_failures = {}+ self.n_missing_libs = 0+ self.missing_libs = {}+ self.n_unexpected_passes = 0+ self.unexpected_passes = {}+ self.n_unexpected_failures = 0+ self.unexpected_failures = {}+ self.n_unexpected_stat_failures = 0+ self.unexpected_stat_failures = {}+ + global t+ t = TestRun()++ def getTestRun():+ return t++ # -----------------------------------------------------------------------------+ # Information about the current test++ class TestOptions:+ def __init__(self):+ # if not None then we look for namebase.stderr etc rather than+ # using the test name+ self.with_namebase = None++ # skip this test?+ self.skip = 0++ # skip these ways+ self.omit_ways = []++ # skip all ways except these (None == do all ways)+ self.only_ways = None++ # add these ways to the default set+ self.extra_ways = []++ # the result we normally expect for this test+ self.expect = 'pass'++ # override the expected result for certain ways+ self.expect_fail_for = []++ # the stdin file that this test will use (empty for <name>.stdin)+ self.stdin = ''++ # don't compare output+ self.ignore_output = 0++ # don't give anything as stdin+ self.no_stdin = 0++ # compile this test to .hc only+ self.compile_to_hc = 0++ # We sometimes want to modify the compiler_always_flags, so+ # they are copied from config.compiler_always_flags when we+ # make a new instance of TestOptions.+ self.compiler_always_flags = []++ # extra compiler opts for this test+ self.extra_hc_opts = ''++ # extra run opts for this test+ self.extra_run_opts = ''++ # expected exit code+ self.exit_code = 0++ # should we clean up after ourselves?+ self.cleanup = ''++ # extra files to clean afterward+ self.clean_files = []++ # which -t numeric fields do we want to look at, and what bounds must+ # they fall within?+ # Elements of these lists should be things like+ # ('bytes allocated',+ # 9300000000,+ # 10)+ # To allow a 10% deviation from 9300000000.+ self.compiler_stats_range_fields = {}+ self.stats_range_fields = {}++ # should we run this test alone, i.e. not run it in parallel with+ # any other threads+ self.alone = False++ # Does this test use a literate (.lhs) file?+ self.literate = 0++ # Does this test use a .c, .m or .mm file?+ self.c_src = 0+ self.objc_src = 0+ self.objcpp_src = 0++ # Does this test use a .cmm file?+ self.cmm_src = 0++ # Should we put .hi/.o files in a subdirectory?+ self.outputdir = None++ # Command to run before the test+ self.pre_cmd = None++ # Command to run for extra cleaning+ self.clean_cmd = None++ # Command wrapper: a function to apply to the command before running it+ self.cmd_wrapper = None++ # Prefix to put on the command before compiling it+ self.compile_cmd_prefix = ''++ # Extra output normalisation+ self.extra_normaliser = lambda x: x++ # Custom output checker, otherwise do a comparison with expected+ # stdout file. Accepts two arguments: filename of actual stdout+ # output, and a normaliser function given other test options+ self.check_stdout = None++ # Extra normalisation for compiler error messages+ self.extra_errmsg_normaliser = lambda x: x++ # The directory the test is in+ self.testdir = '.'++ # Should we redirect stdout and stderr to a single file?+ self.combined_output = False++ # How should the timeout be adjusted on this test?+ self.timeout_multiplier = 1.0++ # The default set of options+ global default_testopts+ default_testopts = TestOptions()++ # (bug, directory, name) of tests marked broken+ global brokens+ brokens = []++EOF+chmod 644 'ghc-test-framework/driver/testglobals.py'++mkdir -p 'ghc-test-framework/driver'+sed "s/^ //" <<"EOF" >'ghc-test-framework/driver/testlib.py'+ #+ # (c) Simon Marlow 2002+ #++ from __future__ import print_function++ import shutil+ import sys+ import os+ import errno+ import string+ import re+ import traceback+ import time+ import datetime+ import copy+ import glob+ from math import ceil, trunc+ import collections++ have_subprocess = False+ try:+ import subprocess+ have_subprocess = True+ except:+ print("Warning: subprocess not found, will fall back to spawnv")++ from testglobals import *+ from testutil import *++ if config.use_threads:+ import threading+ try:+ import thread+ except ImportError: # Python 3+ import _thread as thread++ global wantToStop+ wantToStop = False+ def stopNow():+ global wantToStop+ wantToStop = True+ def stopping():+ return wantToStop++ # Options valid for the current test only (these get reset to+ # testdir_testopts after each test).++ global testopts_local+ if config.use_threads:+ testopts_local = threading.local()+ else:+ class TestOpts_Local:+ pass+ testopts_local = TestOpts_Local()++ def getTestOpts():+ return testopts_local.x++ def setLocalTestOpts(opts):+ global testopts_local+ testopts_local.x=opts++ def isStatsTest():+ opts = getTestOpts()+ return len(opts.compiler_stats_range_fields) > 0 or len(opts.stats_range_fields) > 0+++ # This can be called at the top of a file of tests, to set default test options+ # for the following tests.+ def setTestOpts( f ):+ global thisdir_settings+ thisdir_settings = [thisdir_settings, f]++ # -----------------------------------------------------------------------------+ # Canned setup functions for common cases. eg. for a test you might say+ #+ # test('test001', normal, compile, [''])+ #+ # to run it without any options, but change it to+ #+ # test('test001', expect_fail, compile, [''])+ #+ # to expect failure for this test.++ def normal( name, opts ):+ return;++ def skip( name, opts ):+ opts.skip = 1++ def expect_fail( name, opts ):+ opts.expect = 'fail';++ def reqlib( lib ):+ return lambda name, opts, l=lib: _reqlib (name, opts, l )++ # Cache the results of looking to see if we have a library or not.+ # This makes quite a difference, especially on Windows.+ have_lib = {}++ def _reqlib( name, opts, lib ):+ if lib in have_lib:+ got_it = have_lib[lib]+ else:+ if have_subprocess:+ # By preference we use subprocess, as the alternative uses+ # /dev/null which mingw doesn't have.+ cmd = strip_quotes(config.ghc_pkg)+ p = subprocess.Popen([cmd, '--no-user-package-db', 'describe', lib],+ stdout=subprocess.PIPE,+ stderr=subprocess.PIPE)+ # read from stdout and stderr to avoid blocking due to+ # buffers filling+ p.communicate()+ r = p.wait()+ else:+ r = os.system(config.ghc_pkg + ' --no-user-package-db describe '+ + lib + ' > /dev/null 2> /dev/null')+ got_it = r == 0+ have_lib[lib] = got_it++ if not got_it:+ opts.expect = 'missing-lib'++ def req_profiling( name, opts ):+ if not config.have_profiling:+ opts.expect = 'fail'++ def req_shared_libs( name, opts ):+ if not config.have_shared_libs:+ opts.expect = 'fail'++ def req_interp( name, opts ):+ if not config.have_interp:+ opts.expect = 'fail'++ def req_smp( name, opts ):+ if not config.have_smp:+ opts.expect = 'fail'++ def ignore_output( name, opts ):+ opts.ignore_output = 1++ def no_stdin( name, opts ):+ opts.no_stdin = 1++ def combined_output( name, opts ):+ opts.combined_output = True++ # -----++ def expect_fail_for( ways ):+ return lambda name, opts, w=ways: _expect_fail_for( name, opts, w )++ def _expect_fail_for( name, opts, ways ):+ opts.expect_fail_for = ways++ def expect_broken( bug ):+ return lambda name, opts, b=bug: _expect_broken (name, opts, b )++ def _expect_broken( name, opts, bug ):+ record_broken(name, opts, bug)+ opts.expect = 'fail';++ def expect_broken_for( bug, ways ):+ return lambda name, opts, b=bug, w=ways: _expect_broken_for( name, opts, b, w )++ def _expect_broken_for( name, opts, bug, ways ):+ record_broken(name, opts, bug)+ opts.expect_fail_for = ways++ def record_broken(name, opts, bug):+ global brokens+ me = (bug, opts.testdir, name)+ if not me in brokens:+ brokens.append(me)++ # -----++ def omit_ways( ways ):+ return lambda name, opts, w=ways: _omit_ways( name, opts, w )++ def _omit_ways( name, opts, ways ):+ opts.omit_ways = ways++ # -----++ def only_ways( ways ):+ return lambda name, opts, w=ways: _only_ways( name, opts, w )++ def _only_ways( name, opts, ways ):+ opts.only_ways = ways++ # -----++ def extra_ways( ways ):+ return lambda name, opts, w=ways: _extra_ways( name, opts, w )++ def _extra_ways( name, opts, ways ):+ opts.extra_ways = ways++ # -----++ def omit_compiler_types( compiler_types ):+ return lambda name, opts, c=compiler_types: _omit_compiler_types(name, opts, c)++ def _omit_compiler_types( name, opts, compiler_types ):+ if config.compiler_type in compiler_types:+ opts.skip = 1++ # -----++ def only_compiler_types( compiler_types ):+ return lambda name, opts, c=compiler_types: _only_compiler_types(name, opts, c)++ def _only_compiler_types( name, opts, compiler_types ):+ if config.compiler_type not in compiler_types:+ opts.skip = 1++ # -----++ def set_stdin( file ):+ return lambda name, opts, f=file: _set_stdin(name, opts, f);++ def _set_stdin( name, opts, f ):+ opts.stdin = f++ # -----++ def exit_code( val ):+ return lambda name, opts, v=val: _exit_code(name, opts, v);++ def _exit_code( name, opts, v ):+ opts.exit_code = v++ def signal_exit_code( val ):+ if opsys('solaris2'):+ return exit_code( val );+ else:+ # When application running on Linux receives fatal error+ # signal, then its exit code is encoded as 128 + signal+ # value. See http://www.tldp.org/LDP/abs/html/exitcodes.html+ # I assume that Mac OS X behaves in the same way at least Mac+ # OS X builder behavior suggests this.+ return exit_code( val+128 );++ # -----++ def timeout_multiplier( val ):+ return lambda name, opts, v=val: _timeout_multiplier(name, opts, v)++ def _timeout_multiplier( name, opts, v ):+ opts.timeout_multiplier = v++ # -----++ def extra_run_opts( val ):+ return lambda name, opts, v=val: _extra_run_opts(name, opts, v);++ def _extra_run_opts( name, opts, v ):+ opts.extra_run_opts = v++ # -----++ def extra_hc_opts( val ):+ return lambda name, opts, v=val: _extra_hc_opts(name, opts, v);++ def _extra_hc_opts( name, opts, v ):+ opts.extra_hc_opts = v++ # -----++ def extra_clean( files ):+ return lambda name, opts, v=files: _extra_clean(name, opts, v);++ def _extra_clean( name, opts, v ):+ opts.clean_files = v++ # -----++ def stats_num_field( field, expecteds ):+ return lambda name, opts, f=field, e=expecteds: _stats_num_field(name, opts, f, e);++ def _stats_num_field( name, opts, field, expecteds ):+ if field in opts.stats_range_fields:+ framework_fail(name, 'duplicate-numfield', 'Duplicate ' + field + ' num_field check')++ if type(expecteds) is list:+ for (b, expected, dev) in expecteds:+ if b:+ opts.stats_range_fields[field] = (expected, dev)+ return+ framework_fail(name, 'numfield-no-expected', 'No expected value found for ' + field + ' in num_field check')++ else:+ (expected, dev) = expecteds+ opts.stats_range_fields[field] = (expected, dev)++ def compiler_stats_num_field( field, expecteds ):+ return lambda name, opts, f=field, e=expecteds: _compiler_stats_num_field(name, opts, f, e);++ def _compiler_stats_num_field( name, opts, field, expecteds ):+ if field in opts.compiler_stats_range_fields:+ framework_fail(name, 'duplicate-numfield', 'Duplicate ' + field + ' num_field check')++ # Compiler performance numbers change when debugging is on, making the results+ # useless and confusing. Therefore, skip if debugging is on.+ if compiler_debugged():+ skip(name, opts)++ for (b, expected, dev) in expecteds:+ if b:+ opts.compiler_stats_range_fields[field] = (expected, dev)+ return++ framework_fail(name, 'numfield-no-expected', 'No expected value found for ' + field + ' in num_field check')++ # -----++ def when(b, f):+ # When list_brokens is on, we want to see all expect_broken calls,+ # so we always do f+ if b or config.list_broken:+ return f+ else:+ return normal++ def unless(b, f):+ return when(not b, f)++ def doing_ghci():+ return 'ghci' in config.run_ways++ def ghci_dynamic( ):+ return config.ghc_dynamic++ def fast():+ return config.fast++ def platform( plat ):+ return config.platform == plat++ def opsys( os ):+ return config.os == os++ def arch( arch ):+ return config.arch == arch++ def wordsize( ws ):+ return config.wordsize == str(ws)++ def msys( ):+ return config.msys++ def cygwin( ):+ return config.cygwin++ def have_vanilla( ):+ return config.have_vanilla++ def have_dynamic( ):+ return config.have_dynamic++ def have_profiling( ):+ return config.have_profiling++ def in_tree_compiler( ):+ return config.in_tree_compiler++ def compiler_type( compiler ):+ return config.compiler_type == compiler++ def compiler_lt( compiler, version ):+ return config.compiler_type == compiler and \+ version_lt(config.compiler_version, version)++ def compiler_le( compiler, version ):+ return config.compiler_type == compiler and \+ version_le(config.compiler_version, version)++ def compiler_gt( compiler, version ):+ return config.compiler_type == compiler and \+ version_gt(config.compiler_version, version)++ def compiler_ge( compiler, version ):+ return config.compiler_type == compiler and \+ version_ge(config.compiler_version, version)++ def unregisterised( ):+ return config.unregisterised++ def compiler_profiled( ):+ return config.compiler_profiled++ def compiler_debugged( ):+ return config.compiler_debugged++ def tag( t ):+ return t in config.compiler_tags++ # ---++ def namebase( nb ):+ return lambda opts, nb=nb: _namebase(opts, nb)++ def _namebase( opts, nb ):+ opts.with_namebase = nb++ # ---++ def high_memory_usage(name, opts):+ opts.alone = True++ # If a test is for a multi-CPU race, then running the test alone+ # increases the chance that we'll actually see it.+ def multi_cpu_race(name, opts):+ opts.alone = True++ # ---+ def literate( name, opts ):+ opts.literate = 1;++ def c_src( name, opts ):+ opts.c_src = 1;++ def objc_src( name, opts ):+ opts.objc_src = 1;++ def objcpp_src( name, opts ):+ opts.objcpp_src = 1;++ def cmm_src( name, opts ):+ opts.cmm_src = 1;++ def outputdir( odir ):+ return lambda name, opts, d=odir: _outputdir(name, opts, d)++ def _outputdir( name, opts, odir ):+ opts.outputdir = odir;++ # ----++ def pre_cmd( cmd ):+ return lambda name, opts, c=cmd: _pre_cmd(name, opts, cmd)++ def _pre_cmd( name, opts, cmd ):+ opts.pre_cmd = cmd++ # ----++ def clean_cmd( cmd ):+ return lambda name, opts, c=cmd: _clean_cmd(name, opts, cmd)++ def _clean_cmd( name, opts, cmd ):+ opts.clean_cmd = cmd++ # ----++ def cmd_prefix( prefix ):+ return lambda name, opts, p=prefix: _cmd_prefix(name, opts, prefix)++ def _cmd_prefix( name, opts, prefix ):+ opts.cmd_wrapper = lambda cmd, p=prefix: p + ' ' + cmd;++ # ----++ def cmd_wrapper( fun ):+ return lambda name, opts, f=fun: _cmd_wrapper(name, opts, fun)++ def _cmd_wrapper( name, opts, fun ):+ opts.cmd_wrapper = fun++ # ----++ def compile_cmd_prefix( prefix ):+ return lambda name, opts, p=prefix: _compile_cmd_prefix(name, opts, prefix)++ def _compile_cmd_prefix( name, opts, prefix ):+ opts.compile_cmd_prefix = prefix++ # ----++ def check_stdout( f ):+ return lambda name, opts, f=f: _check_stdout(name, opts, f)++ def _check_stdout( name, opts, f ):+ opts.check_stdout = f++ # ----++ def normalise_slashes( name, opts ):+ _normalise_fun(name, opts, normalise_slashes_)++ def normalise_exe( name, opts ):+ _normalise_fun(name, opts, normalise_exe_)++ def normalise_fun( *fs ):+ return lambda name, opts: _normalise_fun(name, opts, fs)++ def _normalise_fun( name, opts, *fs ):+ opts.extra_normaliser = join_normalisers(opts.extra_normaliser, fs)++ def normalise_errmsg_fun( *fs ):+ return lambda name, opts: _normalise_errmsg_fun(name, opts, fs)++ def _normalise_errmsg_fun( name, opts, *fs ):+ opts.extra_errmsg_normaliser = join_normalisers(opts.extra_errmsg_normaliser, fs)++ def normalise_version_( *pkgs ):+ def normalise_version__( str ):+ return re.sub('(' + '|'.join(map(re.escape,pkgs)) + ')-[0-9.]+',+ '\\1-<VERSION>', str)+ return normalise_version__++ def normalise_version( *pkgs ):+ def normalise_version__( name, opts ):+ _normalise_fun(name, opts, normalise_version_(*pkgs))+ _normalise_errmsg_fun(name, opts, normalise_version_(*pkgs))+ return normalise_version__++ def join_normalisers(*a):+ """+ Compose functions, flattening sequences.++ join_normalisers(f1,[f2,f3],f4)++ is the same as++ lambda x: f1(f2(f3(f4(x))))+ """++ def flatten(l):+ """+ Taken from http://stackoverflow.com/a/2158532/946226+ """+ for el in l:+ if isinstance(el, collections.Iterable) and not isinstance(el, basestring):+ for sub in flatten(el):+ yield sub+ else:+ yield el++ a = flatten(a)++ fn = lambda x:x # identity function+ for f in a:+ assert callable(f)+ fn = lambda x,f=f,fn=fn: fn(f(x))+ return fn++ # ----+ # Function for composing two opt-fns together++ def executeSetups(fs, name, opts):+ if type(fs) is list:+ # If we have a list of setups, then execute each one+ for f in fs:+ executeSetups(f, name, opts)+ else:+ # fs is a single function, so just apply it+ fs(name, opts)++ # -----------------------------------------------------------------------------+ # The current directory of tests++ def newTestDir( dir ):+ global thisdir_settings+ # reset the options for this test directory+ thisdir_settings = lambda name, opts, dir=dir: _newTestDir( name, opts, dir )++ def _newTestDir( name, opts, dir ):+ opts.testdir = dir+ opts.compiler_always_flags = config.compiler_always_flags++ # -----------------------------------------------------------------------------+ # Actually doing tests++ parallelTests = []+ aloneTests = []+ allTestNames = set([])++ def runTest (opts, name, func, args):+ ok = 0++ if config.use_threads:+ t.thread_pool.acquire()+ try:+ while config.threads<(t.running_threads+1):+ t.thread_pool.wait()+ t.running_threads = t.running_threads+1+ ok=1+ t.thread_pool.release()+ thread.start_new_thread(test_common_thread, (name, opts, func, args))+ except:+ if not ok:+ t.thread_pool.release()+ else:+ test_common_work (name, opts, func, args)++ # name :: String+ # setup :: TestOpts -> IO ()+ def test (name, setup, func, args):+ global aloneTests+ global parallelTests+ global allTestNames+ global thisdir_settings+ if name in allTestNames:+ framework_fail(name, 'duplicate', 'There are multiple tests with this name')+ if not re.match('^[0-9]*[a-zA-Z][a-zA-Z0-9._-]*$', name):+ framework_fail(name, 'bad_name', 'This test has an invalid name')++ # Make a deep copy of the default_testopts, as we need our own copy+ # of any dictionaries etc inside it. Otherwise, if one test modifies+ # them, all tests will see the modified version!+ myTestOpts = copy.deepcopy(default_testopts)++ executeSetups([thisdir_settings, setup], name, myTestOpts)++ thisTest = lambda : runTest(myTestOpts, name, func, args)+ if myTestOpts.alone:+ aloneTests.append(thisTest)+ else:+ parallelTests.append(thisTest)+ allTestNames.add(name)++ if config.use_threads:+ def test_common_thread(name, opts, func, args):+ t.lock.acquire()+ try:+ test_common_work(name,opts,func,args)+ finally:+ t.lock.release()+ t.thread_pool.acquire()+ t.running_threads = t.running_threads - 1+ t.thread_pool.notify()+ t.thread_pool.release()++ def get_package_cache_timestamp():+ if config.package_conf_cache_file == '':+ return 0.0+ else:+ try:+ return os.stat(config.package_conf_cache_file).st_mtime+ except:+ return 0.0+++ def test_common_work (name, opts, func, args):+ try:+ t.total_tests = t.total_tests+1+ setLocalTestOpts(opts)++ package_conf_cache_file_start_timestamp = get_package_cache_timestamp()++ # All the ways we might run this test+ if func == compile or func == multimod_compile:+ all_ways = config.compile_ways+ elif func == compile_and_run or func == multimod_compile_and_run:+ all_ways = config.run_ways+ elif func == ghci_script:+ if 'ghci' in config.run_ways:+ all_ways = ['ghci']+ else:+ all_ways = []+ else:+ all_ways = ['normal']++ # A test itself can request extra ways by setting opts.extra_ways+ all_ways = all_ways + [way for way in opts.extra_ways if way not in all_ways]++ t.total_test_cases = t.total_test_cases + len(all_ways)++ ok_way = lambda way: \+ not getTestOpts().skip \+ and (config.only == [] or name in config.only) \+ and (getTestOpts().only_ways == None or way in getTestOpts().only_ways) \+ and (config.cmdline_ways == [] or way in config.cmdline_ways) \+ and (not (config.skip_perf_tests and isStatsTest())) \+ and way not in getTestOpts().omit_ways++ # Which ways we are asked to skip+ do_ways = list(filter (ok_way,all_ways))++ # In fast mode, we skip all but one way+ if config.fast and len(do_ways) > 0:+ do_ways = [do_ways[0]]++ if not config.clean_only:+ # Run the required tests...+ for way in do_ways:+ if stopping():+ break+ do_test (name, way, func, args)++ for way in all_ways:+ if way not in do_ways:+ skiptest (name,way)++ if getTestOpts().cleanup != '' and (config.clean_only or do_ways != []):+ pretest_cleanup(name)+ clean([name + suff for suff in [+ '', '.exe', '.exe.manifest', '.genscript',+ '.stderr.normalised', '.stdout.normalised',+ '.run.stderr.normalised', '.run.stdout.normalised',+ '.comp.stderr.normalised', '.comp.stdout.normalised',+ '.interp.stderr.normalised', '.interp.stdout.normalised',+ '.stats', '.comp.stats',+ '.hi', '.o', '.prof', '.exe.prof', '.hc',+ '_stub.h', '_stub.c', '_stub.o',+ '.hp', '.exe.hp', '.ps', '.aux', '.hcr', '.eventlog']])++ if func == multi_compile or func == multi_compile_fail:+ extra_mods = args[1]+ clean([replace_suffix(fx[0],'o') for fx in extra_mods])+ clean([replace_suffix(fx[0], 'hi') for fx in extra_mods])+++ clean(getTestOpts().clean_files)++ if getTestOpts().outputdir != None:+ odir = in_testdir(getTestOpts().outputdir)+ try:+ shutil.rmtree(odir)+ except:+ pass++ try:+ shutil.rmtree(in_testdir('.hpc.' + name))+ except:+ pass++ try:+ cleanCmd = getTestOpts().clean_cmd+ if cleanCmd != None:+ result = runCmdFor(name, 'cd ' + getTestOpts().testdir + ' && ' + cleanCmd)+ if result != 0:+ framework_fail(name, 'cleaning', 'clean-command failed: ' + str(result))+ except:+ framework_fail(name, 'cleaning', 'clean-command exception')++ package_conf_cache_file_end_timestamp = get_package_cache_timestamp();++ if package_conf_cache_file_start_timestamp != package_conf_cache_file_end_timestamp:+ framework_fail(name, 'whole-test', 'Package cache timestamps do not match: ' + str(package_conf_cache_file_start_timestamp) + ' ' + str(package_conf_cache_file_end_timestamp))++ try:+ for f in files_written[name]:+ if os.path.exists(f):+ try:+ if not f in files_written_not_removed[name]:+ files_written_not_removed[name].append(f)+ except:+ files_written_not_removed[name] = [f]+ except:+ pass+ except Exception as e:+ framework_fail(name, 'runTest', 'Unhandled exception: ' + str(e))++ def clean(strs):+ for str in strs:+ for name in glob.glob(in_testdir(str)):+ clean_full_path(name)++ def clean_full_path(name):+ try:+ # Remove files...+ os.remove(name)+ except OSError as e1:+ try:+ # ... and empty directories+ os.rmdir(name)+ except OSError as e2:+ # We don't want to fail here, but we do want to know+ # what went wrong, so print out the exceptions.+ # ENOENT isn't a problem, though, as we clean files+ # that don't necessarily exist.+ if e1.errno != errno.ENOENT:+ print(e1)+ if e2.errno != errno.ENOENT:+ print(e2)++ def do_test(name, way, func, args):+ full_name = name + '(' + way + ')'++ try:+ if_verbose(2, "=====> %s %d of %d %s " % \+ (full_name, t.total_tests, len(allTestNames), \+ [t.n_unexpected_passes, \+ t.n_unexpected_failures, \+ t.n_framework_failures]))++ if config.use_threads:+ t.lock.release()++ try:+ preCmd = getTestOpts().pre_cmd+ if preCmd != None:+ result = runCmdFor(name, 'cd ' + getTestOpts().testdir + ' && ' + preCmd)+ if result != 0:+ framework_fail(name, way, 'pre-command failed: ' + str(result))+ except:+ framework_fail(name, way, 'pre-command exception')++ try:+ result = func(*[name,way] + args)+ finally:+ if config.use_threads:+ t.lock.acquire()++ if getTestOpts().expect != 'pass' and \+ getTestOpts().expect != 'fail' and \+ getTestOpts().expect != 'missing-lib':+ framework_fail(name, way, 'bad expected ' + getTestOpts().expect)++ try:+ passFail = result['passFail']+ except:+ passFail = 'No passFail found'++ if passFail == 'pass':+ if getTestOpts().expect == 'pass' \+ and way not in getTestOpts().expect_fail_for:+ t.n_expected_passes = t.n_expected_passes + 1+ if name in t.expected_passes:+ t.expected_passes[name].append(way)+ else:+ t.expected_passes[name] = [way]+ else:+ if_verbose(1, '*** unexpected pass for %s' % full_name)+ t.n_unexpected_passes = t.n_unexpected_passes + 1+ addPassingTestInfo(t.unexpected_passes, getTestOpts().testdir, name, way)+ elif passFail == 'fail':+ if getTestOpts().expect == 'pass' \+ and way not in getTestOpts().expect_fail_for:+ reason = result['reason']+ tag = result.get('tag')+ if tag == 'stat':+ if_verbose(1, '*** unexpected stat test failure for %s' % full_name)+ t.n_unexpected_stat_failures = t.n_unexpected_stat_failures + 1+ addFailingTestInfo(t.unexpected_stat_failures, getTestOpts().testdir, name, reason, way)+ else:+ if_verbose(1, '*** unexpected failure for %s' % full_name)+ t.n_unexpected_failures = t.n_unexpected_failures + 1+ addFailingTestInfo(t.unexpected_failures, getTestOpts().testdir, name, reason, way)+ else:+ if getTestOpts().expect == 'missing-lib':+ t.n_missing_libs = t.n_missing_libs + 1+ if name in t.missing_libs:+ t.missing_libs[name].append(way)+ else:+ t.missing_libs[name] = [way]+ else:+ t.n_expected_failures = t.n_expected_failures + 1+ if name in t.expected_failures:+ t.expected_failures[name].append(way)+ else:+ t.expected_failures[name] = [way]+ else:+ framework_fail(name, way, 'bad result ' + passFail)+ except KeyboardInterrupt:+ stopNow()+ except:+ framework_fail(name, way, 'do_test exception')+ traceback.print_exc()++ def addPassingTestInfo (testInfos, directory, name, way):+ directory = re.sub('^\\.[/\\\\]', '', directory)++ if not directory in testInfos:+ testInfos[directory] = {}++ if not name in testInfos[directory]:+ testInfos[directory][name] = []++ testInfos[directory][name].append(way)++ def addFailingTestInfo (testInfos, directory, name, reason, way):+ directory = re.sub('^\\.[/\\\\]', '', directory)++ if not directory in testInfos:+ testInfos[directory] = {}++ if not name in testInfos[directory]:+ testInfos[directory][name] = {}++ if not reason in testInfos[directory][name]:+ testInfos[directory][name][reason] = []++ testInfos[directory][name][reason].append(way)++ def skiptest (name, way):+ # print 'Skipping test \"', name, '\"'+ t.n_tests_skipped = t.n_tests_skipped + 1+ if name in t.tests_skipped:+ t.tests_skipped[name].append(way)+ else:+ t.tests_skipped[name] = [way]++ def framework_fail( name, way, reason ):+ full_name = name + '(' + way + ')'+ if_verbose(1, '*** framework failure for %s %s ' % (full_name, reason))+ t.n_framework_failures = t.n_framework_failures + 1+ if name in t.framework_failures:+ t.framework_failures[name].append(way)+ else:+ t.framework_failures[name] = [way]++ def badResult(result):+ try:+ if result['passFail'] == 'pass':+ return False+ return True+ except:+ return True++ def passed():+ return {'passFail': 'pass'}++ def failBecause(reason, tag=None):+ return {'passFail': 'fail', 'reason': reason, 'tag': tag}++ # -----------------------------------------------------------------------------+ # Generic command tests++ # A generic command test is expected to run and exit successfully.+ #+ # The expected exit code can be changed via exit_code() as normal, and+ # the expected stdout/stderr are stored in <testname>.stdout and+ # <testname>.stderr. The output of the command can be ignored+ # altogether by using run_command_ignore_output instead of+ # run_command.++ def run_command( name, way, cmd ):+ return simple_run( name, '', cmd, '' )++ # -----------------------------------------------------------------------------+ # GHCi tests++ def ghci_script_without_flag(flag):+ def apply(name, way, script):+ overrides = [f for f in getTestOpts().compiler_always_flags if f != flag]+ return ghci_script_override_default_flags(overrides)(name, way, script)++ return apply++ def ghci_script_override_default_flags(overrides):+ def apply(name, way, script):+ return ghci_script(name, way, script, overrides)++ return apply++ def ghci_script( name, way, script, override_flags = None ):+ # filter out -fforce-recomp from compiler_always_flags, because we're+ # actually testing the recompilation behaviour in the GHCi tests.+ flags = ' '.join(get_compiler_flags(override_flags, noforce=True))++ way_flags = ' '.join(config.way_flags(name)['ghci'])++ # We pass HC and HC_OPTS as environment variables, so that the+ # script can invoke the correct compiler by using ':! $HC $HC_OPTS'+ cmd = ('HC={{compiler}} HC_OPTS="{flags}" {{compiler}} {flags} {way_flags}'+ ).format(flags=flags, way_flags=way_flags)++ getTestOpts().stdin = script+ return simple_run( name, way, cmd, getTestOpts().extra_run_opts )++ # -----------------------------------------------------------------------------+ # Compile-only tests++ def compile_override_default_flags(overrides):+ def apply(name, way, extra_opts):+ return do_compile(name, way, 0, '', [], extra_opts, overrides)++ return apply++ def compile_fail_override_default_flags(overrides):+ def apply(name, way, extra_opts):+ return do_compile(name, way, 1, '', [], extra_opts, overrides)++ return apply++ def compile_without_flag(flag):+ def apply(name, way, extra_opts):+ overrides = [f for f in getTestOpts().compiler_always_flags if f != flag]+ return compile_override_default_flags(overrides)(name, way, extra_opts)++ return apply++ def compile_fail_without_flag(flag):+ def apply(name, way, extra_opts):+ overrides = [f for f in getTestOpts.compiler_always_flags if f != flag]+ return compile_fail_override_default_flags(overrides)(name, way, extra_opts)++ return apply++ def compile( name, way, extra_hc_opts ):+ return do_compile( name, way, 0, '', [], extra_hc_opts )++ def compile_fail( name, way, extra_hc_opts ):+ return do_compile( name, way, 1, '', [], extra_hc_opts )++ def multimod_compile( name, way, top_mod, extra_hc_opts ):+ return do_compile( name, way, 0, top_mod, [], extra_hc_opts )++ def multimod_compile_fail( name, way, top_mod, extra_hc_opts ):+ return do_compile( name, way, 1, top_mod, [], extra_hc_opts )++ def multi_compile( name, way, top_mod, extra_mods, extra_hc_opts ):+ return do_compile( name, way, 0, top_mod, extra_mods, extra_hc_opts)++ def multi_compile_fail( name, way, top_mod, extra_mods, extra_hc_opts ):+ return do_compile( name, way, 1, top_mod, extra_mods, extra_hc_opts)++ def do_compile( name, way, should_fail, top_mod, extra_mods, extra_hc_opts, override_flags = None ):+ # print 'Compile only, extra args = ', extra_hc_opts+ pretest_cleanup(name)++ result = extras_build( way, extra_mods, extra_hc_opts )+ if badResult(result):+ return result+ extra_hc_opts = result['hc_opts']++ force = 0+ if extra_mods:+ force = 1+ result = simple_build( name, way, extra_hc_opts, should_fail, top_mod, 0, 1, force, override_flags )++ if badResult(result):+ return result++ # the actual stderr should always match the expected, regardless+ # of whether we expected the compilation to fail or not (successful+ # compilations may generate warnings).++ if getTestOpts().with_namebase == None:+ namebase = name+ else:+ namebase = getTestOpts().with_namebase++ (platform_specific, expected_stderr_file) = platform_wordsize_qualify(namebase, 'stderr')+ actual_stderr_file = qualify(name, 'comp.stderr')++ if not compare_outputs(way, 'stderr',+ join_normalisers(getTestOpts().extra_errmsg_normaliser,+ normalise_errmsg,+ normalise_whitespace),+ expected_stderr_file, actual_stderr_file):+ return failBecause('stderr mismatch')++ # no problems found, this test passed+ return passed()++ def compile_cmp_asm( name, way, extra_hc_opts ):+ print('Compile only, extra args = ', extra_hc_opts)+ pretest_cleanup(name)+ result = simple_build( name + '.cmm', way, '-keep-s-files -O ' + extra_hc_opts, 0, '', 0, 0, 0)++ if badResult(result):+ return result++ # the actual stderr should always match the expected, regardless+ # of whether we expected the compilation to fail or not (successful+ # compilations may generate warnings).++ if getTestOpts().with_namebase == None:+ namebase = name+ else:+ namebase = getTestOpts().with_namebase++ (platform_specific, expected_asm_file) = platform_wordsize_qualify(namebase, 'asm')+ actual_asm_file = qualify(name, 's')++ if not compare_outputs(way, 'asm',+ join_normalisers(normalise_errmsg, normalise_asm),+ expected_asm_file, actual_asm_file):+ return failBecause('asm mismatch')++ # no problems found, this test passed+ return passed()++ # -----------------------------------------------------------------------------+ # Compile-and-run tests++ def compile_and_run__( name, way, top_mod, extra_mods, extra_hc_opts ):+ # print 'Compile and run, extra args = ', extra_hc_opts+ pretest_cleanup(name)++ result = extras_build( way, extra_mods, extra_hc_opts )+ if badResult(result):+ return result+ extra_hc_opts = result['hc_opts']++ if way == 'ghci': # interpreted...+ return interpreter_run( name, way, extra_hc_opts, 0, top_mod )+ else: # compiled...+ force = 0+ if extra_mods:+ force = 1++ result = simple_build( name, way, extra_hc_opts, 0, top_mod, 1, 1, force)+ if badResult(result):+ return result++ cmd = './' + name;++ # we don't check the compiler's stderr for a compile-and-run test+ return simple_run( name, way, cmd, getTestOpts().extra_run_opts )++ def compile_and_run( name, way, extra_hc_opts ):+ return compile_and_run__( name, way, '', [], extra_hc_opts)++ def multimod_compile_and_run( name, way, top_mod, extra_hc_opts ):+ return compile_and_run__( name, way, top_mod, [], extra_hc_opts)++ def multi_compile_and_run( name, way, top_mod, extra_mods, extra_hc_opts ):+ return compile_and_run__( name, way, top_mod, extra_mods, extra_hc_opts)++ def stats( name, way, stats_file ):+ opts = getTestOpts()+ return checkStats(name, way, stats_file, opts.stats_range_fields)++ # -----------------------------------------------------------------------------+ # Check -t stats info++ def checkStats(name, way, stats_file, range_fields):+ full_name = name + '(' + way + ')'++ result = passed()+ if len(range_fields) > 0:+ f = open(in_testdir(stats_file))+ contents = f.read()+ f.close()++ for (field, (expected, dev)) in range_fields.items():+ m = re.search('\("' + field + '", "([0-9]+)"\)', contents)+ if m == None:+ print('Failed to find field: ', field)+ result = failBecause('no such stats field')+ val = int(m.group(1))++ lowerBound = trunc( expected * ((100 - float(dev))/100))+ upperBound = trunc(0.5 + ceil(expected * ((100 + float(dev))/100)))++ deviation = round(((float(val) * 100)/ expected) - 100, 1)++ if val < lowerBound:+ print(field, 'value is too low:')+ print('(If this is because you have improved GHC, please')+ print('update the test so that GHC doesn\'t regress again)')+ result = failBecause('stat too good', tag='stat')+ if val > upperBound:+ print(field, 'value is too high:')+ result = failBecause('stat not good enough', tag='stat')++ if val < lowerBound or val > upperBound or config.verbose >= 4:+ valStr = str(val)+ valLen = len(valStr)+ expectedStr = str(expected)+ expectedLen = len(expectedStr)+ length = max(len(str(x)) for x in [expected, lowerBound, upperBound, val])++ def display(descr, val, extra):+ print(descr, str(val).rjust(length), extra)++ display(' Expected ' + full_name + ' ' + field + ':', expected, '+/-' + str(dev) + '%')+ display(' Lower bound ' + full_name + ' ' + field + ':', lowerBound, '')+ display(' Upper bound ' + full_name + ' ' + field + ':', upperBound, '')+ display(' Actual ' + full_name + ' ' + field + ':', val, '')+ if val != expected:+ display(' Deviation ' + full_name + ' ' + field + ':', deviation, '%')+ + return result++ # -----------------------------------------------------------------------------+ # Build a single-module program++ def extras_build( way, extra_mods, extra_hc_opts ):+ for modopts in extra_mods:+ mod, opts = modopts+ result = simple_build( mod, way, opts + ' ' + extra_hc_opts, 0, '', 0, 0, 0)+ if not (mod.endswith('.hs') or mod.endswith('.lhs')):+ extra_hc_opts += ' ' + replace_suffix(mod, 'o')+ if badResult(result):+ return result++ return {'passFail' : 'pass', 'hc_opts' : extra_hc_opts}+++ def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf, noforce, override_flags = None ):+ opts = getTestOpts()+ errname = add_suffix(name, 'comp.stderr')+ rm_no_fail( qualify(errname, '') )++ if top_mod != '':+ srcname = top_mod+ rm_no_fail( qualify(name, '') )+ base, suf = os.path.splitext(top_mod)+ rm_no_fail( qualify(base, '') )+ rm_no_fail( qualify(base, 'exe') )+ elif addsuf:+ srcname = add_hs_lhs_suffix(name)+ rm_no_fail( qualify(name, '') )+ else:+ srcname = name+ rm_no_fail( qualify(name, 'o') )++ rm_no_fail( qualify(replace_suffix(srcname, "o"), '') )++ to_do = ''+ if top_mod != '':+ to_do = '--make '+ if link:+ to_do = to_do + '-o ' + name+ elif link:+ to_do = '-o ' + name+ elif opts.compile_to_hc:+ to_do = '-C'+ else:+ to_do = '-c' # just compile++ stats_file = name + '.comp.stats'+ if len(opts.compiler_stats_range_fields) > 0:+ extra_hc_opts += ' +RTS -V0 -t' + stats_file + ' --machine-readable -RTS'++ # Required by GHC 7.3+, harmless for earlier versions:+ if (getTestOpts().c_src or+ getTestOpts().objc_src or+ getTestOpts().objcpp_src or+ getTestOpts().cmm_src):+ extra_hc_opts += ' -no-hs-main '++ if getTestOpts().compile_cmd_prefix == '':+ cmd_prefix = ''+ else:+ cmd_prefix = getTestOpts().compile_cmd_prefix + ' '++ flags = ' '.join(get_compiler_flags(override_flags, noforce) ++ config.way_flags(name)[way])++ cmd = ('cd {opts.testdir} && {cmd_prefix} '+ '{{compiler}} {to_do} {srcname} {flags} {extra_hc_opts} '+ '> {errname} 2>&1'+ ).format(**locals())++ result = runCmdFor(name, cmd)++ if result != 0 and not should_fail:+ actual_stderr = qualify(name, 'comp.stderr')+ if_verbose(1,'Compile failed (status ' + repr(result) + ') errors were:')+ if_verbose_dump(1,actual_stderr)++ # ToDo: if the sub-shell was killed by ^C, then exit++ statsResult = checkStats(name, way, stats_file, opts.compiler_stats_range_fields)++ if badResult(statsResult):+ return statsResult++ if should_fail:+ if result == 0:+ return failBecause('exit code 0')+ else:+ if result != 0:+ return failBecause('exit code non-0')++ return passed()++ # -----------------------------------------------------------------------------+ # Run a program and check its output+ #+ # If testname.stdin exists, route input from that, else+ # from /dev/null. Route output to testname.run.stdout and+ # testname.run.stderr. Returns the exit code of the run.++ def simple_run( name, way, prog, args ):+ opts = getTestOpts()++ # figure out what to use for stdin+ if opts.stdin != '':+ use_stdin = opts.stdin+ else:+ stdin_file = add_suffix(name, 'stdin')+ if os.path.exists(in_testdir(stdin_file)):+ use_stdin = stdin_file+ else:+ use_stdin = '/dev/null'++ run_stdout = add_suffix(name,'run.stdout')+ run_stderr = add_suffix(name,'run.stderr')++ rm_no_fail(qualify(name,'run.stdout'))+ rm_no_fail(qualify(name,'run.stderr'))+ rm_no_fail(qualify(name, 'hp'))+ rm_no_fail(qualify(name,'ps'))+ rm_no_fail(qualify(name, 'prof'))++ my_rts_flags = rts_flags(way)++ stats_file = name + '.stats'+ if len(opts.stats_range_fields) > 0:+ args += ' +RTS -V0 -t' + stats_file + ' --machine-readable -RTS'++ if opts.no_stdin:+ stdin_comes_from = ''+ else:+ stdin_comes_from = ' <' + use_stdin++ if opts.combined_output:+ redirection = ' > {0} 2>&1'.format(run_stdout)+ redirection_append = ' >> {0} 2>&1'.format(run_stdout)+ else:+ redirection = ' > {0} 2> {1}'.format(run_stdout, run_stderr)+ redirection_append = ' >> {0} 2>> {1}'.format(run_stdout, run_stderr)++ cmd = prog + ' ' + args + ' ' \+ + my_rts_flags + ' ' \+ + stdin_comes_from \+ + redirection++ if opts.cmd_wrapper != None:+ cmd = opts.cmd_wrapper(cmd) + redirection_append++ cmd = 'cd ' + opts.testdir + ' && ' + cmd++ # run the command+ result = runCmdFor(name, cmd, timeout_multiplier=opts.timeout_multiplier)++ exit_code = result >> 8+ signal = result & 0xff++ # check the exit code+ if exit_code != opts.exit_code:+ print('Wrong exit code (expected', opts.exit_code, ', actual', exit_code, ')')+ dump_stdout(name)+ dump_stderr(name)+ return failBecause('bad exit code')++ check_hp = my_rts_flags.find("-h") != -1+ check_prof = my_rts_flags.find("-p") != -1++ if not opts.ignore_output:+ bad_stderr = not opts.combined_output and not check_stderr_ok(name, way)+ bad_stdout = not check_stdout_ok(name, way)+ if bad_stderr:+ return failBecause('bad stderr')+ if bad_stdout:+ return failBecause('bad stdout')+ # exit_code > 127 probably indicates a crash, so don't try to run hp2ps.+ if check_hp and (exit_code <= 127 or exit_code == 251) and not check_hp_ok(name):+ return failBecause('bad heap profile')+ if check_prof and not check_prof_ok(name, way):+ return failBecause('bad profile')++ return checkStats(name, way, stats_file, opts.stats_range_fields)++ def rts_flags(way):+ if (way == ''):+ return ''+ else:+ args = config.way_rts_flags[way]++ if args == []:+ return ''+ else:+ return '+RTS ' + ' '.join(args) + ' -RTS'++ # -----------------------------------------------------------------------------+ # Run a program in the interpreter and check its output++ def interpreter_run( name, way, extra_hc_opts, compile_only, top_mod ):+ outname = add_suffix(name, 'interp.stdout')+ errname = add_suffix(name, 'interp.stderr')+ rm_no_fail(outname)+ rm_no_fail(errname)+ rm_no_fail(name)++ if (top_mod == ''):+ srcname = add_hs_lhs_suffix(name)+ else:+ srcname = top_mod++ scriptname = add_suffix(name, 'genscript')+ qscriptname = in_testdir(scriptname)+ rm_no_fail(qscriptname)++ delimiter = '===== program output begins here\n'++ script = open(qscriptname, 'w')+ if not compile_only:+ # set the prog name and command-line args to match the compiled+ # environment.+ script.write(':set prog ' + name + '\n')+ script.write(':set args ' + getTestOpts().extra_run_opts + '\n')+ # Add marker lines to the stdout and stderr output files, so we+ # can separate GHCi's output from the program's.+ script.write(':! echo ' + delimiter)+ script.write(':! echo 1>&2 ' + delimiter)+ # Set stdout to be line-buffered to match the compiled environment.+ script.write('System.IO.hSetBuffering System.IO.stdout System.IO.LineBuffering\n')+ # wrapping in GHC.TopHandler.runIO ensures we get the same output+ # in the event of an exception as for the compiled program.+ script.write('GHC.TopHandler.runIOFastExit Main.main Prelude.>> Prelude.return ()\n')+ script.close()++ # figure out what to use for stdin+ if getTestOpts().stdin != '':+ stdin_file = in_testdir(getTestOpts().stdin)+ else:+ stdin_file = qualify(name, 'stdin')++ if os.path.exists(stdin_file):+ stdin = open(stdin_file, 'r')+ os.system('cat ' + stdin_file + ' >>' + qscriptname)++ script.close()++ flags = ' '.join(get_compiler_flags(override_flags=None, noforce=False) ++ config.way_flags(name)[way])++ if getTestOpts().combined_output:+ redirection = ' > {0} 2>&1'.format(outname)+ redirection_append = ' >> {0} 2>&1'.format(outname)+ else:+ redirection = ' > {0} 2> {1}'.format(outname, errname)+ redirection_append = ' >> {0} 2>> {1}'.format(outname, errname)++ cmd = ('{{compiler}} {srcname} {flags} {extra_hc_opts} '+ '< {scriptname} {redirection}'+ ).format(**locals())++ if getTestOpts().cmd_wrapper != None:+ cmd = getTestOpts().cmd_wrapper(cmd) + redirection_append;++ cmd = 'cd ' + getTestOpts().testdir + " && " + cmd++ result = runCmdFor(name, cmd, timeout_multiplier=getTestOpts().timeout_multiplier)++ exit_code = result >> 8+ signal = result & 0xff++ # split the stdout into compilation/program output+ split_file(in_testdir(outname), delimiter,+ qualify(name, 'comp.stdout'),+ qualify(name, 'run.stdout'))+ split_file(in_testdir(errname), delimiter,+ qualify(name, 'comp.stderr'),+ qualify(name, 'run.stderr'))++ # check the exit code+ if exit_code != getTestOpts().exit_code:+ print('Wrong exit code (expected', getTestOpts().exit_code, ', actual', exit_code, ')')+ dump_stdout(name)+ dump_stderr(name)+ return failBecause('bad exit code')++ # ToDo: if the sub-shell was killed by ^C, then exit++ if getTestOpts().ignore_output or (check_stderr_ok(name, way) and+ check_stdout_ok(name, way)):+ return passed()+ else:+ return failBecause('bad stdout or stderr')+++ def split_file(in_fn, delimiter, out1_fn, out2_fn):+ infile = open(in_fn)+ out1 = open(out1_fn, 'w')+ out2 = open(out2_fn, 'w')++ line = infile.readline()+ line = re.sub('\r', '', line) # ignore Windows EOL+ while (re.sub('^\s*','',line) != delimiter and line != ''):+ out1.write(line)+ line = infile.readline()+ line = re.sub('\r', '', line)+ out1.close()++ line = infile.readline()+ while (line != ''):+ out2.write(line)+ line = infile.readline()+ out2.close()++ # -----------------------------------------------------------------------------+ # Utils+ def get_compiler_flags(override_flags, noforce):+ opts = getTestOpts()++ if override_flags is not None:+ flags = copy.copy(override_flags)+ else:+ flags = copy.copy(opts.compiler_always_flags)++ if noforce:+ flags = [f for f in flags if f != '-fforce-recomp']++ flags.append(opts.extra_hc_opts)++ if opts.outputdir != None:+ flags.extend(["-outputdir", opts.outputdir])++ return flags++ def check_stdout_ok(name, way):+ if getTestOpts().with_namebase == None:+ namebase = name+ else:+ namebase = getTestOpts().with_namebase++ actual_stdout_file = qualify(name, 'run.stdout')+ (platform_specific, expected_stdout_file) = platform_wordsize_qualify(namebase, 'stdout')++ def norm(str):+ if platform_specific:+ return str+ else:+ return normalise_output(str)++ extra_norm = join_normalisers(norm, getTestOpts().extra_normaliser)++ check_stdout = getTestOpts().check_stdout+ if check_stdout:+ return check_stdout(actual_stdout_file, extra_norm)++ return compare_outputs(way, 'stdout', extra_norm,+ expected_stdout_file, actual_stdout_file)++ def dump_stdout( name ):+ print('Stdout:')+ print(read_no_crs(qualify(name, 'run.stdout')))++ def check_stderr_ok(name, way):+ if getTestOpts().with_namebase == None:+ namebase = name+ else:+ namebase = getTestOpts().with_namebase++ actual_stderr_file = qualify(name, 'run.stderr')+ (platform_specific, expected_stderr_file) = platform_wordsize_qualify(namebase, 'stderr')++ def norm(str):+ if platform_specific:+ return str+ else:+ return normalise_errmsg(str)++ return compare_outputs(way, 'stderr',+ join_normalisers(norm, getTestOpts().extra_errmsg_normaliser), \+ expected_stderr_file, actual_stderr_file)++ def dump_stderr( name ):+ print("Stderr:")+ print(read_no_crs(qualify(name, 'run.stderr')))++ def read_no_crs(file):+ str = ''+ try:+ h = open(file)+ str = h.read()+ h.close+ except:+ # On Windows, if the program fails very early, it seems the+ # files stdout/stderr are redirected to may not get created+ pass+ return re.sub('\r', '', str)++ def write_file(file, str):+ h = open(file, 'w')+ h.write(str)+ h.close++ def check_hp_ok(name):++ # do not qualify for hp2ps because we should be in the right directory+ hp2psCmd = "cd " + getTestOpts().testdir + " && {hp2ps} " + name++ hp2psResult = runCmdExitCode(hp2psCmd)++ actual_ps_file = qualify(name, 'ps')++ if(hp2psResult == 0):+ if (os.path.exists(actual_ps_file)):+ if gs_working:+ gsResult = runCmdExitCode(genGSCmd(actual_ps_file))+ if (gsResult == 0):+ return (True)+ else:+ print("hp2ps output for " + name + "is not valid PostScript")+ else: return (True) # assume postscript is valid without ghostscript+ else:+ print("hp2ps did not generate PostScript for " + name)+ return (False)+ else:+ print("hp2ps error when processing heap profile for " + name)+ return(False)++ def check_prof_ok(name, way):++ prof_file = qualify(name,'prof')++ if not os.path.exists(prof_file):+ print(prof_file + " does not exist")+ return(False)++ if os.path.getsize(qualify(name,'prof')) == 0:+ print(prof_file + " is empty")+ return(False)++ if getTestOpts().with_namebase == None:+ namebase = name+ else:+ namebase = getTestOpts().with_namebase++ (platform_specific, expected_prof_file) = \+ platform_wordsize_qualify(namebase, 'prof.sample')++ # sample prof file is not required+ if not os.path.exists(expected_prof_file):+ return True+ else:+ return compare_outputs(way, 'prof',+ join_normalisers(normalise_whitespace,normalise_prof), \+ expected_prof_file, prof_file)++ # Compare expected output to actual output, and optionally accept the+ # new output. Returns true if output matched or was accepted, false+ # otherwise.+ def compare_outputs(way, kind, normaliser, expected_file, actual_file):+ if os.path.exists(expected_file):+ expected_raw = read_no_crs(expected_file)+ # print "norm:", normaliser(expected_raw)+ expected_str = normaliser(expected_raw)+ expected_file_for_diff = expected_file+ else:+ expected_str = ''+ expected_file_for_diff = '/dev/null'++ actual_raw = read_no_crs(actual_file)+ actual_str = normaliser(actual_raw)++ if expected_str == actual_str:+ return 1+ else:+ if_verbose(1, 'Actual ' + kind + ' output differs from expected:')++ if expected_file_for_diff == '/dev/null':+ expected_normalised_file = '/dev/null'+ else:+ expected_normalised_file = expected_file + ".normalised"+ write_file(expected_normalised_file, expected_str)++ actual_normalised_file = actual_file + ".normalised"+ write_file(actual_normalised_file, actual_str)++ # Ignore whitespace when diffing. We should only get to this+ # point if there are non-whitespace differences+ #+ # Note we are diffing the *actual* output, not the normalised+ # output. The normalised output may have whitespace squashed+ # (including newlines) so the diff would be hard to read.+ # This does mean that the diff might contain changes that+ # would be normalised away.+ if (config.verbose >= 1):+ r = os.system( 'diff -uw ' + expected_file_for_diff + \+ ' ' + actual_file )++ # If for some reason there were no non-whitespace differences,+ # then do a full diff+ if r == 0:+ r = os.system( 'diff -u ' + expected_file_for_diff + \+ ' ' + actual_file )++ if config.accept and (getTestOpts().expect == 'fail' or+ way in getTestOpts().expect_fail_for):+ if_verbose(1, 'Test is expected to fail. Not accepting new output.')+ return 0+ elif config.accept:+ if_verbose(1, 'Accepting new output.')+ write_file(expected_file, actual_raw)+ return 1+ else:+ return 0+++ def normalise_whitespace( str ):+ # Merge contiguous whitespace characters into a single space.+ str = re.sub('[ \t\n]+', ' ', str)+ return str++ def normalise_errmsg( str ):+ # If somefile ends in ".exe" or ".exe:", zap ".exe" (for Windows)+ # the colon is there because it appears in error messages; this+ # hacky solution is used in place of more sophisticated filename+ # mangling+ str = re.sub('([^\\s])\\.exe', '\\1', str)+ # normalise slashes, minimise Windows/Unix filename differences+ str = re.sub('\\\\', '/', str)+ # The inplace ghc's are called ghc-stage[123] to avoid filename+ # collisions, so we need to normalise that to just "ghc"+ str = re.sub('ghc-stage[123]', 'ghc', str)+ # Error messages simetimes contain integer implementation package+ str = re.sub('integer-(gmp|simple)-[0-9.]+', 'integer-<IMPL>-<VERSION>', str)+ return str++ # normalise a .prof file, so that we can reasonably compare it against+ # a sample. This doesn't compare any of the actual profiling data,+ # only the shape of the profile and the number of entries.+ def normalise_prof (str):+ # strip everything up to the line beginning "COST CENTRE"+ str = re.sub('^(.*\n)*COST CENTRE[^\n]*\n','',str)++ # strip results for CAFs, these tend to change unpredictably+ str = re.sub('[ \t]*(CAF|IDLE).*\n','',str)++ # XXX Ignore Main.main. Sometimes this appears under CAF, and+ # sometimes under MAIN.+ str = re.sub('[ \t]*main[ \t]+Main.*\n','',str)++ # We have somthing like this:++ # MAIN MAIN 101 0 0.0 0.0 100.0 100.0+ # k Main 204 1 0.0 0.0 0.0 0.0+ # foo Main 205 1 0.0 0.0 0.0 0.0+ # foo.bar Main 207 1 0.0 0.0 0.0 0.0++ # then we remove all the specific profiling data, leaving only the+ # cost centre name, module, and entries, to end up with this:++ # MAIN MAIN 0+ # k Main 1+ # foo Main 1+ # foo.bar Main 1++ str = re.sub('\n([ \t]*[^ \t]+)([ \t]+[^ \t]+)([ \t]+\\d+)([ \t]+\\d+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)','\n\\1 \\2 \\4',str)+ return str++ def normalise_slashes_( str ):+ str = re.sub('\\\\', '/', str)+ return str++ def normalise_exe_( str ):+ str = re.sub('\.exe', '', str)+ return str++ def normalise_output( str ):+ # Remove a .exe extension (for Windows)+ # This can occur in error messages generated by the program.+ str = re.sub('([^\\s])\\.exe', '\\1', str)+ return str++ def normalise_asm( str ):+ lines = str.split('\n')+ # Only keep instructions and labels not starting with a dot.+ metadata = re.compile('^[ \t]*\\..*$')+ out = []+ for line in lines:+ # Drop metadata directives (e.g. ".type")+ if not metadata.match(line):+ line = re.sub('@plt', '', line)+ instr = line.lstrip().split()+ # Drop empty lines.+ if not instr:+ continue+ # Drop operands, except for call instructions.+ elif instr[0] == 'call':+ out.append(instr[0] + ' ' + instr[1])+ else:+ out.append(instr[0])+ out = '\n'.join(out)+ return out++ def if_verbose( n, s ):+ if config.verbose >= n:+ print(s)++ def if_verbose_dump( n, f ):+ if config.verbose >= n:+ try:+ print(open(f).read())+ except:+ print('')++ def rawSystem(cmd_and_args):+ # We prefer subprocess.call to os.spawnv as the latter+ # seems to send its arguments through a shell or something+ # with the Windows (non-cygwin) python. An argument "a b c"+ # turns into three arguments ["a", "b", "c"].++ # However, subprocess is new in python 2.4, so fall back to+ # using spawnv if we don't have it+ cmd = cmd_and_args[0]+ if have_subprocess:+ return subprocess.call([strip_quotes(cmd)] + cmd_and_args[1:])+ else:+ return os.spawnv(os.P_WAIT, cmd, cmd_and_args)++ # When running under native msys Python, any invocations of non-msys binaries,+ # including timeout.exe, will have their arguments munged according to some+ # heuristics, which leads to malformed command lines (#9626). The easiest way+ # to avoid problems is to invoke through /usr/bin/cmd which sidesteps argument+ # munging because it is a native msys application.+ def passThroughCmd(cmd_and_args):+ args = []+ # cmd needs a Windows-style path for its first argument.+ args.append(cmd_and_args[0].replace('/', '\\'))+ # Other arguments need to be quoted to deal with spaces.+ args.extend(['"%s"' % arg for arg in cmd_and_args[1:]])+ return ["cmd", "/c", " ".join(args)]++ # Note that this doesn't handle the timeout itself; it is just used for+ # commands that have timeout handling built-in.+ def rawSystemWithTimeout(cmd_and_args):+ if config.os == 'mingw32' and sys.executable.startswith('/usr'):+ # This is only needed when running under msys python.+ cmd_and_args = passThroughCmd(cmd_and_args)+ r = rawSystem(cmd_and_args)+ if r == 98:+ # The python timeout program uses 98 to signal that ^C was pressed+ stopNow()+ return r++ # cmd is a complex command in Bourne-shell syntax+ # e.g (cd . && 'c:/users/simonpj/darcs/HEAD/compiler/stage1/ghc-inplace' ...etc)+ # Hence it must ultimately be run by a Bourne shell+ #+ # Mostly it invokes the command wrapped in 'timeout' thus+ # timeout 300 'cd . && ...blah blah'+ # so it's timeout's job to invoke the Bourne shell+ #+ # But watch out for the case when there is no timeout program!+ # Then, when using the native Python, os.system will invoke the cmd shell++ def runCmd( cmd ):+ # Format cmd using config. Example: cmd='{hpc} report A.tix'+ cmd = cmd.format(**config.__dict__)++ if_verbose( 3, cmd )+ r = 0+ if config.os == 'mingw32':+ # On MinGW, we will always have timeout+ assert config.timeout_prog!=''++ if config.timeout_prog != '':+ r = rawSystemWithTimeout([config.timeout_prog, str(config.timeout), cmd])+ else:+ r = os.system(cmd)+ return r << 8++ def runCmdFor( name, cmd, timeout_multiplier=1.0 ):+ # Format cmd using config. Example: cmd='{hpc} report A.tix'+ cmd = cmd.format(**config.__dict__)++ if_verbose( 3, cmd )+ r = 0+ if config.os == 'mingw32':+ # On MinGW, we will always have timeout+ assert config.timeout_prog!=''+ timeout = int(ceil(config.timeout * timeout_multiplier))++ if config.timeout_prog != '':+ if config.check_files_written:+ fn = name + ".strace"+ r = rawSystemWithTimeout(+ ["strace", "-o", fn, "-fF",+ "-e", "creat,open,chdir,clone,vfork",+ config.timeout_prog, str(timeout), cmd])+ addTestFilesWritten(name, fn)+ rm_no_fail(fn)+ else:+ r = rawSystemWithTimeout([config.timeout_prog, str(timeout), cmd])+ else:+ r = os.system(cmd)+ return r << 8++ def runCmdExitCode( cmd ):+ return (runCmd(cmd) >> 8);+++ # -----------------------------------------------------------------------------+ # checking for files being written to by multiple tests++ re_strace_call_end = '(\) += ([0-9]+|-1 E.*)| <unfinished ...>)$'+ re_strace_unavailable = re.compile('^\) += \? <unavailable>$')+ re_strace_pid = re.compile('^([0-9]+) +(.*)')+ re_strace_clone = re.compile('^(clone\(|<... clone resumed> ).*\) = ([0-9]+)$')+ re_strace_clone_unfinished = re.compile('^clone\( <unfinished \.\.\.>$')+ re_strace_vfork = re.compile('^(vfork\(\)|<\.\.\. vfork resumed> \)) += ([0-9]+)$')+ re_strace_vfork_unfinished = re.compile('^vfork\( <unfinished \.\.\.>$')+ re_strace_chdir = re.compile('^chdir\("([^"]*)"(\) += 0| <unfinished ...>)$')+ re_strace_chdir_resumed = re.compile('^<\.\.\. chdir resumed> \) += 0$')+ re_strace_open = re.compile('^open\("([^"]*)", ([A-Z_|]*)(, [0-9]+)?' + re_strace_call_end)+ re_strace_open_resumed = re.compile('^<... open resumed> ' + re_strace_call_end)+ re_strace_ignore_sigchild = re.compile('^--- SIGCHLD \(Child exited\) @ 0 \(0\) ---$')+ re_strace_ignore_sigvtalarm = re.compile('^--- SIGVTALRM \(Virtual timer expired\) @ 0 \(0\) ---$')+ re_strace_ignore_sigint = re.compile('^--- SIGINT \(Interrupt\) @ 0 \(0\) ---$')+ re_strace_ignore_sigfpe = re.compile('^--- SIGFPE \(Floating point exception\) @ 0 \(0\) ---$')+ re_strace_ignore_sigsegv = re.compile('^--- SIGSEGV \(Segmentation fault\) @ 0 \(0\) ---$')+ re_strace_ignore_sigpipe = re.compile('^--- SIGPIPE \(Broken pipe\) @ 0 \(0\) ---$')++ # Files that are read or written but shouldn't be:+ # * ghci_history shouldn't be read or written by tests+ # * things under package.conf.d shouldn't be written by tests+ bad_file_usages = {}++ # Mapping from tests to the list of files that they write+ files_written = {}++ # Mapping from tests to the list of files that they write but don't clean+ files_written_not_removed = {}++ def add_bad_file_usage(name, file):+ try:+ if not file in bad_file_usages[name]:+ bad_file_usages[name].append(file)+ except:+ bad_file_usages[name] = [file]++ def mkPath(curdir, path):+ # Given the current full directory is 'curdir', what is the full+ # path to 'path'?+ return os.path.realpath(os.path.join(curdir, path))++ def addTestFilesWritten(name, fn):+ if config.use_threads:+ with t.lockFilesWritten:+ addTestFilesWrittenHelper(name, fn)+ else:+ addTestFilesWrittenHelper(name, fn)++ def addTestFilesWrittenHelper(name, fn):+ started = False+ working_directories = {}++ with open(fn, 'r') as f:+ for line in f:+ m_pid = re_strace_pid.match(line)+ if m_pid:+ pid = m_pid.group(1)+ content = m_pid.group(2)+ elif re_strace_unavailable.match(line):+ next+ else:+ framework_fail(name, 'strace', "Can't find pid in strace line: " + line)++ m_open = re_strace_open.match(content)+ m_chdir = re_strace_chdir.match(content)+ m_clone = re_strace_clone.match(content)+ m_vfork = re_strace_vfork.match(content)++ if not started:+ working_directories[pid] = os.getcwd()+ started = True++ if m_open:+ file = m_open.group(1)+ file = mkPath(working_directories[pid], file)+ if file.endswith("ghci_history"):+ add_bad_file_usage(name, file)+ elif not file in ['/dev/tty', '/dev/null'] and not file.startswith("/tmp/ghc"):+ flags = m_open.group(2).split('|')+ if 'O_WRONLY' in flags or 'O_RDWR' in flags:+ if re.match('package\.conf\.d', file):+ add_bad_file_usage(name, file)+ else:+ try:+ if not file in files_written[name]:+ files_written[name].append(file)+ except:+ files_written[name] = [file]+ elif 'O_RDONLY' in flags:+ pass+ else:+ framework_fail(name, 'strace', "Can't understand flags in open strace line: " + line)+ elif m_chdir:+ # We optimistically assume that unfinished chdir's are going to succeed+ dir = m_chdir.group(1)+ working_directories[pid] = mkPath(working_directories[pid], dir)+ elif m_clone:+ working_directories[m_clone.group(2)] = working_directories[pid]+ elif m_vfork:+ working_directories[m_vfork.group(2)] = working_directories[pid]+ elif re_strace_open_resumed.match(content):+ pass+ elif re_strace_chdir_resumed.match(content):+ pass+ elif re_strace_vfork_unfinished.match(content):+ pass+ elif re_strace_clone_unfinished.match(content):+ pass+ elif re_strace_ignore_sigchild.match(content):+ pass+ elif re_strace_ignore_sigvtalarm.match(content):+ pass+ elif re_strace_ignore_sigint.match(content):+ pass+ elif re_strace_ignore_sigfpe.match(content):+ pass+ elif re_strace_ignore_sigsegv.match(content):+ pass+ elif re_strace_ignore_sigpipe.match(content):+ pass+ else:+ framework_fail(name, 'strace', "Can't understand strace line: " + line)+ + def checkForFilesWrittenProblems(file):+ foundProblem = False++ files_written_inverted = {}+ for t in files_written.keys():+ for f in files_written[t]:+ try:+ files_written_inverted[f].append(t)+ except:+ files_written_inverted[f] = [t]++ for f in files_written_inverted.keys():+ if len(files_written_inverted[f]) > 1:+ if not foundProblem:+ foundProblem = True+ file.write("\n")+ file.write("\nSome files are written by multiple tests:\n")+ file.write(" " + f + " (" + str(files_written_inverted[f]) + ")\n")+ if foundProblem:+ file.write("\n")++ # -----++ if len(files_written_not_removed) > 0:+ file.write("\n")+ file.write("\nSome files written but not removed:\n")+ tests = list(files_written_not_removed.keys())+ tests.sort()+ for t in tests:+ for f in files_written_not_removed[t]:+ file.write(" " + t + ": " + f + "\n")+ file.write("\n")++ # -----++ if len(bad_file_usages) > 0:+ file.write("\n")+ file.write("\nSome bad file usages:\n")+ tests = list(bad_file_usages.keys())+ tests.sort()+ for t in tests:+ for f in bad_file_usages[t]:+ file.write(" " + t + ": " + f + "\n")+ file.write("\n")++ # -----------------------------------------------------------------------------+ # checking if ghostscript is available for checking the output of hp2ps++ def genGSCmd(psfile):+ return (config.gs + ' -dNODISPLAY -dBATCH -dQUIET -dNOPAUSE ' + psfile);++ def gsNotWorking():+ global gs_working+ print("GhostScript not available for hp2ps tests")++ global gs_working+ gs_working = 0+ if config.have_profiling:+ if config.gs != '':+ resultGood = runCmdExitCode(genGSCmd(config.confdir + '/good.ps'));+ if resultGood == 0:+ resultBad = runCmdExitCode(genGSCmd(config.confdir + '/bad.ps') ++ ' >/dev/null 2>&1')+ if resultBad != 0:+ print("GhostScript available for hp2ps tests")+ gs_working = 1;+ else:+ gsNotWorking();+ else:+ gsNotWorking();+ else:+ gsNotWorking();++ def rm_no_fail( file ):+ try:+ os.remove( file )+ finally:+ return++ def add_suffix( name, suffix ):+ if suffix == '':+ return name+ else:+ return name + '.' + suffix++ def add_hs_lhs_suffix(name):+ if getTestOpts().c_src:+ return add_suffix(name, 'c')+ elif getTestOpts().cmm_src:+ return add_suffix(name, 'cmm')+ elif getTestOpts().objc_src:+ return add_suffix(name, 'm')+ elif getTestOpts().objcpp_src:+ return add_suffix(name, 'mm')+ elif getTestOpts().literate:+ return add_suffix(name, 'lhs')+ else:+ return add_suffix(name, 'hs')++ def replace_suffix( name, suffix ):+ base, suf = os.path.splitext(name)+ return base + '.' + suffix++ def in_testdir( name ):+ return (getTestOpts().testdir + '/' + name)++ def qualify( name, suff ):+ return in_testdir(add_suffix(name, suff))+++ # Finding the sample output. The filename is of the form+ #+ # <test>.stdout[-<compiler>][-<version>][-ws-<wordsize>][-<platform>]+ #+ # and we pick the most specific version available. The <version> is+ # the major version of the compiler (e.g. 6.8.2 would be "6.8"). For+ # more fine-grained control use if_compiler_lt().+ #+ def platform_wordsize_qualify( name, suff ):++ basepath = qualify(name, suff)++ paths = [(platformSpecific, basepath + comp + vers + ws + plat)+ for (platformSpecific, plat) in [(1, '-' + config.platform),+ (1, '-' + config.os),+ (0, '')]+ for ws in ['-ws-' + config.wordsize, '']+ for comp in ['-' + config.compiler_type, '']+ for vers in ['-' + config.compiler_maj_version, '']]++ dir = glob.glob(basepath + '*')+ dir = [normalise_slashes_(d) for d in dir]++ for (platformSpecific, f) in paths:+ if f in dir:+ return (platformSpecific,f)++ return (0, basepath)++ # Clean up prior to the test, so that we can't spuriously conclude+ # that it passed on the basis of old run outputs.+ def pretest_cleanup(name):+ if getTestOpts().outputdir != None:+ odir = in_testdir(getTestOpts().outputdir)+ try:+ shutil.rmtree(odir)+ except:+ pass+ os.mkdir(odir)++ rm_no_fail(qualify(name,'interp.stderr'))+ rm_no_fail(qualify(name,'interp.stdout'))+ rm_no_fail(qualify(name,'comp.stderr'))+ rm_no_fail(qualify(name,'comp.stdout'))+ rm_no_fail(qualify(name,'run.stderr'))+ rm_no_fail(qualify(name,'run.stdout'))+ rm_no_fail(qualify(name,'tix'))+ rm_no_fail(qualify(name,'exe.tix'))+ # simple_build zaps the following:+ # rm_nofail(qualify("o"))+ # rm_nofail(qualify(""))+ # not interested in the return code++ # -----------------------------------------------------------------------------+ # Return a list of all the files ending in '.T' below directories roots.++ def findTFiles(roots):+ # It would be better to use os.walk, but that+ # gives backslashes on Windows, which trip the+ # testsuite later :-(+ return [filename for root in roots for filename in findTFiles_(root)]++ def findTFiles_(path):+ if os.path.isdir(path):+ paths = [path + '/' + x for x in os.listdir(path)]+ return findTFiles(paths)+ elif path[-2:] == '.T':+ return [path]+ else:+ return []++ # -----------------------------------------------------------------------------+ # Output a test summary to the specified file object++ def summary(t, file):++ file.write('\n')+ printUnexpectedTests(file, [t.unexpected_passes, t.unexpected_failures, t.unexpected_stat_failures])+ file.write('OVERALL SUMMARY for test run started at '+ + time.strftime("%c %Z", t.start_time) + '\n'+ + str(datetime.timedelta(seconds=+ round(time.time() - time.mktime(t.start_time)))).rjust(8)+ + ' spent to go through\n'+ + repr(t.total_tests).rjust(8)+ + ' total tests, which gave rise to\n'+ + repr(t.total_test_cases).rjust(8)+ + ' test cases, of which\n'+ + repr(t.n_tests_skipped).rjust(8)+ + ' were skipped\n'+ + '\n'+ + repr(t.n_missing_libs).rjust(8)+ + ' had missing libraries\n'+ + repr(t.n_expected_passes).rjust(8)+ + ' expected passes\n'+ + repr(t.n_expected_failures).rjust(8)+ + ' expected failures\n'+ + '\n'+ + repr(t.n_framework_failures).rjust(8)+ + ' caused framework failures\n'+ + repr(t.n_unexpected_passes).rjust(8)+ + ' unexpected passes\n'+ + repr(t.n_unexpected_failures).rjust(8)+ + ' unexpected failures\n'+ + repr(t.n_unexpected_stat_failures).rjust(8)+ + ' unexpected stat failures\n'+ + '\n')++ if t.n_unexpected_passes > 0:+ file.write('Unexpected passes:\n')+ printPassingTestInfosSummary(file, t.unexpected_passes)++ if t.n_unexpected_failures > 0:+ file.write('Unexpected failures:\n')+ printFailingTestInfosSummary(file, t.unexpected_failures)++ if t.n_unexpected_stat_failures > 0:+ file.write('Unexpected stat failures:\n')+ printFailingTestInfosSummary(file, t.unexpected_stat_failures)++ if config.check_files_written:+ checkForFilesWrittenProblems(file)++ if stopping():+ file.write('WARNING: Testsuite run was terminated early\n')++ def printUnexpectedTests(file, testInfoss):+ unexpected = []+ for testInfos in testInfoss:+ directories = testInfos.keys()+ for directory in directories:+ tests = list(testInfos[directory].keys())+ unexpected += tests+ if unexpected != []:+ file.write('Unexpected results from:\n')+ file.write('TEST="' + ' '.join(unexpected) + '"\n')+ file.write('\n')++ def printPassingTestInfosSummary(file, testInfos):+ directories = list(testInfos.keys())+ directories.sort()+ maxDirLen = max(len(x) for x in directories)+ for directory in directories:+ tests = list(testInfos[directory].keys())+ tests.sort()+ for test in tests:+ file.write(' ' + directory.ljust(maxDirLen + 2) + test + \+ ' (' + ','.join(testInfos[directory][test]) + ')\n')+ file.write('\n')++ def printFailingTestInfosSummary(file, testInfos):+ directories = list(testInfos.keys())+ directories.sort()+ maxDirLen = max(len(d) for d in directories)+ for directory in directories:+ tests = list(testInfos[directory].keys())+ tests.sort()+ for test in tests:+ reasons = testInfos[directory][test].keys()+ for reason in reasons:+ file.write(' ' + directory.ljust(maxDirLen + 2) + test + \+ ' [' + reason + ']' + \+ ' (' + ','.join(testInfos[directory][test][reason]) + ')\n')+ file.write('\n')++ def getStdout(cmd_and_args):+ if have_subprocess:+ p = subprocess.Popen([strip_quotes(cmd_and_args[0])] + cmd_and_args[1:],+ stdout=subprocess.PIPE,+ stderr=subprocess.PIPE)+ (stdout, stderr) = p.communicate()+ r = p.wait()+ if r != 0:+ raise Exception("Command failed: " + str(cmd_and_args))+ if stderr != '':+ raise Exception("stderr from command: " + str(cmd_and_args))+ return stdout+ else:+ raise Exception("Need subprocess to get stdout, but don't have it")+EOF+chmod 644 'ghc-test-framework/driver/testlib.py'++mkdir -p 'ghc-test-framework/driver'+sed "s/^ //" <<"EOF" >'ghc-test-framework/driver/testutil.py'+ # -----------------------------------------------------------------------------+ # Utils+ def version_to_ints(v):+ return [ int(x) for x in v.split('.') ]++ def version_lt(x, y):+ return version_to_ints(x) < version_to_ints(y)++ def version_le(x, y):+ return version_to_ints(x) <= version_to_ints(y)++ def version_gt(x, y):+ return version_to_ints(x) > version_to_ints(y)++ def version_ge(x, y):+ return version_to_ints(x) >= version_to_ints(y)++ def strip_quotes(s):+ # Don't wrap commands to subprocess.call/Popen in quotes.+ return s.strip('\'"')+EOF+chmod 644 'ghc-test-framework/driver/testutil.py'++mkdir -p 'ghc-test-framework/mk'+sed "s/^ //" <<"EOF" >'ghc-test-framework/mk/boilerplate.mk'++ default: all++ HAVE_EVAL := NO+ $(eval HAVE_EVAL := YES)++ ifeq "$(HAVE_EVAL)" "NO"+ $(error Your make does not support eval. You need GNU make >= 3.81)+ endif++ ifeq "$(abspath /)" ""+ $(error Your make does not support abspath. You need GNU make >= 3.81)+ endif++ show:+ @echo '$(VALUE)="$($(VALUE))"'++ define canonicalise+ # $1 = path variable+ $1_CYGPATH := $$(shell $(SHELL) -c "cygpath -m '$$($1)'" 2> /dev/null)+ ifneq "$$($1_CYGPATH)" ""+ # We use 'override' in case we are trying to update a value given on+ # the commandline (e.g. TEST_HC)+ override $1 := $$($1_CYGPATH)+ endif+ endef++ define canonicaliseExecutable+ # $1 = program path variable+ ifneq "$$(shell test -x '$$($1).exe' && echo exists)" ""+ # We use 'override' in case we are trying to update a value given on+ # the commandline (e.g. TEST_HC)+ override $1 := $$($1).exe+ endif+ $(call canonicalise,$1)+ endef++ ifeq "$(TEST_HC)" ""++ STAGE1_GHC := $(abspath $(TOP)/../inplace/bin/ghc-stage1)+ STAGE2_GHC := $(abspath $(TOP)/../inplace/bin/ghc-stage2)+ STAGE3_GHC := $(abspath $(TOP)/../inplace/bin/ghc-stage3)++ ifneq "$(wildcard $(STAGE1_GHC) $(STAGE1_GHC).exe)" ""++ IMPLICIT_COMPILER = NO+ IN_TREE_COMPILER = YES+ ifeq "$(BINDIST)" "YES"+ TEST_HC := $(abspath $(TOP)/../)/bindisttest/install dir/bin/ghc+ else ifeq "$(stage)" "1"+ TEST_HC := $(STAGE1_GHC)+ else ifeq "$(stage)" "3"+ TEST_HC := $(STAGE3_GHC)+ else+ # use stage2 by default+ TEST_HC := $(STAGE2_GHC)+ endif++ else+ IMPLICIT_COMPILER = YES+ IN_TREE_COMPILER = NO+ TEST_HC := $(shell which ghc)+ endif++ else+ ifeq "$(TEST_HC)" "ghc"+ IMPLICIT_COMPILER = YES+ else+ IMPLICIT_COMPILER = NO+ endif+ IN_TREE_COMPILER = NO+ # We want to support both "ghc" and "/usr/bin/ghc" as values of TEST_HC+ # passed in by the user, but+ # which ghc == /usr/bin/ghc+ # which /usr/bin/ghc == /usr/bin/ghc+ # so on unix-like platforms we can just always 'which' it.+ # However, on cygwin, we can't just use which:+ # $ which c:/ghc/ghc-7.4.1/bin/ghc.exe+ # which: no ghc.exe in (./c:/ghc/ghc-7.4.1/bin)+ # so we start off by using realpath, and if that succeeds then we use+ # that value. Otherwise we fall back on 'which'.+ #+ # Note also that we need to use 'override' in order to override a+ # value given on the commandline.+ TEST_HC_REALPATH := $(realpath $(TEST_HC))+ ifeq "$(TEST_HC_REALPATH)" ""+ override TEST_HC := $(shell which '$(TEST_HC)')+ else+ override TEST_HC := $(TEST_HC_REALPATH)+ endif+ endif++ # We can't use $(dir ...) here as TEST_HC might be in a path+ # containing spaces+ BIN_ROOT = $(shell dirname '$(TEST_HC)')++ ifeq "$(IMPLICIT_COMPILER)" "YES"+ find_tool = $(shell which $(1))+ else+ find_tool = $(BIN_ROOT)/$(1)+ endif++ ifeq "$(GHC_PKG)" ""+ GHC_PKG := $(call find_tool,ghc-pkg)+ endif++ ifeq "$(RUNGHC)" ""+ RUNGHC := $(call find_tool,runghc)+ endif++ ifeq "$(HSC2HS)" ""+ HSC2HS := $(call find_tool,hsc2hs)+ endif++ ifeq "$(HP2PS_ABS)" ""+ HP2PS_ABS := $(call find_tool,hp2ps)+ endif++ ifeq "$(HPC)" ""+ HPC := $(call find_tool,hpc)+ endif++ $(eval $(call canonicaliseExecutable,TEST_HC))+ ifeq "$(shell test -x '$(TEST_HC)' && echo exists)" ""+ $(error Cannot find ghc: $(TEST_HC))+ endif++ $(eval $(call canonicaliseExecutable,GHC_PKG))+ ifeq "$(shell test -x '$(GHC_PKG)' && echo exists)" ""+ $(error Cannot find ghc-pkg: $(GHC_PKG))+ endif++ $(eval $(call canonicaliseExecutable,HSC2HS))+ ifeq "$(shell test -x '$(HSC2HS)' && echo exists)" ""+ $(error Cannot find hsc2hs: $(HSC2HS))+ endif++ $(eval $(call canonicaliseExecutable,HP2PS_ABS))+ ifeq "$(shell test -x '$(HP2PS_ABS)' && echo exists)" ""+ $(error Cannot find hp2ps: $(HP2PS_ABS))+ endif++ $(eval $(call canonicaliseExecutable,HPC))+ ifeq "$(shell test -x '$(HPC)' && echo exists)" ""+ $(error Cannot find hpc: $(HPC))+ endif++ # Be careful when using this. On Windows it ends up looking like+ # c:/foo/bar which confuses make, as make thinks that the : is Makefile+ # syntax+ TOP_ABS := $(abspath $(TOP))+ $(eval $(call canonicalise,TOP_ABS))++ GS = gs+ CP = cp+ RM = rm -f+ PYTHON = python+ ifeq "$(shell $(SHELL) -c 'python2 -c 0' 2> /dev/null && echo exists)" "exists"+ PYTHON = python2+ endif++ # -----------------------------------------------------------------------------+ # configuration of TEST_HC++ # ghc-config.hs is a short Haskell program that runs ghc --info, parses+ # the results, and emits a little .mk file with make bindings for the values.+ # This way we cache the results for different values of $(TEST_HC)++ $(TOP)/mk/ghc-config : $(TOP)/mk/ghc-config.hs+ "$(TEST_HC)" --make -o $@ $<++ empty=+ space=$(empty) $(empty)+ ghc-config-mk = $(TOP)/mk/ghcconfig$(subst $(space),_,$(subst :,_,$(subst /,_,$(subst \,_,$(TEST_HC))))).mk++ $(ghc-config-mk) : $(TOP)/mk/ghc-config+ $(TOP)/mk/ghc-config "$(TEST_HC)" >"$@"; if [ $$? != 0 ]; then $(RM) "$@"; exit 1; fi+ # If the ghc-config fails, remove $@, and fail++ ifeq "$(findstring clean,$(MAKECMDGOALS))" ""+ include $(ghc-config-mk)+ endif++ ifeq "$(GhcDynamic)" "YES"+ ghcThWayFlags = -dynamic+ ghciWayFlags = -dynamic+ ghcPluginWayFlags = -dynamic+ else+ ghcThWayFlags = -static+ ghciWayFlags = -static+ ghcPluginWayFlags = -static+ endif++ # -----------------------------------------------------------------------------++ ifeq "$(HostOS)" "mingw32"+ WINDOWS = YES+ else+ WINDOWS = NO+ endif+ ifeq "$(HostOS)" "darwin"+ DARWIN = YES+ else+ DARWIN = NO+ endif++EOF+chmod 644 'ghc-test-framework/mk/boilerplate.mk'++mkdir -p 'ghc-test-framework/mk'+sed "s/^ //" <<"EOF" >'ghc-test-framework/mk/ghc-config.hs'+ import System.Environment+ import System.Process+ import Data.Maybe++ main = do+ [ghc] <- getArgs++ info <- readProcess ghc ["+RTS", "--info"] ""+ let fields = read info :: [(String,String)]+ getGhcFieldOrFail fields "HostOS" "Host OS"+ getGhcFieldOrFail fields "WORDSIZE" "Word size"+ getGhcFieldOrFail fields "TARGETPLATFORM" "Target platform"+ getGhcFieldOrFail fields "TargetOS_CPP" "Target OS"+ getGhcFieldOrFail fields "TargetARCH_CPP" "Target architecture"++ info <- readProcess ghc ["--info"] ""+ let fields = read info :: [(String,String)]++ getGhcFieldOrFail fields "GhcStage" "Stage"+ getGhcFieldOrFail fields "GhcDebugged" "Debug on"+ getGhcFieldOrFail fields "GhcWithNativeCodeGen" "Have native code generator"+ getGhcFieldOrFail fields "GhcWithInterpreter" "Have interpreter"+ getGhcFieldOrFail fields "GhcUnregisterised" "Unregisterised"+ getGhcFieldOrFail fields "GhcWithSMP" "Support SMP"+ getGhcFieldOrFail fields "GhcRTSWays" "RTS ways"+ getGhcFieldOrDefault fields "GhcDynamicByDefault" "Dynamic by default" "NO"+ getGhcFieldOrDefault fields "GhcDynamic" "GHC Dynamic" "NO"+ getGhcFieldProgWithDefault fields "AR" "ar command" "ar"+ getGhcFieldProgWithDefault fields "LLC" "LLVM llc command" "llc"++ let pkgdb_flag = case lookup "Project version" fields of+ Just v+ | parseVersion v >= [7,5] -> "package-db"+ _ -> "package-conf"+ putStrLn $ "GhcPackageDbFlag" ++ '=':pkgdb_flag+++ getGhcFieldOrFail :: [(String,String)] -> String -> String -> IO ()+ getGhcFieldOrFail fields mkvar key+ = getGhcField fields mkvar key id (fail ("No field: " ++ key))++ getGhcFieldOrDefault :: [(String,String)] -> String -> String -> String -> IO ()+ getGhcFieldOrDefault fields mkvar key deflt+ = getGhcField fields mkvar key id on_fail+ where+ on_fail = putStrLn (mkvar ++ '=' : deflt)++ getGhcFieldProgWithDefault+ :: [(String,String)]+ -> String -> String -> String+ -> IO ()+ getGhcFieldProgWithDefault fields mkvar key deflt+ = getGhcField fields mkvar key fix on_fail+ where+ fix val = fixSlashes (fixTopdir topdir val)+ topdir = fromMaybe "" (lookup "LibDir" fields)+ on_fail = putStrLn (mkvar ++ '=' : deflt)++ getGhcField+ :: [(String,String)] -> String -> String+ -> (String -> String)+ -> IO ()+ -> IO ()+ getGhcField fields mkvar key fix on_fail =+ case lookup key fields of+ Nothing -> on_fail+ Just val -> putStrLn (mkvar ++ '=' : fix val)++ fixTopdir :: String -> String -> String+ fixTopdir t "" = ""+ fixTopdir t ('$':'t':'o':'p':'d':'i':'r':s) = t ++ s+ fixTopdir t (c:s) = c : fixTopdir t s++ fixSlashes :: FilePath -> FilePath+ fixSlashes = map f+ where f '\\' = '/'+ f c = c++ parseVersion :: String -> [Int]+ parseVersion v = case break (== '.') v of+ (n, rest) -> read n : case rest of+ [] -> []+ ('.':v') -> parseVersion v'+ _ -> error "bug in parseVersion"+EOF+chmod 644 'ghc-test-framework/mk/ghc-config.hs'++mkdir -p 'ghc-test-framework/mk'+sed "s/^ //" <<"EOF" >'ghc-test-framework/mk/test.mk'+ # -----------------------------------------------------------------------------+ # Examples of use:+ #+ # make -- run all the tests in the current directory+ # make verbose -- as make test, but up the verbosity+ # make accept -- run the tests, accepting the current output+ #+ # The following variables may be set on the make command line:+ #+ # TEST -- specific test to run+ # TESTS -- specific tests to run (same as $TEST really)+ # EXTRA_HC_OPTS -- extra flags to send to the Haskell compiler+ # EXTRA_RUNTEST_OPTS -- extra flags to give the test driver+ # CONFIG -- use a different configuration file+ # COMPILER -- select a configuration file from config/+ # THREADS -- run n tests at once+ #+ # -----------------------------------------------------------------------------++ # export the value of $MAKE for invocation in tests/driver/+ export MAKE++ RUNTESTS = $(TOP)/driver/runtests.py+ COMPILER = ghc+ CONFIGDIR = $(TOP)/config+ CONFIG = $(CONFIGDIR)/$(COMPILER)++ ifeq "$(GhcUnregisterised)" "YES"+ # Otherwise C backend generates many warnings about+ # imcompatible proto casts for GCC's buitins:+ # memcpy, printf, strlen.+ EXTRA_HC_OPTS += -optc-fno-builtin+ endif++ # TEST_HC_OPTS is passed to every invocation of TEST_HC + # in nested Makefiles+ TEST_HC_OPTS = -fforce-recomp -dcore-lint -dcmm-lint -dno-debug-output -no-user-$(GhcPackageDbFlag) -rtsopts $(EXTRA_HC_OPTS)++ # The warning suppression flag below is a temporary kludge. While working with+ # tests that contain tabs, please de-tab them so this flag can be eventually+ # removed. See+ # http://ghc.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces+ # for details+ #+ TEST_HC_OPTS += -fno-warn-tabs++ RUNTEST_OPTS =++ ifeq "$(filter $(TargetOS_CPP), cygwin32 mingw32)" ""+ exeext =+ else+ exeext = .exe+ endif++ ifneq "$(filter $(TargetOS_CPP),cygwin32 mingw32)" ""+ dllext = .dll+ else ifeq "$(TargetOS_CPP)" "darwin"+ dllext = .dylib+ else+ dllext = .so+ endif++ RUNTEST_OPTS += -e ghc_compiler_always_flags="'$(TEST_HC_OPTS)'"++ RUNTEST_OPTS += -e ghc_debugged=$(GhcDebugged)++ ifeq "$(GhcWithNativeCodeGen)" "YES"+ RUNTEST_OPTS += -e ghc_with_native_codegen=1+ else+ RUNTEST_OPTS += -e ghc_with_native_codegen=0+ endif++ GHC_PRIM_LIBDIR := $(subst library-dirs: ,,$(shell "$(GHC_PKG)" field ghc-prim library-dirs --simple-output))+ HAVE_VANILLA := $(shell if [ -f $(subst \,/,$(GHC_PRIM_LIBDIR))/GHC/PrimopWrappers.hi ]; then echo YES; else echo NO; fi)+ HAVE_DYNAMIC := $(shell if [ -f $(subst \,/,$(GHC_PRIM_LIBDIR))/GHC/PrimopWrappers.dyn_hi ]; then echo YES; else echo NO; fi)+ HAVE_PROFILING := $(shell if [ -f $(subst \,/,$(GHC_PRIM_LIBDIR))/GHC/PrimopWrappers.p_hi ]; then echo YES; else echo NO; fi)++ ifeq "$(HAVE_VANILLA)" "YES"+ RUNTEST_OPTS += -e ghc_with_vanilla=1+ else+ RUNTEST_OPTS += -e ghc_with_vanilla=0+ endif++ ifeq "$(HAVE_DYNAMIC)" "YES"+ RUNTEST_OPTS += -e ghc_with_dynamic=1+ else+ RUNTEST_OPTS += -e ghc_with_dynamic=0+ endif++ ifeq "$(HAVE_PROFILING)" "YES"+ RUNTEST_OPTS += -e ghc_with_profiling=1+ else+ RUNTEST_OPTS += -e ghc_with_profiling=0+ endif++ ifeq "$(filter thr, $(GhcRTSWays))" "thr"+ RUNTEST_OPTS += -e ghc_with_threaded_rts=1+ else+ RUNTEST_OPTS += -e ghc_with_threaded_rts=0+ endif++ ifeq "$(filter dyn, $(GhcRTSWays))" "dyn"+ RUNTEST_OPTS += -e ghc_with_dynamic_rts=1+ else+ RUNTEST_OPTS += -e ghc_with_dynamic_rts=0+ endif++ ifeq "$(GhcWithInterpreter)" "NO"+ RUNTEST_OPTS += -e ghc_with_interpreter=0+ else ifeq "$(GhcStage)" "1"+ RUNTEST_OPTS += -e ghc_with_interpreter=0+ else+ RUNTEST_OPTS += -e ghc_with_interpreter=1+ endif++ ifeq "$(GhcUnregisterised)" "YES"+ RUNTEST_OPTS += -e ghc_unregisterised=1+ else+ RUNTEST_OPTS += -e ghc_unregisterised=0+ endif++ ifeq "$(GhcDynamicByDefault)" "YES"+ RUNTEST_OPTS += -e ghc_dynamic_by_default=True+ CABAL_MINIMAL_BUILD = --enable-shared --disable-library-vanilla+ else+ RUNTEST_OPTS += -e ghc_dynamic_by_default=False+ CABAL_MINIMAL_BUILD = --enable-library-vanilla --disable-shared+ endif++ ifeq "$(GhcDynamic)" "YES"+ RUNTEST_OPTS += -e ghc_dynamic=True+ CABAL_PLUGIN_BUILD = --enable-shared --disable-library-vanilla+ else+ RUNTEST_OPTS += -e ghc_dynamic=False+ CABAL_PLUGIN_BUILD = --enable-library-vanilla --disable-shared+ endif++ ifeq "$(GhcWithSMP)" "YES"+ RUNTEST_OPTS += -e ghc_with_smp=1+ else+ RUNTEST_OPTS += -e ghc_with_smp=0+ endif++ ifeq "$(LLC)" ""+ RUNTEST_OPTS += -e ghc_with_llvm=0+ else ifneq "$(LLC)" "llc"+ # If we have a real detected value for LLVM, then it really ought to work+ RUNTEST_OPTS += -e ghc_with_llvm=1+ else ifneq "$(shell $(SHELL) -c 'llc --version | grep version' 2> /dev/null)" ""+ RUNTEST_OPTS += -e ghc_with_llvm=1+ else+ RUNTEST_OPTS += -e ghc_with_llvm=0+ endif++ ifeq "$(WINDOWS)" "YES"+ RUNTEST_OPTS += -e windows=True+ else+ RUNTEST_OPTS += -e windows=False+ endif++ ifeq "$(DARWIN)" "YES"+ RUNTEST_OPTS += -e darwin=True+ else+ RUNTEST_OPTS += -e darwin=False+ endif++ ifeq "$(IN_TREE_COMPILER)" "YES"+ RUNTEST_OPTS += -e in_tree_compiler=True+ else+ RUNTEST_OPTS += -e in_tree_compiler=False+ endif++ ifneq "$(THREADS)" ""+ RUNTEST_OPTS += --threads=$(THREADS)+ endif++ ifneq "$(VERBOSE)" ""+ RUNTEST_OPTS += --verbose=$(VERBOSE)+ endif++ ifeq "$(SKIP_PERF_TESTS)" "YES"+ RUNTEST_OPTS += --skip-perf-tests+ endif++ ifneq "$(CLEAN_ONLY)" ""+ RUNTEST_OPTS += -e clean_only=True+ else+ RUNTEST_OPTS += -e clean_only=False+ endif++ ifneq "$(CHECK_FILES_WRITTEN)" ""+ RUNTEST_OPTS += --check-files-written+ endif++ RUNTEST_OPTS += \+ --rootdir=. \+ --config=$(CONFIG) \+ -e 'config.confdir="$(CONFIGDIR)"' \+ -e 'config.platform="$(TARGETPLATFORM)"' \+ -e 'config.os="$(TargetOS_CPP)"' \+ -e 'config.arch="$(TargetARCH_CPP)"' \+ -e 'config.wordsize="$(WORDSIZE)"' \+ -e 'default_testopts.cleanup="$(CLEANUP)"' \+ -e 'config.timeout=int($(TIMEOUT)) or config.timeout' \+ -e 'config.exeext="$(exeext)"' \+ -e 'config.top="$(TOP_ABS)"'++ # Put an extra pair of quotes around program paths,+ # so we don't have to in .T scripts or driver/testlib.py.+ RUNTEST_OPTS += \+ -e 'config.compiler="\"$(TEST_HC)\""' \+ -e 'config.ghc_pkg="\"$(GHC_PKG)\""' \+ -e 'config.hp2ps="\"$(HP2PS_ABS)\""' \+ -e 'config.hpc="\"$(HPC)\""' \+ -e 'config.gs="\"$(GS)\""' \+ -e 'config.timeout_prog="\"$(TIMEOUT_PROGRAM)\""'++ ifneq "$(OUTPUT_SUMMARY)" ""+ RUNTEST_OPTS += \+ --output-summary "$(OUTPUT_SUMMARY)"+ endif++ RUNTEST_OPTS += \+ $(EXTRA_RUNTEST_OPTS)++ ifeq "$(list_broken)" "YES"+ set_list_broken = -e config.list_broken=True+ else+ set_list_broken = + endif++ ifeq "$(fast)" "YES"+ setfast = -e config.fast=1+ else+ setfast = + endif++ ifeq "$(accept)" "YES"+ setaccept = -e config.accept=1+ else+ setaccept = + endif++ TESTS = + TEST = + WAY =++ .PHONY: all boot test verbose accept fast list_broken++ all: test++ TIMEOUT_PROGRAM = $(TOP)/timeout/install-inplace/bin/timeout$(exeext)++ boot: $(TIMEOUT_PROGRAM)++ $(TIMEOUT_PROGRAM) :+ @echo "Looks like you don't have timeout, building it first..."+ $(MAKE) -C $(TOP)/timeout all++ test: $(TIMEOUT_PROGRAM)+ $(PYTHON) $(RUNTESTS) $(RUNTEST_OPTS) \+ $(patsubst %, --only=%, $(TEST)) \+ $(patsubst %, --only=%, $(TESTS)) \+ $(patsubst %, --way=%, $(WAY)) \+ $(patsubst %, --skipway=%, $(SKIPWAY)) \+ $(set_list_broken) \+ $(setfast) \+ $(setaccept)++ verbose: test++ accept:+ $(MAKE) accept=YES++ fast:+ $(MAKE) fast=YES++ list_broken:+ $(MAKE) list_broken=YES++EOF+chmod 644 'ghc-test-framework/mk/test.mk'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/Makefile'+ TOP = ..++ # If we're cleaning then we don't want to do all the GHC detection hardwork,+ # and we certainly don't want to fail if GHC etc can't be found!+ # However, we can't just put this conditional in boilerplate.mk, as+ # some of the tests have a "clean" makefile target that relies on GHC_PKG+ # being defined.+ ifneq "$(MAKECMDGOALS)" "clean"+ ifneq "$(MAKECMDGOALS)" "distclean"+ ifneq "$(MAKECMDGOALS)" "maintainer-clean"++ include $(TOP)/mk/boilerplate.mk++ TIMEOUT_PROGRAM = install-inplace/bin/timeout$(exeext)++ PREFIX := $(abspath install-inplace)+ $(eval $(call canonicalise,PREFIX))++ ifneq "$(GCC)" ""+ WITH_GCC = --with-gcc='$(GCC)'+ endif++ ifeq "$(WINDOWS)" "NO"+ # Use a python timeout program, so that we don't have to worry about+ # whether or not the compiler we're testing has built the timeout+ # program correctly+ $(TIMEOUT_PROGRAM): timeout.py+ rm -rf install-inplace+ mkdir install-inplace+ mkdir install-inplace/bin+ cp $< $@.py+ echo '#!/bin/sh' > $@+ echo 'exec "${PYTHON}" $$0.py "$$@"' >> $@+ chmod +x $@+ else+ # The python timeout program doesn't work on mingw, so we still use the+ # Haskell program on Windows+ $(TIMEOUT_PROGRAM): timeout.hs+ rm -rf install-inplace+ '$(TEST_HC)' --make Setup+ ./Setup configure --with-compiler='$(TEST_HC)' \+ --with-hc-pkg='$(GHC_PKG)' \+ --with-hsc2hs='$(HSC2HS)' \+ $(WITH_GCC) \+ --ghc-option=-threaded --prefix='$(PREFIX)'+ ./Setup build+ ./Setup install+ endif++ boot all :: calibrate.out $(TIMEOUT_PROGRAM)++ calibrate.out:+ $(RM) -f TimeMe.o TimeMe.hi TimeMe TimeMe.exe+ $(PYTHON) calibrate '$(GHC_STAGE1)' > $@+ # We use stage 1 to do the calibration, as stage 2 may not exist.+ # This isn't necessarily the compiler we'll be running the testsuite+ # with, but it's really the performance of the machine that we're+ # interested in++ endif+ endif+ endif++ clean distclean maintainer-clean:+ -./Setup clean+ $(RM) -rf install-inplace+ $(RM) -f calibrate.out+ $(RM) -f Setup Setup.exe Setup.hi Setup.o++EOF+chmod 644 'ghc-test-framework/timeout/Makefile'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/Setup.hs'+ module Main (main) where++ import Distribution.Simple++ main :: IO ()+ main = defaultMain+EOF+chmod 644 'ghc-test-framework/timeout/Setup.hs'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/TimeMe.hs'+ module Main (main) where++ import System.IO++ main = hPutStr stderr ""++EOF+chmod 644 'ghc-test-framework/timeout/TimeMe.hs'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/WinCBindings.hsc'+ {-# LANGUAGE CPP, ForeignFunctionInterface #-}+ module WinCBindings where++ #if defined(mingw32_HOST_OS)++ import Foreign+ import System.Win32.File+ import System.Win32.Types++ #include <windows.h>++ type LPPROCESS_INFORMATION = Ptr PROCESS_INFORMATION+ data PROCESS_INFORMATION = PROCESS_INFORMATION+ { piProcess :: HANDLE+ , piThread :: HANDLE+ , piProcessId :: DWORD+ , piThreadId :: DWORD+ } deriving Show++ instance Storable PROCESS_INFORMATION where+ sizeOf = const #size PROCESS_INFORMATION+ alignment = sizeOf+ poke buf pi = do+ (#poke PROCESS_INFORMATION, hProcess) buf (piProcess pi)+ (#poke PROCESS_INFORMATION, hThread) buf (piThread pi)+ (#poke PROCESS_INFORMATION, dwProcessId) buf (piProcessId pi)+ (#poke PROCESS_INFORMATION, dwThreadId) buf (piThreadId pi)++ peek buf = do+ vhProcess <- (#peek PROCESS_INFORMATION, hProcess) buf+ vhThread <- (#peek PROCESS_INFORMATION, hThread) buf+ vdwProcessId <- (#peek PROCESS_INFORMATION, dwProcessId) buf+ vdwThreadId <- (#peek PROCESS_INFORMATION, dwThreadId) buf+ return $ PROCESS_INFORMATION {+ piProcess = vhProcess,+ piThread = vhThread,+ piProcessId = vdwProcessId,+ piThreadId = vdwThreadId}++ type LPSTARTUPINFO = Ptr STARTUPINFO+ data STARTUPINFO = STARTUPINFO+ { siCb :: DWORD+ , siDesktop :: LPTSTR+ , siTitle :: LPTSTR+ , siX :: DWORD+ , siY :: DWORD+ , siXSize :: DWORD+ , siYSize :: DWORD+ , siXCountChars :: DWORD+ , siYCountChars :: DWORD+ , siFillAttribute :: DWORD+ , siFlags :: DWORD+ , siShowWindow :: WORD+ , siStdInput :: HANDLE+ , siStdOutput :: HANDLE+ , siStdError :: HANDLE+ } deriving Show++ instance Storable STARTUPINFO where+ sizeOf = const #size STARTUPINFO+ alignment = sizeOf+ poke buf si = do+ (#poke STARTUPINFO, cb) buf (siCb si)+ (#poke STARTUPINFO, lpDesktop) buf (siDesktop si)+ (#poke STARTUPINFO, lpTitle) buf (siTitle si)+ (#poke STARTUPINFO, dwX) buf (siX si)+ (#poke STARTUPINFO, dwY) buf (siY si)+ (#poke STARTUPINFO, dwXSize) buf (siXSize si)+ (#poke STARTUPINFO, dwYSize) buf (siYSize si)+ (#poke STARTUPINFO, dwXCountChars) buf (siXCountChars si)+ (#poke STARTUPINFO, dwYCountChars) buf (siYCountChars si)+ (#poke STARTUPINFO, dwFillAttribute) buf (siFillAttribute si)+ (#poke STARTUPINFO, dwFlags) buf (siFlags si)+ (#poke STARTUPINFO, wShowWindow) buf (siShowWindow si)+ (#poke STARTUPINFO, hStdInput) buf (siStdInput si)+ (#poke STARTUPINFO, hStdOutput) buf (siStdOutput si)+ (#poke STARTUPINFO, hStdError) buf (siStdError si)++ peek buf = do+ vcb <- (#peek STARTUPINFO, cb) buf+ vlpDesktop <- (#peek STARTUPINFO, lpDesktop) buf+ vlpTitle <- (#peek STARTUPINFO, lpTitle) buf+ vdwX <- (#peek STARTUPINFO, dwX) buf+ vdwY <- (#peek STARTUPINFO, dwY) buf+ vdwXSize <- (#peek STARTUPINFO, dwXSize) buf+ vdwYSize <- (#peek STARTUPINFO, dwYSize) buf+ vdwXCountChars <- (#peek STARTUPINFO, dwXCountChars) buf+ vdwYCountChars <- (#peek STARTUPINFO, dwYCountChars) buf+ vdwFillAttribute <- (#peek STARTUPINFO, dwFillAttribute) buf+ vdwFlags <- (#peek STARTUPINFO, dwFlags) buf+ vwShowWindow <- (#peek STARTUPINFO, wShowWindow) buf+ vhStdInput <- (#peek STARTUPINFO, hStdInput) buf+ vhStdOutput <- (#peek STARTUPINFO, hStdOutput) buf+ vhStdError <- (#peek STARTUPINFO, hStdError) buf+ return $ STARTUPINFO {+ siCb = vcb,+ siDesktop = vlpDesktop,+ siTitle = vlpTitle,+ siX = vdwX,+ siY = vdwY,+ siXSize = vdwXSize,+ siYSize = vdwYSize,+ siXCountChars = vdwXCountChars,+ siYCountChars = vdwYCountChars,+ siFillAttribute = vdwFillAttribute,+ siFlags = vdwFlags,+ siShowWindow = vwShowWindow,+ siStdInput = vhStdInput,+ siStdOutput = vhStdOutput,+ siStdError = vhStdError}++ foreign import stdcall unsafe "windows.h WaitForSingleObject"+ waitForSingleObject :: HANDLE -> DWORD -> IO DWORD++ cWAIT_ABANDONED :: DWORD+ cWAIT_ABANDONED = #const WAIT_ABANDONED++ cWAIT_OBJECT_0 :: DWORD+ cWAIT_OBJECT_0 = #const WAIT_OBJECT_0++ cWAIT_TIMEOUT :: DWORD+ cWAIT_TIMEOUT = #const WAIT_TIMEOUT++ foreign import stdcall unsafe "windows.h GetExitCodeProcess"+ getExitCodeProcess :: HANDLE -> LPDWORD -> IO BOOL++ foreign import stdcall unsafe "windows.h TerminateJobObject"+ terminateJobObject :: HANDLE -> UINT -> IO BOOL++ foreign import stdcall unsafe "windows.h AssignProcessToJobObject"+ assignProcessToJobObject :: HANDLE -> HANDLE -> IO BOOL++ foreign import stdcall unsafe "windows.h CreateJobObjectW"+ createJobObjectW :: LPSECURITY_ATTRIBUTES -> LPCTSTR -> IO HANDLE++ foreign import stdcall unsafe "windows.h CreateProcessW"+ createProcessW :: LPCTSTR -> LPTSTR+ -> LPSECURITY_ATTRIBUTES -> LPSECURITY_ATTRIBUTES+ -> BOOL -> DWORD -> LPVOID -> LPCTSTR -> LPSTARTUPINFO+ -> LPPROCESS_INFORMATION -> IO BOOL++ #endif++EOF+chmod 644 'ghc-test-framework/timeout/WinCBindings.hsc'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/calibrate'+ #!/usr/bin/env python++ import math+ import os+ from os import *+ from sys import *+ try:+ from resource import *+ except:+ # We don't have resource, so this is a non-UNIX machine.+ # It's probably a reasonable modern x86/x86_64 machines, so we'd+ # probably calibrate to 300 anyway; thus just print 300.+ print(300)+ exit(0)++ compiler = argv[1]+ compiler_name = os.path.basename(compiler)++ spawnl(os.P_WAIT, compiler,+ compiler_name, 'TimeMe.hs', '-o', 'TimeMe', '-O2')+ spawnl(os.P_WAIT, './TimeMe', 'TimeMe')++ xs = getrusage(RUSAGE_CHILDREN);+ x = int(math.ceil(xs[0] + xs[1]))+ if x < 1:+ x = 1+ print (300*x)++EOF+chmod 644 'ghc-test-framework/timeout/calibrate'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/timeout.cabal'+ Name: timeout+ Version: 1+ Copyright: GHC Team+ License: BSD3+ Author: GHC Team <cvs-ghc@haskell.org>+ Maintainer: GHC Team <cvs-ghc@haskell.org>+ Synopsis: timout utility+ Description: timeout utility+ Category: Development+ build-type: Simple+ cabal-version: >=1.2++ Executable timeout+ Main-Is: timeout.hs+ Other-Modules: WinCBindings+ Extensions: CPP+ Build-Depends: base, process+ if os(windows)+ Build-Depends: Win32+ else+ Build-Depends: unix++EOF+chmod 644 'ghc-test-framework/timeout/timeout.cabal'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/timeout.hs'+ {-# OPTIONS -cpp #-}+ module Main where++ import Control.Concurrent (forkIO, threadDelay)+ import Control.Concurrent.MVar (putMVar, takeMVar, newEmptyMVar)+ import Control.Monad+ import Control.Exception+ import Data.Maybe (isNothing)+ import System.Environment (getArgs)+ import System.Exit+ import System.IO (hPutStrLn, stderr)++ #if !defined(mingw32_HOST_OS)+ import System.Posix hiding (killProcess)+ import System.IO.Error hiding (try,catch)+ #endif++ #if defined(mingw32_HOST_OS)+ import System.Process+ import WinCBindings+ import Foreign+ import System.Win32.DebugApi+ import System.Win32.Types+ #endif++ main :: IO ()+ main = do+ args <- getArgs+ case args of+ [secs,cmd] ->+ case reads secs of+ [(secs', "")] -> run secs' cmd+ _ -> ((>> exitFailure) . hPutStrLn stderr) ("Can't parse " ++ show secs ++ " as a number of seconds")+ _ -> ((>> exitFailure) . hPutStrLn stderr) ("Bad arguments " ++ show args)++ timeoutMsg :: String+ timeoutMsg = "Timeout happened...killing process..."++ run :: Int -> String -> IO ()+ #if !defined(mingw32_HOST_OS)+ run secs cmd = do+ m <- newEmptyMVar+ mp <- newEmptyMVar+ installHandler sigINT (Catch (putMVar m Nothing)) Nothing+ forkIO $ do threadDelay (secs * 1000000)+ putMVar m Nothing+ forkIO $ do ei <- try $ do pid <- systemSession cmd+ return pid+ putMVar mp ei+ case ei of+ Left _ -> return ()+ Right pid -> do+ r <- getProcessStatus True False pid+ putMVar m r+ ei_pid_ph <- takeMVar mp+ case ei_pid_ph of+ Left e -> do hPutStrLn stderr+ ("Timeout:\n" ++ show (e :: IOException))+ exitWith (ExitFailure 98)+ Right pid -> do+ r <- takeMVar m+ case r of+ Nothing -> do+ hPutStrLn stderr timeoutMsg+ killProcess pid+ exitWith (ExitFailure 99)+ Just (Exited r) -> exitWith r+ Just (Terminated s) -> raiseSignal s+ Just _ -> exitWith (ExitFailure 1)++ systemSession cmd =+ forkProcess $ do+ createSession+ executeFile "/bin/sh" False ["-c", cmd] Nothing+ -- need to use exec() directly here, rather than something like+ -- System.Process.system, because we are in a forked child and some+ -- pthread libraries get all upset if you start doing certain+ -- things in a forked child of a pthread process, such as forking+ -- more threads.++ killProcess pid = do+ ignoreIOExceptions (signalProcessGroup sigTERM pid)+ checkReallyDead 10+ where+ checkReallyDead 0 = hPutStrLn stderr "checkReallyDead: Giving up"+ checkReallyDead (n+1) =+ do threadDelay (3*100000) -- 3/10 sec+ m <- tryJust (guard . isDoesNotExistError) $+ getProcessStatus False False pid+ case m of+ Right Nothing -> return ()+ Left _ -> return ()+ _ -> do+ ignoreIOExceptions (signalProcessGroup sigKILL pid)+ checkReallyDead n++ ignoreIOExceptions :: IO () -> IO ()+ ignoreIOExceptions io = io `catch` ((\_ -> return ()) :: IOException -> IO ())++ #else+ run secs cmd =+ let escape '\\' = "\\\\"+ escape '"' = "\\\""+ escape c = [c]+ cmd' = "sh -c \"" ++ concatMap escape cmd ++ "\"" in+ alloca $ \p_startupinfo ->+ alloca $ \p_pi ->+ withTString cmd' $ \cmd'' ->+ do job <- createJobObjectW nullPtr nullPtr+ let creationflags = 0+ b <- createProcessW nullPtr cmd'' nullPtr nullPtr True+ creationflags+ nullPtr nullPtr p_startupinfo p_pi+ unless b $ errorWin "createProcessW"+ pi <- peek p_pi+ assignProcessToJobObject job (piProcess pi)+ resumeThread (piThread pi)++ -- The program is now running++ let handle = piProcess pi+ let millisecs = secs * 1000+ rc <- waitForSingleObject handle (fromIntegral millisecs)+ if rc == cWAIT_TIMEOUT+ then do hPutStrLn stderr timeoutMsg+ terminateJobObject job 99+ exitWith (ExitFailure 99)+ else alloca $ \p_exitCode ->+ do r <- getExitCodeProcess handle p_exitCode+ if r then do ec <- peek p_exitCode+ let ec' = if ec == 0+ then ExitSuccess+ else ExitFailure $ fromIntegral ec+ exitWith ec'+ else errorWin "getExitCodeProcess"+ #endif++EOF+chmod 644 'ghc-test-framework/timeout/timeout.hs'++mkdir -p 'ghc-test-framework/timeout'+sed "s/^ //" <<"EOF" >'ghc-test-framework/timeout/timeout.py'+ #!/usr/bin/env python++ try:++ import errno+ import os+ import signal+ import sys+ import time++ secs = int(sys.argv[1])+ cmd = sys.argv[2]++ def killProcess(pid):+ os.killpg(pid, signal.SIGKILL)+ for x in range(10):+ try:+ time.sleep(0.3)+ r = os.waitpid(pid, os.WNOHANG)+ if r == (0, 0):+ os.killpg(pid, signal.SIGKILL)+ else:+ return+ except OSError as e:+ if e.errno == errno.ECHILD:+ return+ else:+ raise e++ pid = os.fork()+ if pid == 0:+ # child+ os.setpgrp()+ os.execvp('/bin/sh', ['/bin/sh', '-c', cmd])+ else:+ # parent+ def handler(signum, frame):+ sys.stderr.write('Timeout happened...killing process...\n')+ killProcess(pid)+ sys.exit(99)+ old = signal.signal(signal.SIGALRM, handler)+ signal.alarm(secs)+ (pid2, res) = os.waitpid(pid, 0)+ if (os.WIFEXITED(res)):+ sys.exit(os.WEXITSTATUS(res))+ else:+ sys.exit(res)++ except KeyboardInterrupt:+ sys.exit(98)+ except:+ raise++EOF+chmod 644 'ghc-test-framework/timeout/timeout.py'
− tools/ghc.patch
@@ -1,62 +0,0 @@-# allow ghc and its tools to be located in different directories---- testsuite/mk/boilerplate.mk-+++ testsuite/mk/boilerplate.mk-@@ -56,6 +56,7 @@ TEST_HC := $(STAGE2_GHC)- endif- - else-+implicit_compiler = YES- IN_TREE_COMPILER = NO- TEST_HC := $(shell which ghc)- endif-@@ -87,24 +88,30 @@ endif- # containing spaces- BIN_ROOT = $(shell dirname '$(TEST_HC)')- -+ifeq "$(implicit_compiler)" "YES"-+find_tool = $(shell which $(1))-+else-+find_tool = $(BIN_ROOT)/$(1)-+endif-+- ifeq "$(GHC_PKG)" ""--GHC_PKG := $(BIN_ROOT)/ghc-pkg-+GHC_PKG := $(call find_tool,ghc-pkg)- endif- - ifeq "$(RUNGHC)" ""--RUNGHC := $(BIN_ROOT)/runghc-+RUNGHC := $(call find_tool,runghc)- endif- - ifeq "$(HSC2HS)" ""--HSC2HS := $(BIN_ROOT)/hsc2hs-+HSC2HS := $(call find_tool,hsc2hs)- endif- - ifeq "$(HP2PS_ABS)" ""--HP2PS_ABS := $(BIN_ROOT)/hp2ps-+HP2PS_ABS := $(call find_tool,hp2ps)- endif- - ifeq "$(HPC)" ""--HPC := $(BIN_ROOT)/hpc-+HPC := $(call find_tool,hpc)- endif- - $(eval $(call canonicaliseExecutable,TEST_HC))--# 'die' is not available until GHC 7.10---- testsuite/timeout/timeout.hs-+++ testsuite/timeout/timeout.hs-@@ -30,8 +30,8 @@ main = do- [secs,cmd] ->- case reads secs of- [(secs', "")] -> run secs' cmd-- _ -> die ("Can't parse " ++ show secs ++ " as a number of seconds")-- _ -> die ("Bad arguments " ++ show args)-+ _ -> ((>> exitFailure) . hPutStrLn stderr) ("Can't parse " ++ show secs ++ " as a number of seconds")-+ _ -> ((>> exitFailure) . hPutStrLn stderr) ("Bad arguments " ++ show args)- - timeoutMsg :: String- timeoutMsg = "Timeout happened...killing process..."
tools/run-tests view
@@ -21,18 +21,20 @@ HSFLAGS="-optP-include -optP../dist/build/autogen/cabal_macros.h $HSFLAGS" export HSFLAGS -# download the GHC repository-[ -f dist/testsuite/ghc.ok ] || {- rm -fr dist/testsuite/ghc- git clone --depth 1 https://github.com/ghc/ghc dist/testsuite/ghc- patch <tools/ghc.patch -d dist/testsuite/ghc -N -p 0 -r - || :- touch dist/testsuite/ghc.ok-}+# extract the test framework if needed+[ -f dist/testsuite/ghc-test-framework.ok ] || (+ set -e+ mkdir -p dist/testsuite+ cd dist/testsuite+ rm -fr ghc-test-framework+ sh+ touch ghc-test-framework.ok+) <tools/ghc-test-framework.shar # we can't just specify `TOP` as an argument for `make` because it will # override `TOP` for *every* included makefile sed >dist/testsuite/Makefile \- "s|^TOP=.*$|TOP=../dist/testsuite/ghc/testsuite|" \+ "s|^TOP=.*$|TOP=../dist/testsuite/ghc-test-framework|" \ tests/Makefile cd tests