[PATCH] qmtest_gcc: Support libstdc++ testing under QMTest

Nathaniel Smith njs at pobox.com
Thu Mar 18 18:39:15 UTC 2004


-- Nathaniel

-- 
So let us espouse a less contested notion of truth and falsehood, even
if it is philosophically debatable (if we listen to philosophers, we
must debate everything, and there would be no end to the discussion).
  -- Serendipities, Umberto Eco
-------------- next part --------------
Index: classes.qmc
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/classes.qmc,v
retrieving revision 1.11
diff -u -r1.11 classes.qmc
--- classes.qmc	23 Jun 2003 06:34:04 -0000	1.11
+++ classes.qmc	18 Mar 2004 18:33:55 -0000
@@ -1,2 +1,36 @@
 <?xml version="1.0" ?>
-<class-directory><class kind="database">gcc_database.GCCDatabase</class><class kind="resource">gpp_init.GPPInit</class><class kind="resource">dg_tls_test.GPPTLSInit</class><class kind="test">dg_tls_test.GPPDGTLSTest</class><class kind="resource">debug_test.GPPDebugInit</class><class kind="test">gpp_dg_test.GPPDGTest</class><class kind="test">gpp_profile_test.GPPProfileTest</class><class kind="test">gpp_gcov_test.GPPGCOVTest</class><class kind="test">gpp_old_deja_test.GPPOldDejaTest</class><class kind="test">gcc_dg_test.GCCDGTest</class><class kind="test">gcc_dg_test.GCCDGNoncompileTest</class><class kind="resource">debug_test.GCCDebugInit</class><class kind="test">debug_test.GPPDGDebugTest</class><class kind="test">debug_test.GCCDGDebugTest</class><class kind="test">gcc_dg_test.GCCDGCPPTradTest</class><class kind="test">dg_pch_test.GPPDGPCHTest</class><class kind="test">dg_pch_test.GCCDGPCHTest</class><class kind="test">gcc_dg_test.GCCDGCPPTest</class><class kind="test">gcc_dg_test.GCCDGFormatTest</class><class kind="resource">dg_tls_test.GCCTLSInit</class><class kind="test">dg_tls_test.GCCDGTLSTest</class><class kind="test">gcc_dg_test.GCCDGTortureTest</class><class kind="resource">gcc_init.GCCInit</class><class kind="test">compat_test.GCCCompatTest</class><class kind="test">compat_test.GPPCompatTest</class><class kind="test">gcc_dg_test.GCCCTortureCompileTest</class></class-directory>
+<!DOCTYPE class-directory
+  PUBLIC '-//QM/2.2/Class-Directory//EN'
+  'http://www.codesourcery.com/qm/dtds/2.2/-//qm/2.2/class-directory//en.dtd'>
+<class-directory>
+ <class kind="resource" name="gpp_init.GPPInit"/>
+ <class kind="resource" name="dg_tls_test.GPPTLSInit"/>
+ <class kind="resource" name="debug_test.GPPDebugInit"/>
+ <class kind="resource" name="debug_test.GCCDebugInit"/>
+ <class kind="resource" name="dg_tls_test.GCCTLSInit"/>
+ <class kind="resource" name="gcc_init.GCCInit"/>
+ <class kind="database" name="gcc_database.GCCDatabase"/>
+ <class kind="database" name="v3_database.V3Database"/>
+ <class kind="test" name="dg_tls_test.GPPDGTLSTest"/>
+ <class kind="test" name="gpp_dg_test.GPPDGTest"/>
+ <class kind="test" name="gpp_profile_test.GPPProfileTest"/>
+ <class kind="test" name="gpp_gcov_test.GPPGCOVTest"/>
+ <class kind="test" name="gpp_old_deja_test.GPPOldDejaTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGNoncompileTest"/>
+ <class kind="test" name="debug_test.GPPDGDebugTest"/>
+ <class kind="test" name="debug_test.GCCDGDebugTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGCPPTradTest"/>
+ <class kind="test" name="dg_pch_test.GPPDGPCHTest"/>
+ <class kind="test" name="dg_pch_test.GCCDGPCHTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGCPPTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGFormatTest"/>
+ <class kind="test" name="dg_tls_test.GCCDGTLSTest"/>
+ <class kind="test" name="gcc_dg_test.GCCDGTortureTest"/>
+ <class kind="test" name="compat_test.GCCCompatTest"/>
+ <class kind="test" name="compat_test.GPPCompatTest"/>
+ <class kind="test" name="gcc_dg_test.GCCCTortureCompileTest"/>
+ <class kind="test" name="v3_test.V3DGTest"/>
+ <class kind="test" name="v3_test.V3ABITest"/>
+ <class kind="resource" name="v3_test.V3Init"/>
+</class-directory>
Index: debug_test.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/debug_test.py,v
retrieving revision 1.2
diff -u -r1.2 debug_test.py
--- debug_test.py	24 Feb 2004 08:23:12 -0000	1.2
+++ debug_test.py	18 Mar 2004 18:33:55 -0000
@@ -103,18 +103,23 @@
 
         basename = os.path.basename(self._GetSourcePath())
             
+        def isanywhere(string, list):
+            for s in list:
+                if s.find(string) != -1:
+                    return True
+            return False
+
         self._SetUp(context)
         for opts in context[GCCDebugInit.OPTIONS_TAG]:
-            optstring = " ".join(opts)
             if (basename in ["debug-1.c", "debug-2.c", "debug-6.c"]
                 and opts[0].endswith("1")):
                 continue
             elif (basename in ["debug-1.c", "debug-2.c"]
-                  and optstring.find("03") != -1
-                  and (optstring.find("coff") != -1
-                       or optstring.find("stabs") != -1)):
+                  and isanywhere("03", opts) != -1
+                  and (isanywhere("coff", opts) != -1
+                       or isanywhere("stabs", opts) != -1)):
                 continue
-            self._RunDGTest(optstring, "", context, result)
+            self._RunDGTest(opts, [], context, result)
 
 
 
Index: dg_pch_test.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/dg_pch_test.py,v
retrieving revision 1.3
diff -u -r1.3 dg_pch_test.py
--- dg_pch_test.py	24 Feb 2004 08:23:12 -0000	1.3
+++ dg_pch_test.py	18 Mar 2004 18:33:55 -0000
@@ -65,7 +65,7 @@
                 pass
             shutil.copyfile(os.path.splitext(source)[0] + suffix + "s",
                             basename + suffix)
-            self._RunDGTest(o, "", context, result,
+            self._RunDGTest(o, [], context, result,
                             basename + suffix,
                             self.KIND_PRECOMPILE,
                             keep_output = 1)
@@ -73,15 +73,15 @@
             assembly_outcome = self.UNTESTED
             if os.path.exists(basename + suffix + ".gch"):
                 os.remove(basename + suffix)
-                options = o + " -I" + context.GetTemporaryDirectory()
-                self._RunDGTest(options, "", context, result, keep_output = 1)
+                options = o + ["-I" + context.GetTemporaryDirectory()]
+                self._RunDGTest(options, [], context, result, keep_output = 1)
                 os.remove(basename + suffix + ".gch")
                 if os.path.exists(basename + ".s"):
                     os.rename(basename + ".s", basename + ".s-gch")
                     shutil.copyfile((os.path.splitext(source)[0]
                                      + suffix + "s"),
                                     basename + suffix)
-                    self._RunDGTest(options, "", context, result,
+                    self._RunDGTest(options, [], context, result,
                                     keep_output = 1)
                     if filecmp.cmp(basename + ".s", basename + ".s-gch"):
                         assembly_outcome = self.PASS
@@ -104,7 +104,7 @@
 
     _suffix = ".h"
 
-    _pch_options = ["-O0 -g"] + GCCDGTortureTest._torture_without_loops
+    _pch_options = [["-O0", "-g"]] + GCCDGTortureTest._torture_without_loops
 
 
 
@@ -113,5 +113,5 @@
 
     _suffix = ".H"
 
-    _pch_options = ("-g", "-O2 -g", "-O2")
+    _pch_options = (["-g"], ["-O2", "-g"], ["-O2"])
     
Index: gcc_dg_test.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gcc_dg_test.py,v
retrieving revision 1.5
diff -u -r1.5 gcc_dg_test.py
--- gcc_dg_test.py	23 Jun 2003 06:34:04 -0000	1.5
+++ gcc_dg_test.py	18 Mar 2004 18:33:55 -0000
@@ -30,7 +30,7 @@
     This test class emulates the 'gcc-dg.exp' source file in the GCC
     testsuite."""
 
-    _default_options = "-ansi -pedantic-errors"
+    _default_options = ["-ansi", "-pedantic-errors"]
 
     def _DGrequire_weak(self, line_num, args, context):
         """Emulate the 'dg-require-weak' command.
@@ -99,7 +99,7 @@
     This test class emulates the 'noncompile.exp' source file in the
     GCC testsuite."""
 
-    _default_options = ""
+    _default_options = []
     
 
 
@@ -109,7 +109,7 @@
     This test class emulates the 'cpp.exp' source file in the GCC
     testsuite."""
 
-    _default_options = "-ansi -pedantic-errors"
+    _default_options = ["-ansi", "-pedantic-errors"]
 
 
 
@@ -119,24 +119,25 @@
     This test class emulates the 'trad.exp' source file in the GCC
     testsuite."""
 
-    _default_options = "-traditional-cpp"
+    _default_options = ["-traditional-cpp"]
 
 
 
 class GCCDGTortureTest(GCCDGTest):
     """A 'GCCDGTortureTest' emulates 'gcc.dg/torture/dg-torture.exp'."""
 
-    _default_options = ""
+    _default_options = []
 
     _torture_with_loops = [
-        "-O0",
-        "-O1",
-        "-O2",
-        "-O3 -fomit-frame-pointer",
-        "-O3 -fomit-frame-pointer -funroll-loops",
-        "-O3 -fomit-frame-pointer -funroll-all-loops -finline-functions",
-        "-O3 -g",
-        "-Os"
+        ["-O0"],
+        ["-O1"],
+        ["-O2"],
+        ["-O3", "-fomit-frame-pointer"],
+        ["-O3", "-fomit-frame-pointer", "-funroll-loops"],
+        ["-O3", "-fomit-frame-pointer",
+         "-funroll-all-loops", "-finline-functions"],
+        ["-O3", "-g"],
+        ["-Os"],
         ]
     """A list of command-line options to use for "torture" tests.
 
@@ -207,7 +208,7 @@
 class GCCDGFormatTest(GCCDGTortureTest):
     """A 'GCCDGFormatTest' emulates 'gcc.dg/format/format.exp'."""
 
-    _default_options = ""
+    _default_options = []
     
     _torture_with_loops = [ "", "-DWIDE" ]
 
@@ -220,4 +221,4 @@
 
     _default_kind = GCCDGTortureTest.KIND_ASSEMBLE
     
-    _default_options = "-w"
+    _default_options = ["-w"]
Index: gcc_dg_test_base.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gcc_dg_test_base.py,v
retrieving revision 1.4
diff -u -r1.4 gcc_dg_test_base.py
--- gcc_dg_test_base.py	12 Jun 2003 23:43:00 -0000	1.4
+++ gcc_dg_test_base.py	18 Mar 2004 18:33:55 -0000
@@ -63,7 +63,7 @@
     def Run(self, context, result):
 
         self._SetUp(context)
-        self._RunDGTest("", self._default_options, context, result)
+        self._RunDGTest([], self._default_options, context, result)
                         
 
     def _ExecuteFinalCommand(self, command, args, context, result):
@@ -160,7 +160,6 @@
             dirname = os.path.dirname(path)
             source_files += map(lambda f: os.path.join(dirname, f),
                                 self.__additional_source_files)
-        options = self._ParseTclWords(options)
         if "-frepo" in options:
             is_repo_test = 1
             kind = DGTest.KIND_ASSEMBLE
@@ -220,6 +219,9 @@
 
         This method emulates 'dg-scan' in the GCC testsuite."""
 
+        print "__ScanFile: %s, %s, %s" % tuple(map(repr, [command,
+                                                    output_file, args]))
+
         # See if there is a target selector applied to this test.
         expectation = self.PASS
         if len(args) > 1:
@@ -247,7 +249,13 @@
             outcome = self.PASS
         else:
             outcome = self.FAIL
-        message = self._name + " " + command + " " + pattern
+        print "Pattern is %s" % repr(pattern)
+        printable_pattern = pattern
+        for old, new in [("\\", "\\\\"), ("\n", "\\n"),
+                         ("\r", "\\r"),  ("\t", "\\t")]:
+            printable_pattern = printable_pattern.replace(old, new)
+        print "Printable pattern is %s" % repr(printable_pattern)
+        message = self._name + " " + command + " " + printable_pattern
         self._RecordDejaGNUOutcome(result, outcome, message, expectation)
         
 
Index: gcc_test_base.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gcc_test_base.py,v
retrieving revision 1.2
diff -u -r1.2 gcc_test_base.py
--- gcc_test_base.py	2 Jun 2003 23:50:18 -0000	1.2
+++ gcc_test_base.py	18 Mar 2004 18:33:56 -0000
@@ -85,39 +85,39 @@
     """A map from DejaGNU compilation modes to 'Compiler' modes."""
 
     def _RecordPass(self, result, testcase, cflags):
-	"""Emulate '${tool}_pass'.
+        """Emulate '${tool}_pass'.
 
-	'result' -- The 'Result'.
-	
-	'testcase' -- The name of the test.
-
-	'cflags' -- The options provided to the test."""
-
-	if cflags:
-	    message = "%s, %s" % (testcase, cflags)
-	else:
-	    message = testcase
-	self._RecordDejaGNUOutcome(result, DejaGNUTest.PASS, message)
+        'result' -- The 'Result'.
+        
+        'testcase' -- The name of the test.
+
+        'cflags' -- The options provided to the test."""
+
+        if cflags:
+            message = "%s, %s" % (testcase, cflags)
+        else:
+            message = testcase
+        self._RecordDejaGNUOutcome(result, DejaGNUTest.PASS, message)
 
 
     def _RecordFail(self, result, testcase, cflags):
-	"""Emulate '${tool}_fail'.
+        """Emulate '${tool}_fail'.
 
-	'result' -- The 'Result'.
-	
-	'testcase' -- The name of the test.
-
-	'cflags' -- The options provided to the test."""
-
-	if cflags:
-	    message = "%s, %s" % (testcase, cflags)
-	else:
-	    message = testcase
-	self._RecordDejaGNUOutcome(result, DejaGNUTest.FAIL, message)
+        'result' -- The 'Result'.
+        
+        'testcase' -- The name of the test.
+
+        'cflags' -- The options provided to the test."""
+
+        if cflags:
+            message = "%s, %s" % (testcase, cflags)
+        else:
+            message = testcase
+        self._RecordDejaGNUOutcome(result, DejaGNUTest.FAIL, message)
 
 
     def _Compile(self, context, result, source_files, output_file, mode,
-                 options = []):
+                 options = [], post_options = []):
         """Compile the 'source_files'.
 
         'context' -- The 'Context' in which the test is running.
@@ -175,7 +175,7 @@
         # Run the compiler.
         index = self._RecordCommand(result, command)
         status, output \
-	   = compiler.ExecuteCommand(context.GetTemporaryDirectory(), command)
+           = compiler.ExecuteCommand(context.GetTemporaryDirectory(), command)
         self._RecordCommandOutput(result, index, status, output)
                     
         # If there was no output, DejaGNU uses the exit status.
@@ -186,43 +186,43 @@
         
         
     def _CheckCompile(self, result, testcase, option, objname, gcc_output):
-	"""Check the result of a compilation.
+        """Check the result of a compilation.
+
+        'result' -- The QMTest 'Result' object.
+        
+        'testcase' -- The name of the test.
+
+        'option' -- The options used when performing the test.
+
+        'objname' -- The name of the output file.
+
+        'gcc_output' -- The output generated by the compiler.
+
+        returns -- '1' if the compilation suceeded, '0' otherwise.  If
+        '0' is returned, the 'result' has been updated to indicate the
+        problem.
+        
+        This function emulates 'g++_check_compile' in
+        'gcc-defs.exp'."""
 
-	'result' -- The QMTest 'Result' object.
-	
-	'testcase' -- The name of the test.
-
-	'option' -- The options used when performing the test.
-
-	'objname' -- The name of the output file.
-
-	'gcc_output' -- The output generated by the compiler.
-
-	returns -- '1' if the compilation suceeded, '0' otherwise.  If
-	'0' is returned, the 'result' has been updated to indicate the
-	problem.
-	
-	This function emulates 'g++_check_compile' in
-	'gcc-defs.exp'."""
-
-	match = GCCTestBase.__signal_regexp.match(gcc_output)
-	if match:
-	    self._RecordFail(result, testcase,
-			     "Got Signal %s, %s" % (match.group(1), option))
-	    return 0
+        match = GCCTestBase.__signal_regexp.match(gcc_output)
+        if match:
+            self._RecordFail(result, testcase,
+                             "Got Signal %s, %s" % (match.group(1), option))
+            return 0
 
         gcc_output = GCCTestBase.__newline_regexp.sub(gcc_output, "")
-		
+                
         if gcc_output != "":
-	    self._RecordFail(result, testcase, option)
-	    return 0
-	    
-	if objname and not os.path.exists(objname):
-	    self._RecordFail(result, testcase, option)
-	    return 0
+            self._RecordFail(result, testcase, option)
+            return 0
+            
+        if objname and not os.path.exists(objname):
+            self._RecordFail(result, testcase, option)
+            return 0
         
         self._RecordPass(result, testcase, option)
-	return 1
+        return 1
 
         
     
Index: gpp_dg_test.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gpp_dg_test.py,v
retrieving revision 1.6
diff -u -r1.6 gpp_dg_test.py
--- gpp_dg_test.py	2 Jun 2003 21:08:50 -0000	1.6
+++ gpp_dg_test.py	18 Mar 2004 18:33:56 -0000
@@ -29,7 +29,7 @@
     This test class emulates the 'g++-dg.exp' source file in the GCC
     testsuite."""
 
-    _default_options = "-ansi -pedantic-errors -Wno-long-long"
+    _default_options = ["-ansi", "-pedantic-errors", "-Wno-long-long"]
 
     def _GetTargetEnvironment(self, context):
 
Index: gpp_init.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gpp_init.py,v
retrieving revision 1.6
diff -u -r1.6 gpp_init.py
--- gpp_init.py	24 Feb 2004 08:23:12 -0000	1.6
+++ gpp_init.py	18 Mar 2004 18:33:56 -0000
@@ -20,6 +20,7 @@
 from   dejagnu_base import DejaGNUBase
 from   qm.executable import RedirectedExecutable
 from   qm.test.resource import Resource
+from   qm.test.result import Result
 import os
 import sys
 
@@ -87,8 +88,8 @@
             executable.Run(command)
             options += executable.stdout.split()
         except:
-            result.Fail("Could not run testsuite_flags")
-            result[result.EXCEPTION] = "%s: %s" % sys.exc_info()[:2]
+            result.NoteException("Could not run testsuite_flags",
+                                 Result.FAIL)
             return
 
         # Avoid splitting diagnostic message lines.
-------------- next part --------------
#!/usr/bin/python 

import sys
import os
import os.path
import shutil
import glob
import getpass
import time
from optparse import OptionParser

j = os.path.join

optparser = OptionParser("usage: %prog [options] <gcc-version> "
                         "<source-directory> <qmtc-directory> "
                         "<qmtest_gcc-directory>")
optparser.add_option("-v", "--version", action="store",
                     dest="pkg_version", default="1.0",
                     metavar="VERSION",
                     help="Package version (default 1.0)")
optparser.add_option("-o", "--output", action="store",
                     dest="targetdir", metavar="DIR",
                     help="Output directory"
                     "(default qmtest_libstdcpp_GCCVER-PKGVER)")
optparser.add_option("-f", "--force", action="store_true",
                     dest="force", default=False,
                     help="If output directory already exists, delete it")
optparser.add_option("-a", "--add-results", action="append",
                     dest="baselines", default=[],
                     help="Additional result file to distribute (may be "
                     "used multiple times)")


def usage(name):
    print "Usage: %s <full-gcc-version> <source-directory> " \
          "<destination-directory>" % name
    print "E.g., %s 3.3.3 gcc-3.3.3 my-gcc-3.3.3-package" % name
    sys.exit(2)


def ensure_dir(dir):
    # Assume we're given foo/bar/baz/quux, and foo/bar already exists.
    fragments = []
    while dir:
        if os.path.exists(dir):
            break
        dir, tail = os.path.split(dir)
        fragments.insert(0, tail)
    # Now dir == "foo/bar" and fragments == ["baz", "quux"]
    # Iterate through the fragments, building up dir and creating
    # directories.
    for frag in fragments:
        dir = j(dir, frag)
        os.mkdir(dir)
        

def main(name, args):

    options, args = optparser.parse_args(args)
    if len(args) != 4:
        optparser.error("Wrong number of arguments")

    gcc_version, srcdir, qmtcdir, qmtest_gccdir = args
    pkg_version = options.pkg_version
    targetdir = options.targetdir
    if targetdir is None:
        targetdir = "./qmtest_libstdcpp_%s-%s" % (gcc_version,
                                                  pkg_version)

    if os.path.exists(targetdir):
        if options.force:
            shutil.rmtree(targetdir)
        else:
            print "Error: Target directory '%s' already exists and " \
                  "deletion not requested" % targetdir
            sys.exit(2)

    if not gcc_version.startswith("3.3"):
        print "Error: Unsupported gcc version %s" % gcc_version
        sys.exit(2)

    v3src = j(srcdir, "libstdc++-v3")
    ensure_dir(targetdir)

    # Open a file to record which directories exist (and thus need
    # installing).
    content_files = open(j(targetdir, "contents"), "w")

    # Mark that this will be a standalone installation, for later use by
    # the QMTest scripts.
    f = open(j(targetdir, "THIS-IS-STANDALONE-V3"), "w")
    # In case we need to version stuff later:
    f.write("1\n")
    f.close()
    content_files.write("THIS-IS-STANDALONE-V3\n")

    # Copy gcc stuff over:
    shutil.copytree(j(v3src, "testsuite"), j(targetdir, "testsuite"))
    content_files.write("testsuite\n")
    shutil.copytree(j(v3src, "po"), j(targetdir, "po"))
    content_files.write("po\n")
    ensure_dir(j(targetdir, "config"))
    shutil.copytree(j(v3src, "config", "abi"),
                    j(targetdir, "config", "abi"))
    content_files.write("config\n")
    
    # gcc 3.4 has a scripts dir that we need.
    if os.path.exists(j(v3src, "scripts")):
        shutil.copytree(j(v3src, "scripts"), j(targetdir, "scripts"))
        content_files.write("scripts\n")

    # Copy in QMTest extension classes.
    ensure_dir(j(targetdir, "qm-classes"))
    content_files.write("qm-classes\n")
    shutil.copytree(qmtcdir, j(targetdir, "qm-classes", "qmtc"))
    shutil.copytree(qmtest_gccdir,
                    j(targetdir, "qm-classes", "qmtest_gcc"))
    # And then clean them up a bit (remove backup files, compiled files,
    # and CVS/ directories).
    for g in "*~", "*.pyc", "*.pyo":
        for file in glob.glob(j(targetdir, "qm-classes", "*", g)):
            os.unlink(file)
    for dir in glob.glob(j(targetdir, "qm-classes", "*", "CVS")):
        shutil.rmtree(dir)

    # Copy over any supplied baselines.
    ensure_dir(j(targetdir, "qm-baselines"))
    content_files.write("qm-baselines\n")
    for b in options.baselines:
        shutil.copyfile(b, j(targetdir, "qm-baselines",
                             os.path.basename(b)))

    # Now create the misc. files.
    miscdir = j(targetdir, "qm-misc")
    ensure_dir(miscdir)
    content_files.write("qm-misc\n")

    substitutions = {"prog_name": name,
                     "gcc_version": gcc_version,
                     "pkg_version": pkg_version,
                     "prog_args": " ".join(args),
                     "user": getpass.getuser(),
                     "time": time.strftime("%Y-%m-%d %H:%M:%S "),
                     }

    f = open(j(miscdir, "locale-Makefile"), "w")
    f.write(locale_Makefile % substitutions)
    f.close()

    f = open(j(miscdir, "util-Makefile"), "w")
    f.write(util_Makefile % substitutions)
    f.close()

    # And the distribution-level files.
    f = open(j(targetdir, "README"), "w")
    f.write(README_file % substitutions)
    f.close()

    f = open(j(targetdir, "PKGINFO"), "w")
    f.write(PKGINFO_file % substitutions)
    f.close()

    f = open(j(targetdir,
               "qmtest_libstdcpp_%(gcc_version)s.spec"
               % substitutions),
             "w")
    f.write(spec_file % substitutions)
    f.close()
    

locale_Makefile = """\
# Do not edit -- this file automatically generated by %(prog_name)s.
# Makefile to build locale files needed by libstdc++-v3 testsuite.

root := @ROOT@

podir := $(root)/po

POS := $(wildcard $(podir)/*.po)
LANGS := $(basename $(notdir $(POS)))
MOS := $(addsuffix /LC_MESSAGES/libstdc++.mo,$(LANGS))
DIRS := $(addsuffix /LC_MESSAGES,$(LANGS))

# Allow the user to set MSGFMT in their environment
MSGFMT ?= msgfmt

.PHONY: locales

locales: $(POS)
\tmkdir -p $(DIRS)
\tfor lang in $(LANGS); \\
\t    do $(MSGFMT) $(podir)/$$lang.po -o $$lang/LC_MESSAGES/libstdc++.mo; \\
\tdone

"""

util_Makefile = """\
# Do not edit -- this file automatically generated by %(prog_name)s.
# Makefile to build utility files needed by libstdc++-v3 testsuite.

root := @ROOT@
CXX := @CXX@
CXXFLAGS := @CXXFLAGS@

VPATH := $(root)/testsuite

v3test_SOURCES := testsuite_hooks.cc testsuite_allocator.cc
abi_check_SOURCES := abi_check.cc

v3test_OBJECTS := $(v3test_SOURCES:.cc=.o)

libv3test.a: $(v3test_OBJECTS)
\t$(AR) r $@ $^

abi_check: $(abi_check_SOURCES)
\t$(CXX) $(CXXFLAGS) $^ -o $@
"""

README_file = """\
GNU C++ RUNTIME LIBRARY TESTSUITE
=================================

This package contains the GNU C++ Runtime Library Testsuite for
version %(gcc_version)s of the GNU Compiler Collection.  The tests contained in
this package are the same tests distributed with the GNU Compiler
Collection.  The QMTest extension modules included in this package was
developed by CodeSourcery, LLC under contract from the Free Standards
Group.

For assistance with this software, please contact:

  support at codesourcery.com

For general information about QMTest, the software used to run these
tests, please visit:

  http://www.qmtest.com

You can also send mail to the QMTest mailing list:

  qmtest at codesourcery.com

if you have questions about QMTest.

PREREQUISITES
=============

- Python 2.2 (or later)

  Python binary packages are available for all popular GNU/Linux
  distributions and for many other operating systems.  If you do not
  already have Python, visit http://www.python.org.

- QMTest 2.2 (or later)

  As of this writing QMTest 2.2 has not yet been released.  

  A Red Hat Package Manager (RPM) distribution of a QMTest snapshot
  that works with this testsuite is available at:

    http://www.codesourcery.com/qm/qmtest_downloads/qm-20040317/qm-20040317.noarch.rpm

  The RPM is architecture-independent; it can be installed on any
  GNU/Linux system that supports the RPM format.

  You can also obtain the source for QMTest from CVS by following the
  directions at:

    http://www.codesourcery.com/qm/qmtest_development

- GNU Compiler Collection

  In order to test the GNU C++ Runtime Library, you must install
  the GNU C++ Compiler (G++) and its associated Runtime Library.

INSTALLATION
============

There are two installation methods for the Testsuite, depending on
which package format you select.  Both package formats contain
identical files; which package format you select is purely a matter of
preference.

GNU Zip Tape Archive
--------------------

Download the file:

  qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s.tar.gz

Then, unpack the file with the following command:

  tar xzf qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s.tar.gz

Red Hat Package Manager
-----------------------

Download the file:

  qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s-1.noarch.rpm

Then, install the file with the following command, which must be run
as "root":

  rpm -i qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s-1.noarch.rpm

The package will be installed in:

  /usr/share/qmtest_libstdcpp_%(gcc_version)s

USAGE
=====

The following examples all assume that you are using the Bourne shell.
If you are using an alternative shell, you may have to make minor
changes to the commands.  Furthermore, these commands assume that the
environment variable "PKGROOT" is the path to the directory in which
you installed the Testsuite.  For example, if you used the RPM
installation, "PKGROOT" should be set to
"/usr/share/qmtest_libstdcpp_%(gcc_version)s".  These examples assume that both
"qmtest" and "g++" are in your "PATH" and that "g++" is the compiler
that you wish to test.  You must also ensure that the
"LD_LIBRARY_PATH" environment variable includes the directory in which
the GNU C++ Runtime Library has been installed.  If your "g++" is in
"/path/to/bin/g++", then your "LD_LIBRARY_PATH" should include
"/path/to/lib".

There are four steps required to run the testsuite:

1. Tell QMTest where to find the extension classes required for the
   Testsuite:

     $ QMTEST_CLASS_PATH=$PKGROOT/qm-classes/qmtc:$PKGROOT/qm-classes/qmtest_gcc
     $ export QMTEST_CLASS_PATH

2. Create a test database.

     $ qmtest -D v3db create-tdb -c v3_database.V3Database \\
                                 -a srcdir=$PKGROOT/testsuite

3. Create a context file.
 
   The file should be named "v3db/context" and should contain contents
   similar to:

     CompilerTable.languages=cplusplus
     CompilerTable.cplusplus_kind=GCC
     CompilerTable.cplusplus_path=g++
     CompilerTable.cplusplus_options=
     DejaGNUTest.target=i686-pc-linux-gnu
     V3Init.scratch_dir=scratch

  You should adjust the "DejaGNUTest.target" line to indicate the GNU
  triplet for your operating system.

4. Run the tests:

     $ cd v3db
     $ qmtest run

   QMTest will display the results on the standard error stream.

   All released versions of G++ fail some of these tests.  Therefore,
   you should not be surprised to see some failures.

   If you would like the results in the TET output format, use the
   following command:

     $ qmtest run \\
         --result-stream="tet_stream.TETStream(filename='results.tet')"

   This command will write the results into the file "results.tet"
   using TET output format.

   If you would like to compare the results with the benchmark results
   available obtained with version %(gcc_version)s of the GNU Compiler
   Collection on an i686-pc-linux-gnu machine, use the
   following command:

     $ qmtest run \\
         -O $PKGROOT/qm-baselines/%(gcc_version)s_i686-pc-linux-gnu.qmr

  QMTest supports a variety of more complex usage scenarios.  For more
  information see the QMTest manual, available at:

    http://www.codesourcery.com/qm/qmtest_manual
"""

PKGINFO_file = """\
This package generated automatically by %(prog_name)s.

Invoked by %(user)s at %(time)s.
Call was:
  $ %(prog_name)s %(prog_args)s

"""

spec_file = """\
# Do not edit -- this file automatically generated by %(prog_name)s.
Summary:     libstdc++-v3 testsuite packaged to be run under QMTest.
Name:        qmtest_libstdcpp_%(gcc_version)s
Packager:    Nathaniel Smith <njs at codesourcery.com>
Copyright:   Various
Version:     %(pkg_version)s
Release:     1
Group:       Development/Tools
BuildRoot:   %%{_tmppath}/%%{name}-buildroot
Source:      qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s.tar.gz
Vendor:      CodeSourcery LLC
BuildArchitectures: noarch

%%description
This package includes the libstdc++-v3 testsuite from gcc version
%(gcc_version)s, together with QMTest extensions allowing one to run the
testsuite against an installed version of libstdc++.

%%prep
%%setup -q

%%build

%%install
rm -rf $RPM_BUILD_ROOT
DIR=$RPM_BUILD_ROOT/usr/share/qmtest_libstdcpp_%(gcc_version)s
mkdir -p $DIR
for thing in `cat contents`; do
    if [ -d "$thing" ]; then
        cp -r "$thing" "$DIR/$thing"
    else
        cp "$thing" "$DIR/$thing"
    fi
done

%%clean
rm -rf $RPM_BUILD_ROOT

%%files
# Install all files as root:
%%defattr(-,root,root)
/usr/share/qmtest_libstdcpp_%(gcc_version)s/
%%doc README

%%changelog
* Tue Mar 16 2004 Nathaniel Smith <njs at codesourcery.com> 
- Initial release.

"""

if __name__ == "__main__":
    main(sys.argv[0], sys.argv[1:])
-------------- next part --------------
########################################################################
#
# File:   v3_test.py
# Author: Nathaniel Smith
# Date:   03/08/2004
#
# Contents:
#   V3Init, V3DGTest, V3ABICheck
#
# Copyright (c) 2004 by CodeSourcery, LLC.  All rights reserved. 
#
########################################################################

########################################################################
# Imports
########################################################################

import shutil
import fnmatch
import glob
import os
import os.path
import re
import qm
from qm.executable import RedirectedExecutable
from qm.test.test import Test
from qm.test.resource import Resource
from dg_test import DGTest
from dejagnu_base import DejaGNUBase
from qm.test.result import Result
from gcc_test_base import GCCTestBase
from compiler import CompilerExecutable

########################################################################
# Classes
########################################################################

_ld_library_path_names = ["LD_LIBRARY_PATH", "SHLIB_PATH",
                          "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH",
                          "LD_RUN_PATH", "LD_LIBRARY_PATH_32",
                          "LD_LIBRARY_PATH_64", "DYLD_LIBRARY_PATH"]
"""All the different envvars that might mean LD_LIBRARY_PATH."""

class V3Init(Resource):
    """All V3 tests depend on one of these for setup."""

    def SetUp(self, context, result):

        # Get general information that will be used through the rest of
        # the setup.
        srcdir = self.GetDatabase().GetRoot()
        target = context["DejaGNUTest.target"]

        # Are we using the standalone testsuite to test an installed
        # libstdc++/g++, or the integrated testsuite to test a
        # just-built libstdc++/g++?  Check for the magic file that the
        # standalone package contains.
        standalone_marker = os.path.join(srcdir, "..",
                                         "THIS-IS-STANDALONE-V3")
        standalone = os.path.exists(standalone_marker)
        if standalone:
            standalone_root = os.path.join(srcdir, "..")
        context["V3Init.is_standalone"] = standalone

        # Find the compiler.
        compilers = context["CompilerTable.compiler_table"]
        compiler = compilers["cplusplus"]


        if not standalone:
            # Find blddir and outdir, and make outdir available to later
            # tests.
            options = compiler.GetOptions()
            compiler_executable = CompilerExecutable()
            compiler_executable.Run([compiler.GetPath()]
                                    + options
                                    + ['--print-multi-dir'])
            directory = compiler_executable.stdout[:-1]
            
            for o in options:
                if o.startswith("-B"):
                    # Calling 'normpath' is necessary to remove any possible
                    # trailing /.
                    objdir = os.path.dirname(os.path.normpath(o[2:]))
                    break
                else:
                    result.SetOutcome(result.ERROR,
                                      "Cannot find build directory; no -B in "
                                      "compiler options")
                    return

            objdir = os.path.abspath(objdir)
            blddir = os.path.normpath(os.path.join(objdir,
                                                   target,
                                                   directory,
                                                   "libstdc++-v3"))
            outdir = os.path.join(blddir, "testsuite")
        else:
            # User must provide build directory.
            # Our code always refers to this directory as 'outdir' for
            # parallelism with the DejaGNU code we emulate, but we call
            # it "scratch_dir" for UI purposes.
            if context.has_key("V3Init.outdir"):
                result.SetOutcome(result.ERROR,
                                  "Set V3Init.scratch_dir, not outdir")
                return
            outdir = context["V3Init.scratch_dir"]
            outdir = os.path.abspath(outdir)
            if not os.path.exists(outdir):
                os.mkdir(outdir)
            
        context["V3Init.outdir"] = outdir

#         print "options = %s" % repr(options)
#         print "directory = '%s'" % directory
#         print "objdir = '%s'" % objdir
#         print "target = '%s'" % target
#         print "blddir = '%s'" % blddir
#         print "outdir = '%s'" % outdir
#         print "srcdir = '%s'" % srcdir

        # Ensure that the message format files are available.
        # This requires different commands depending on whether we're
        # using the gcc build system or not.
        if not standalone:
            locale_dir = os.path.join(blddir, "po")
            make_command = ["make", "-j1", "check"]
        else:
            # Standalone build needs to set up the locale stuff in its
            # own directory.
            locale_dir = os.path.join(outdir, "qm_locale")
            try:
                os.mkdir(locale_dir)
            except OSError:
                pass
            makefile_in = open(os.path.join(standalone_root,
                                                  "qm-misc",
                                                  "locale-Makefile"))
            makefile_str = makefile_in.read()
            makefile_str = makefile_str.replace("@ROOT@",
                                                standalone_root)
            makefile_out = open(os.path.join(locale_dir, "Makefile"),
                                "w")
            makefile_out.write(makefile_str)
            makefile_out.close()
            make_command = ["make", "-j1", "locales"]

        make_executable = RedirectedExecutable()
        status = make_executable.Run(make_command, dir=locale_dir)
        if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
            result.SetOutcome(result.ERROR,
                              "Error building locale information",
                              {"status": str(status),
                               "stdout": "<pre>"
                                         + make_executable.stdout
                                         + "</pre>",
                               "stderr": "<pre>"
                                         + make_executable.stderr
                                         + "</pre>",
                               "command": " ".join(make_command),
                               })
            return
            

        # Copy data files.
        for file in "*.tst", "*.txt":
            if os.path.isdir(os.path.join(srcdir, "data")):
                # 3.4+ store these files in a special data/ directory.
                subdirs = ["data"]
            else:
                # But earlier versions store them scattered through the
                # tree.
                subdirs = ["*", os.path.join("*", "*")]
            for subdir in subdirs:
                for f in glob.glob(os.path.join(srcdir, subdir, file)):
                    shutil.copy(f, outdir)
        
        # Set up environment and -L switches.
        for name in _ld_library_path_names:
            if os.environ.has_key(name):
                original_ld_library_path = os.environ[name].split(":")
                break
        else:
            original_ld_library_path = []
        libpaths = []
        # Each branch sets ld_library_path and modifies libpaths.
        if not standalone:
            gccdir = os.path.join(objdir, "gcc")
            libpaths.append(gccdir)
            command = compiler.GetPath()
            compiler_executable.Run([compiler.GetPath()]
                                    + options
                                    + ["--print-multi-lib"])
            for line in compiler_executable.stdout.split():
                dir, args = line.split(";", 1)
                if dir == ".":
                    continue
                if glob.glob(os.path.join(gccdir, dir, "libgcc_s*.so.*")):
                    libpaths.append(dir)

            libpaths.append(os.path.join(blddir, "src", ".libs"))
            ld_library_path = ":".join(libpaths + original_ld_library_path)
        else:

            ld_library_path = ":".join(original_ld_library_path)

        libpaths.append(outdir)
        context["V3Init.libpaths"] = libpaths
        context["V3Init.ld_library_path"] = ld_library_path
        result["V3Init.ld_library_path"] = ld_library_path

        # Calculate default g++ flags.  Both branches create basic_flags
        # and default_flags.
        if not standalone:
            # Use the build tree mechanisms.
            try:
                all_flags = self._CalcBuildTreeFlags(result, context,
                                                     blddir, compiler)
            except:
                result.NoteException(cause="Error calculating default flags",
                                     outcome=Result.FAIL)
                return
            basic_flags, default_flags = all_flags
        else:
            # We take the union of the 3.3 and the 3.4 defines; it
            # doesn't seem to hurt.
            basic_flags = [# v3.4 only:
                           "-D_GLIBCXX_ASSERT",
                           # v3.3 only:
                           "-DDEBUG_ASSERT",
                           # Common:
                           "-g", "-O2",
                           "-ffunction-sections", "-fdata-sections",
                           "-fmessage-length=0",
                           "-DLOCALEDIR=\"%s\"" % locale_dir,
                           "-I%s" % srcdir]
            default_flags = []

        default_flags.append("-D_GLIBCXX_ASSERT")
        if fnmatch.fnmatch(context["DejaGNUTest.target"],
                           "powerpc-*-darwin*"):
            default_flags += ["-multiply_defined", "suppress"]
        context["V3Init.basic_cxx_flags"] = basic_flags
        context["V3Init.default_cxx_flags"] = default_flags
        
        if standalone:
            # Build libv3test.a.
            makefile_in = open(os.path.join(standalone_root,
                                            "qm-misc",
                                            "util-Makefile"))
            makefile_str = makefile_in.read()
            makefile_str = makefile_str.replace("@ROOT@",
                                                standalone_root)
            makefile_str = makefile_str.replace("@CXX@",
                                                compiler.GetPath())
            flags = compiler.GetOptions() + basic_flags
            makefile_str = makefile_str.replace("@CXXFLAGS@",
                                                " ".join(flags))
            makefile_out = open(os.path.join(outdir, "Makefile"), "w")
            makefile_out.write(makefile_str)
            makefile_out.close()
            
            make_executable = RedirectedExecutable()
            make_command = ["make", "libv3test.a"]
            status = make_executable.Run(make_command, dir=outdir)
            if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
                result.SetOutcome(result.ERROR,
                                  "Error building libv3test.a",
                                  {"status": str(status),
                                   "stdout": "<pre>"
                                             + make_executable.stdout
                                             + "</pre>",
                                   "stderr": "<pre>"
                                             + make_executable.stderr
                                             + "</pre>",
                                   "command": " ".join(make_command),
                                   })
                return

        
    def _CalcBuildTreeFlags(self, result, context, blddir, compiler):
        """This function emulates a bit of normal.exp and a bit of
        v3-init."""

        basic_flags = []
        default_flags = []

        # Find the command to use.
        for subdir in "", "scripts":
            command = os.path.join(blddir, subdir, "testsuite_flags")
            if os.path.isfile(command):
                break

        result["V3Init.testsuite_flags_command"] = \
            "<pre>" + command + "</pre>"

        executable = RedirectedExecutable()
        executable.Run([command, "--cxxflags"])
        basic_flags += executable.stdout.split()
        executable.Run([command, "--build-includes"])
        basic_flags += executable.stdout.split()

        # 'normal.exp' checks for the existence of 'testsuite_flags' and
        # pretends the output is "" if it doesn't exist; we simply
        # assume it always exists.
        executable.Run([command, "--cxxpchflags"])
        if executable.stdout.find("sage:") != -1:
            # This 'testsuite_flags' does not support --cxxpchflags.
            pass
        else:
            default_flags += executable.stdout.split()

        return (basic_flags, default_flags)

# dg-runtest calls dg-test calls "libstdc++-dg-test prog do_what
# DEFAULT_CXXFLAGS" (DEFAULT_CXXFLAGS as in normal.exp)
# Which calls
#   v3_target_compile $prog $output_file $compile_type additional_flags=$DEFAULT_CXXFLAGS
# Which sets cxx_final to "$cxx [libgloss_link_flags] $cxxflags $includes"
# then calls
#   target_compile $prog $output_file $compile_type additional_flags=$DEFAULT_CXXFLAGS,compiler=$cxx_final,ldflags=-L$blddir/testsuite,libs=-lv3test
# for us, libgloss doesn't exist, which simplifies things.

class V3DGTest(DGTest, GCCTestBase):
    """A 'V3DGTest' is a libstdc++-v3 test using the 'dg' driver.

    This test class emulates the 'lib/libstdc++.exp' and 'lib/prune.exp
    and 'libstdc++-dg/normal.exp' source files in the libstdc++-v3
    testsuite."""

    _default_kind = DGTest.KIND_RUN

    _language = "cplusplus"

    _libdir_context_property = "V3Init.libpaths"

    def Run(self, context, result):

        self._SetUp(context)
        self._RunDGTest(context["V3Init.basic_cxx_flags"],
                        context["V3Init.default_cxx_flags"],
                        context,
                        result)
        

    def _PruneOutput(self, output):
        """This method emulates 'prune.exp'."""

        # Prune out Cygwin warnings and parts of warnings that refer to
        # location of previous definitions etc.
        output = re.sub(r"(^|\n)[^\n]*: -ffunction-sections may affect "
                        r"debugging on some targets[^\n]",
                        "", output)
        output = re.sub(r"(^|\n)[^\n]*: In function [^\n]*", "", output)
        return output


    def _GetTargetEnvironment(self, context):

        env = {}
        for name in _ld_library_path_names:
            env[name] = context["V3Init.ld_library_path"]
        return env


    def _RunTargetExecutable(self, context, result, file, dir = None):

        if dir is None:
            dir = context["V3Init.outdir"]

        sup = super(V3DGTest, self)
        return sup._RunTargetExecutable(context, result, file, dir)


    def _RunTool(self, path, kind, options, context, result):
        """This method emulates libstdc++-dg-test."""

        source_files = [path]
        
        file = self._GetOutputFile(context, kind, path)
        kind = self._test_kind_map[kind]

        if kind == GCCTestBase.KIND_EXECUTABLE:
            source_files += ["-lv3test"]

        output = self._Compile(context, result, source_files, file,
                               kind, options)
        return (output, file)


    def _GetOutputFile(self, context, kind, path):

        base = os.path.basename(path)
        if kind != self.KIND_PRECOMPILE:
            base = os.path.splitext(base)[0]
        base += { DGTest.KIND_PREPROCESS : ".i",
                  DGTest.KIND_COMPILE : ".s",
                  DGTest.KIND_ASSEMBLE : ".o",
                  DGTest.KIND_LINK: ".exe",
                  DGTest.KIND_RUN : ".exe",
                  GCCTestBase.KIND_PRECOMPILE : ".gch",
                  }[kind]

        return os.path.join(context.GetTemporaryDirectory(), base)


    def _DGrequire_iconv(self, line_num, args, context):
        """Emulate the 'dg-require-iconv' command.

        Emulates code from 'libstdc++-v3/testsuite/lib/dg-options.exp'
        and 'gcc/testsuite/lib/target-supports.exp'.
        
        'line_num' -- The number at which the command was found.

        'args' -- The arguments to the command, as a list of
        strings.

        'context' -- The 'Context' in which the test is running."""

        # Parse arguments.
        if len(args) != 1:
            self._Error("dg-require-iconv: wrong number of arguments")
            return

        charset = args[0]

        # Check to see if iconv does exist and work.
        # First by creating and compiling a test program...
        tmpdir = context.GetTemporaryDirectory()
        tmpc = os.path.join(tmpdir, "tmp.c")
        tmpx = os.path.join(tmpdir, "tmp.x")
        f = open(tmpc, "w")
        f.write("""\
#include <iconv.h>
int main (void)
{
    iconv_t cd;
    cd = iconv_open("%(charset)s", "UTF-8");
    if (cd == (iconv_t) -1)
        return 1;
    return 0;
}
"""
                % {"charset": charset})
        f.close()

        compiler = context["CompilerTable.compiler_table"][self._language]
        options = []

        options += context["V3Init.basic_cxx_flags"]
        options += context["V3Init.default_cxx_flags"]
        libpaths = context["V3Init.libpaths"]
        options += ["-L" + p for p in libpaths]

        if context.has_key("GCCTest.libiconv"):
            libiconv_opts = context["GCCTest.libiconv"].split()
        else:
            libiconv_opts = []
        
        (status, output) = compiler.Compile(compiler.MODE_LINK,
                                            [tmpc] + libiconv_opts,
                                            tmpdir, options, tmpx)
        if output == "":
            # ...and then running it, if there are no errors.
            executable = self.TargetExecutable(self.executable_timeout)
            command = [tmpx]
            environment = self._GetTargetEnvironment(context)
            status = executable.Run(command, environment, tmpdir)
            if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
                # We have working libiconv.  Continue as normal.
                return

        # Something went wrong somewhere -- libiconv is not available.
        # Turn off the test.
        self._selected = 0
        # Not clear that setting the expectation here serves any
        # purpose, but it's what 'dg-options.exp' does, so we do too.
        self._expectation = Result.PASS



# check-abi first builds
#    abi_check
#    baseline_symbols
#    current_symbols.txt
# then does
#    ./abi_check --check(-verbose) ./current_symbols.txt ${baseline_file}
#
# abi_check is built by automake as a program.
# baseline_symbols just checks to see if a baseline file exists
# current_symbols.txt depends on ${extract_symvers} ../src/.libs/libstdc++.so
#
#
# new-abi-baseline is what actually generates a new baseline.
# it does it with ${extract_symvers} ../src/.libs/libstdc++.so ${baseline_file}
# baseline_file = ${baseline_dir}/baseline_symbols.txt
# baseline_dir is set by autoconf to some mad thing...
#    $glibcxx_srcdir/config/abi/${abi_baseline_pair}\$(MULTISUBDIR)"
# abi_baseline_pair is set by autoconf to host_cpu-host_os by default.
# but there are some special cases, in particular:
#    x86_64-*-linux*     -> x86_64-linux-gnu
#    alpha*-*-freebsd5*  -> alpha-freebsd5
#    i*86-*-freebsd4*    -> i386-freebsd4
#    i*86-*-freebsd5*    -> i386-freebsd5
#    sparc*-*-freebsd5*  -> sparc-freebsd5
#
# extract_symvers = $(glibcxx_srcdir)/scripts/extract_symvers
# extract_symvers is actually just a shell script
        
class V3ABITest(Test):
    """A 'V3ABITest' checks the ABI of libstdc++ against a baseline.

    Depends on context variable 'V3Test.abi_baseline_file'."""

    def Run(self, context, result):

        # Some variables we'll need throughout.
        executable = RedirectedExecutable()
        tmpdir = context.GetTemporaryDirectory()
        outdir = context["V3Init.outdir"]
        srcdir = self.GetDatabase().GetRoot()

        # First we make sure that the abi_check program exists.
        abi_check = os.path.join(outdir, "abi_check")
        status = executable.Run(["make", "abi_check"], dir=outdir)
        result["make_abi_check_stdout"] = ("<pre>" + executable.stdout
                                           + "</pre>")
        result["make_abi_check_stderr"] = ("<pre>" + executable.stderr
                                           + "</pre>")
        result["make_abi_check_status"] = str(status)
        if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
            result.SetOutcome(result.ERROR, "Error building abi_check")
            return
        if not os.path.isfile(abi_check):
            result.SetOutcome(result.ERROR,
                              "No abi_check program '%s'" % abi_check)
            return

        # Now make sure the baseline file exists.
        baseline_type = self._GetAbiName(context["DejaGNUTest.target"])
        baseline_file = os.path.join(srcdir, "..", "config", "abi",
                                     baseline_type,
                                     "baseline_symbols.txt")
        result["baseline_file"] = baseline_file
        if not os.path.isfile(baseline_file):
            result.SetOutcome(result.ERROR,
                              "No baseline file '%s'" % baseline_file)
            return

        # Check that we have the 'extract_symvers' script.
        # 3.4+ stores it in scripts; 3.3 stores it in config/abi.
        subdirs = ["scripts", os.path.join("config", "abi")]
        for subdir in subdirs:
            extract_symvers = os.path.join(srcdir, "..",
                                           subdir,
                                           "extract_symvers")
            if os.path.isfile(extract_symvers):
                break
        else:
            result.SetOutcome(result.ERROR,
                              "Can't find extract_symvers")
            return

        # Extract the current symbols.
        # First use ldd to find the libstdc++ in use.
        status = executable.Run(["ldd", "abi_check"], dir=outdir)
        result["ldd_stdout"] = ("<pre>" + executable.stdout
                                            + "</pre>")
        result["ldd_stderr"] = ("<pre>" + executable.stderr
                                            + "</pre>")
        result["ldd_status"] = str(status)
        if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
            result.SetOutcome(result.ERROR,
                              "Error running ldd to find libstdc++")
            return
        for token in executable.stdout.split():
            if os.sep in token and token.find("libstdc++") != -1:
                libstdcpp = token
                break
        else:
            result.SetOutcome(result.ERROR,
                              "Could not find path to libstdc++ in "
                              "ldd output")
            return
#         libstdcpp = os.path.join(outdir, "..", "src", ".libs",
#                                  "libstdc++.so")
        result["libstdcpp_path"] = libstdcpp

        curr_symbols = os.path.join(tmpdir, "current_symbols.txt")
        status = executable.Run([extract_symvers,
                                 libstdcpp,
                                 curr_symbols])
        result["extract_symvers_stdout"] = ("<pre>" + executable.stdout
                                            + "</pre>")
        result["extract_symvers_stderr"] = ("<pre>" + executable.stderr
                                            + "</pre>")
        result["extract_symvers_status"] = str(status)
        if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
            result.SetOutcome(result.ERROR, "Error extracting symbols")
            return
        if not os.path.isfile(curr_symbols):
            result.SetOutcome(result.ERROR, "No symbols extracted")
            return

        # We have the checker program, we have the baseline, we have the
        # current symbols.  Now we use the former to compare the
        # latter.
        status = executable.Run([abi_check, "--check-verbose",
                                 curr_symbols, baseline_file])
        result["comparison_stdout"] = ("<pre>" + executable.stdout
                                            + "</pre>")
        result["comparison_stderr"] = ("<pre>" + executable.stderr
                                            + "</pre>")
        result["comparison_status"] = str(status)
        if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
            result.SetOutcome(result.ERROR,
                              "Error comparing symbols to baseline")
            return

        # Parse the output.
        for line in executable.stdout.split("\n"):
            if line.startswith("# of "):
                num_changes_str = line.split(":", 1)[1].strip()
                num_changes = int(num_changes_str)
                if num_changes != 0:
                    result.Fail("Changes against ABI baseline detected")
                    result["failing_line"] = line.strip()
                    return


    def _GetAbiName(self, host):
        """Map a target triple to a abi directory name.

        Emulates 'configure.host'."""

        cpu, vendor, os = host.split("-", 2)
        m = fnmatch.fnmatch
        if m(cpu, "alpha*"):
            cpu = "alpha"
        elif m(cpu, "i[567]86") or m(cpu, "x86_64"):
            cpu = "i486"
        elif m(cpu, "hppa*"):
            cpu = "hppa"
        elif m(cpu, "powerpc*") or m(cpu, "rs6000"):
            cpu = "powerpc"
        elif m(cpu, "s390x"):
            cpu = "s390"
        elif m(cpu, "sparc*") or m(cpu, "ultrasparc"):
            cpu = "sparc"

        return "%s-%s" % (cpu, os)
            
-------------- next part --------------
########################################################################
#
# File:   v3_database.py
# Author: Nathaniel Smith
# Date:   03/01/2004
#
# Contents:
#   V3Database
#
# Copyright (c) 2004 by CodeSourcery, LLC.  All rights reserved. 
#
########################################################################

########################################################################
# Imports
########################################################################

import fnmatch
import os
import qm
import qm.test.base
from   qm.attachment import Attachment, FileAttachmentStore
from   qm.test.database import ResourceDescriptor, TestDescriptor
from   qm.test.file_database import FileDatabase
from   qm.test.runnable import Runnable

########################################################################
# Classes
########################################################################

class V3Database(FileDatabase):
    """A 'V3Database' stores the libstc++-v3 regression tests."""

    arguments = [
        qm.fields.TextField(
            name = "srcdir",
            title = "Source Directory",
            description ="""The root of the libstdc++-v3 test source directory.

            This directory is the one named 'testsuite'."""),
        # The libstdc++ database uses filenames as labels.
        qm.fields.TextField(
            name = "label_class",
            default_value = "file_label.FileLabel",
            computed = "true"
            ),
        # The libstdc++ database cannot be modified programmatically.
        qm.fields.BooleanField(
            name = "modifiable",
            default_value = "false",
            computed = "true",
            ),
        ]
    
    def __init__(self, path, arguments):

        # Initialize the base class.
        super(V3Database, self).__init__(path, arguments)
        # Create an attachment store.
        self.__store = FileAttachmentStore()

        
    def GetResource(self, resource_id):

        if resource_id == "compiler_table":
            return ResourceDescriptor(self, resource_id,
                                      "compiler_table.CompilerTable",
                                      {})
        elif resource_id == "v3_init":
            return ResourceDescriptor(self, resource_id,
                                      "v3_test.V3Init",
                                      { Runnable.RESOURCE_FIELD_ID :
                                        ["compiler_table"] })

        raise self.NoSuchResourceError, resource_id
        
        
    def GetRoot(self):

        return self.srcdir


    def GetAttachmentStore(self):

        return self.__store


    def GetTestIds(self, directory="", scan_subdirs=1):

        result = super(V3Database, self).GetTestIds(directory,
                                                    scan_subdirs)
        if directory == "":
            return result + ["v3_abi_test"]
        else:
            return result


    def GetTest(self, test_id):

        if test_id == "v3_abi_test":
            return TestDescriptor(self, test_id,
                                  "v3_test.V3ABITest",
                                  { Runnable.RESOURCE_FIELD_ID:
                                    ["v3_init"]})
        else:
            return super(V3Database, self).GetTest(test_id)
        

    def _GetTestFromPath(self, test_id, path):

        # Construct the attachment representing the primary source
        # file.
        basename = os.path.basename(path)
        attachment = Attachment("text/plain", basename,
                                basename, path,
                                self.GetAttachmentStore())

        # Create the test descriptor.
        resources = ["v3_init"]
        descriptor = TestDescriptor(self, test_id,
                                    "v3_test.V3DGTest",
                                    { 'source_file' : attachment,
                                      Runnable.RESOURCE_FIELD_ID :
                                        resources })

        return descriptor
        
                

    def _IsResourceFile(self, path):

        # No resources are stored in files.
        return False

        
    def _IsSuiteFile(self, path):

        # All directories are suites.
        return os.path.isdir(path)

        
    def _IsTestFile(self, path):
        """This function emulates scripts/create_testsuite_files."""

        assert path.startswith(self.GetRoot() + os.sep)

        rel_path = path[len(self.GetRoot()) + 1:]
        if os.sep not in rel_path:
            return False

        if not rel_path.endswith(".cc"):
            return False

        forbidden_substrings = ["_xin", "performance"]
        # FIXME: create_testsuite_files checks to see if wchar_t support
        # is enabled (by checking for the existence of
        # $outdir/testsuite_wchar_t), and if it isn't, then "wchar_t" is
        # added to the forbidden list.  The right way to handle this in
        # QMTest is not obvious, so for now we ignore this.
        for f in forbidden_substrings:
            if rel_path.find(f) != -1:
                return False

        return True


More information about the qmtest mailing list