[PATCH] qmtest_gcc updates

Nathaniel Smith njs at pobox.com
Tue Mar 30 18:27:14 UTC 2004


- Rework V3 testsuite to handle the no-compiler scenario.
- Use new Result.Quote method.

-- Nathaniel

-- 
"...All of this suggests that if we wished to find a modern-day model
for British and American speech of the late eighteenth century, we could
probably do no better than Yosemite Sam."
-------------- next part --------------
Index: build_v3_dist
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/build_v3_dist,v
retrieving revision 1.1
diff -u -r1.1 build_v3_dist
--- build_v3_dist	18 Mar 2004 18:34:30 -0000	1.1
+++ build_v3_dist	30 Mar 2004 18:09:13 -0000
@@ -1,4 +1,6 @@
-#!/usr/bin/python 
+#!/usr/bin/env python 
+
+# Note that this script must be run with Python 2.3.
 
 import sys
 import os
@@ -20,8 +22,8 @@
                      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)")
+                     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")
@@ -29,13 +31,10 @@
                      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)
+optparser.add_option("-c", "--config-guess", action="store",
+                     dest="config_guess", metavar="FILE",
+                     help="Path to config.guess "
+                     "(default: search in /usr/share/)")
 
 
 def ensure_dir(dir):
@@ -54,8 +53,36 @@
         os.mkdir(dir)
         
 
-def main(name, args):
+def add(source_file_or_dir, *target_path):
+    dest = os.path.join(*target_path)
+    ensure_dir(os.path.dirname(dest))
+    if os.path.isfile(source_file_or_dir):
+        shutil.copy(source_file_or_dir, dest)
+    elif os.path.isdir(source_file_or_dir):
+        shutil.copytree(source_file_or_dir, dest)
+    else:
+        assert 0, "add() must be given a file or directory"
+
+
+def generate(source_string, substitutions, *target_path):
+    dest = os.path.join(*target_path)
+    ensure_dir(os.path.dirname(dest))
+    f = open(dest, "w")
+    f.write(source_string % substitutions)
+    f.close()
+
 
+class InstallRecorder(object):
+    def __init__(self, file_to_record_in):
+        ensure_dir(os.path.dirname(file_to_record_in))
+        self._file = open(file_to_record_in, "w")
+    def __call__(self, path):
+        self._file.write("%s\n" % path)
+
+
+def main(fullname, args):
+
+    name = os.path.basename(fullname)
     options, args = optparser.parse_args(args)
     if len(args) != 4:
         optparser.error("Wrong number of arguments")
@@ -80,41 +107,46 @@
         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")
+    install = InstallRecorder(j(targetdir, "share-contents"))
 
-    # 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")
+    # Put 'config.guess' in.
+    if options.config_guess is None:
+        config_guesses = glob.glob("/usr/share/*/config.guess")
+        if not config_guesses:
+            optparser.error("Cannot find config.guess, use "
+                            "--config-guess") 
+        config_guess = config_guesses[0]
+    else:
+        config_guess = options.config_guess
+    add(config_guess, targetdir, "config.guess")
+
+    # Mark that this will be a standalone installation, for later
+    # detection by the QMTest harness.  We write the numeral '1' in case
+    # we need versioning information later.
+    generate("1\n", {}, targetdir, "THIS-IS-STANDALONE-V3")
+    install("THIS-IS-STANDALONE-V3")
 
     # 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")
+    add(j(v3src, "testsuite"), targetdir, "testsuite")
+    install("testsuite")
+    add(j(v3src, "po"), targetdir, "po")
+    install("po")
     ensure_dir(j(targetdir, "config"))
-    shutil.copytree(j(v3src, "config", "abi"),
-                    j(targetdir, "config", "abi"))
-    content_files.write("config\n")
+    add(j(v3src, "config", "abi"), targetdir, "config", "abi")
+    install("config")
     
     # 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")
+        add(j(v3src, "scripts"), targetdir, "scripts")
+        install("scripts")
 
     # 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"))
+    add(qmtcdir, targetdir, "qm-classes", "qmtc")
+    add(qmtest_gccdir, targetdir, "qm-classes", "qmtest_gcc")
+    install("qm-classes")
     # And then clean them up a bit (remove backup files, compiled files,
     # and CVS/ directories).
     for g in "*~", "*.pyc", "*.pyo":
@@ -125,49 +157,51 @@
 
     # Copy over any supplied baselines.
     ensure_dir(j(targetdir, "qm-baselines"))
-    content_files.write("qm-baselines\n")
+    install("qm-baselines")
     for b in options.baselines:
-        shutil.copyfile(b, j(targetdir, "qm-baselines",
-                             os.path.basename(b)))
+        add(b, 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")
+    # Copy this script into the package.
+    add(__file__, targetdir, "build_v3_dist")
 
+    # Set up the substitutions dict used by all our templates.
     substitutions = {"prog_name": name,
+                     "prog_fullname": fullname,
                      "gcc_version": gcc_version,
                      "pkg_version": pkg_version,
                      "prog_args": " ".join(args),
                      "user": getpass.getuser(),
-                     "time": time.strftime("%Y-%m-%d %H:%M:%S "),
+                     "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)
+    # Munge testsuite_hooks.h to make testsuite executables
+    # relocatable.
+    f = open(j(targetdir, "testsuite", "testsuite_hooks.h"), "a")
+    f.write(testsuite_hooks_addendum % substitutions)
     f.close()
+    
+    # Now create the misc. files.
+    miscdir = j(targetdir, "qm-misc")
+    ensure_dir(miscdir)
+    install("qm-misc")
 
-    # And the distribution-level files.
-    f = open(j(targetdir, "README"), "w")
-    f.write(README_file % substitutions)
-    f.close()
+    generate(locale_Makefile, substitutions, miscdir, "locale-Makefile")
 
-    f = open(j(targetdir, "PKGINFO"), "w")
-    f.write(PKGINFO_file % substitutions)
-    f.close()
+    generate(util_Makefile, substitutions, miscdir, "util-Makefile")
 
-    f = open(j(targetdir,
-               "qmtest_libstdcpp_%(gcc_version)s.spec"
-               % substitutions),
-             "w")
-    f.write(spec_file % substitutions)
-    f.close()
+    # And the distribution-level files.
+    generate(README_file, substitutions, targetdir, "README")
+    generate(PKGINFO_file, substitutions, targetdir, "PKGINFO")
+    generate(spec_file, substitutions,
+             targetdir, "qmtest_libstdcpp_%(gcc_version)s.spec"
+                        % substitutions)
+    generate(build_binary_testsuite_file, substitutions,
+             targetdir, "build_binary_testsuite")
+    os.chmod(j(targetdir, "build_binary_testsuite"), 0755)
     
 
+## All the templates for generated files:
+
 locale_Makefile = """\
 # Do not edit -- this file automatically generated by %(prog_name)s.
 # Makefile to build locale files needed by libstdc++-v3 testsuite.
@@ -348,7 +382,7 @@
      CompilerTable.cplusplus_path=g++
      CompilerTable.cplusplus_options=
      DejaGNUTest.target=i686-pc-linux-gnu
-     V3Init.scratch_dir=scratch
+     V3Test.scratch_dir=scratch
 
   You should adjust the "DejaGNUTest.target" line to indicate the GNU
   triplet for your operating system.
@@ -386,12 +420,23 @@
     http://www.codesourcery.com/qm/qmtest_manual
 """
 
+testsuite_hooks_addendum = """
+
+// Following added automatically by %(prog_name)s:
+#ifdef LOCALEDIR
+#error "LOCALEDIR should not be defined for standalone testing; \
+set environment variable V3_LOCALEDIR instead"
+#endif
+#include <cstdlib>
+#define LOCALEDIR (std::getenv("V3_LOCALEDIR"))
+"""
+
 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
+  $ %(prog_fullname)s %(prog_args)s
 
 """
 
@@ -407,7 +452,10 @@
 BuildRoot:   %%{_tmppath}/%%{name}-buildroot
 Source:      qmtest_libstdcpp_%(gcc_version)s-%(pkg_version)s.tar.gz
 Vendor:      CodeSourcery LLC
-BuildArchitectures: noarch
+# If we let RPM detect dependencies, it will get the idea that this
+# package requires a specific version of libstdc++.so, and that is
+# unhelpful:
+AutoReqProv: no
 
 %%description
 This package includes the libstdc++-v3 testsuite from gcc version
@@ -421,16 +469,26 @@
 
 %%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
+
+# We do the building first, because some of the files generated will be
+# installed later.
+LIBDIR=$RPM_BUILD_ROOT/usr/lib/qmtest_libstdcpp_%(gcc_version)s
+mkdir -p `dirname $LIBDIR`
+./build_binary_testsuite $LIBDIR 2>&1 | tee BUILD-DETAILS
+
+SHAREDIR=$RPM_BUILD_ROOT/usr/share/qmtest_libstdcpp_%(gcc_version)s
+mkdir -p $SHAREDIR
+for thing in `cat share-contents`; do
     if [ -d "$thing" ]; then
-        cp -r "$thing" "$DIR/$thing"
+        cp -r "$thing" "$SHAREDIR/$thing"
     else
-        cp "$thing" "$DIR/$thing"
+        cp "$thing" "$SHAREDIR/$thing"
     fi
 done
 
+find $LIBDIR -name '*.pyc' -o -name '*.pyo' -print0 | xargs -0 rm -f
+find $SHAREDIR -name '*.pyc' -o -name '*.pyo' -print0 | xargs -0 rm -f
+
 %%clean
 rm -rf $RPM_BUILD_ROOT
 
@@ -438,13 +496,239 @@
 # Install all files as root:
 %%defattr(-,root,root)
 /usr/share/qmtest_libstdcpp_%(gcc_version)s/
+/usr/lib/qmtest_libstdcpp_%(gcc_version)s/
 %%doc README
+%%doc PKGINFO
+%%doc BUILD-DETAILS
+%%doc executable-gen.qmr
+%%doc build_v3_dist
+%%doc build_binary_testsuite
 
 %%changelog
+* Mon Mar 29 2004 Nathaniel Smith <njs at codesourcery.com> 
+- Rework for no-compiler version of testsuite.
+
 * Tue Mar 16 2004 Nathaniel Smith <njs at codesourcery.com> 
 - Initial release.
 
 """
 
+
+build_binary_testsuite_file = """\
+#!/usr/bin/env python
+
+# This script builds the executables needed for testing libstdc++
+# without a compiler, and then runs the tests to generate a baseline.
+# You should ensure that a canonical version of g++ is in your PATH, and
+# a canonical version of libstdc++ in your LD_LIBRARY_PATH, before
+# running this script; they will be taken as the gold standard against
+# which tested versions will be compared.
+#
+# It must be run from the directory that contains the standalone V3
+# distribution.
+
+usage = \"\"\"\\
+Usage:
+    %%(progname)s [executable-output-directory] [g++ to use] \\\\
+       [directory containing libstdc++ to use]
+If the first argument is not given, it defaults to "qm-executables".  If
+the last two arguments are not given, defaults will be found in
+PATH/LD_LIBRARY_PATH.
+\"\"\"
+
+import sys
+import os
+import os.path
+import tempfile
+import shutil
+import atexit
+import glob
+
+def error(*msgs):
+    sys.stderr.write("ERROR: " + "".join(msgs) + "\\n")
+    sys.stderr.flush()
+
+def log(*msgs):
+    prefix = "%%s: " %% progname
+    sys.stdout.write(prefix  + "".join(msgs) + "\\n")
+    sys.stdout.flush()
+
+def run_and_log(cmdline, failure_ok=False):
+    log("Running command: %%s" %% cmdline)
+    log("Output:")
+    status = os.system(cmdline)
+    if status != 0 and not failure_ok:
+        error("Command did not complete successfully.")
+        error("Exit status: %%i" %% status)
+        sys.exit(1)
+    log("Execution complete, status = %%i." %% status)
+    return status
+
+def resolve_executable(name):
+    if os.path.isabs(name):
+        return name
+    if os.sep in name:
+        return os.path.abspath(name)
+    log("Searching PATH for %%s." %% name)
+    path = os.environ.get("PATH", "").split(os.pathsep)
+    for dir in path:
+        candidate = os.path.join(dir, name)
+        if os.path.exists(candidate):
+            return os.path.abspath(candidate)
+    error("Cannot find executable %%s." %% name)
+    sys.exit(1)
+
+if not os.path.exists("THIS-IS-STANDALONE-V3"):
+    error("must run from root of standalone libstdc++ test "
+          "distribution.")
+    sys.exit(2)
+
+# This global variable is used directly by log().
+full_progname = sys.argv[0]
+progname = os.path.basename(full_progname)
+args = sys.argv[1:]
+
+log("Called as: %%s %%s" %% (full_progname, " ".join(args)))
+
+## Process arguments.
+if not 0 <= len(args) <= 3:
+    error("bad command line.")
+    sys.stderr.write(usage %% {"progname": progname})
+    sys.exit(2)
+
+## Find compiler output directory.
+if args:
+    compiler_output_dir = args.pop(0)
+else:
+    compiler_output_dir = "qm-executables"
+
+## Find g++.
+if args:
+    gpp_path = args.pop(0)
+else:
+    gpp_path = "g++"
+gpp_path = resolve_executable(gpp_path)
+
+log("Using g++: %%s" %% gpp_path)
+run_and_log("%%s --version" %% gpp_path)
+log()
+
+## Find libstdc++.
+if args:
+    libstdcpp_path = args.pop(0)
+    curr = os.environ.get("LD_LIBRARY_PATH", "")
+    new = "%%s:%%s" %% (libstdcpp_path, curr)
+    os.environ["LD_LIBRARY_PATH"] = new
+
+log('Using LD_LIBRARY_PATH="%%s".'
+    %% os.environ.get("LD_LIBRARY_PATH", ""))
+log()
+
+## Find qmtest.
+qmtest_path = resolve_executable("qmtest")
+log("Using qmtest: %%s" %% qmtest_path)
+run_and_log("%%s --version" %% qmtest_path)
+log()
+
+## Set up the compiler output directory.
+if os.path.exists(compiler_output_dir):
+    error("output directory %%s already exists." %% compiler_output_dir)
+    sys.exit(1)
+
+os.mkdir(compiler_output_dir)
+
+## Create the temporary scratch directory.
+if hasattr(tempfile, "mkdtemp"):
+    tmpdir = tempfile.mkdtemp()
+else:
+    tmpdir = tempfile.mktemp()
+    os.mkdir(tmpdir)
+atexit.register(shutil.rmtree, tmpdir)
+
+## Find the target triplet.
+(config_guess_in, config_guess_out) = os.popen4("./config.guess")
+config_guess_in.close()
+target_triplet = config_guess_out.read()
+target_triplet = target_triplet.strip()
+assert "-" in target_triplet, "Bad target triplet"
+log("Using target triplet: %%s" %% target_triplet)
+log()
+
+## Create the basic context to use.
+log("Creating V3 context file.")
+context_path = os.path.join(tmpdir, "__v3_context__")
+f = open(context_path, "w")
+f.write(\"\"\"\\
+CompilerTable.languages=cplusplus
+CompilerTable.cplusplus_kind=GCC
+CompilerTable.cplusplus_options=
+CompilerTable.cplusplus_path=%%(gpp_path)s
+DejaGNUTest.target=%%(target_triplet)s
+V3Test.scratch_dir=%%(tmpdir)s
+V3Test.compiler_output_dir=%%(compiler_output_dir)s
+\"\"\" %% locals())
+f.close()
+
+## Set up QMTest environment variables.
+class_paths = [os.path.abspath(os.path.join("qm-classes", pkg))
+               for pkg in "qmtc", "qmtest_gcc"]
+qmtest_class_path = os.pathsep.join(class_paths)
+os.environ["QMTEST_CLASS_PATH"] = qmtest_class_path
+log('Using QMTEST_CLASS_PATH="%%s"' %% qmtest_class_path)
+log()
+
+## Create the test database we use.
+log("Creating V3 test database.")
+dbpath = os.path.join(tmpdir, "__v3_db__")
+srcdir = os.path.abspath("testsuite")
+run_and_log("qmtest -D %%(dbpath)s create-tdb "
+                     "-c v3_database.V3Database "
+                     "-a srcdir=%%(srcdir)s" %% locals())
+log()
+
+## Okay, we're ready to run the tests for the first time.
+log("Running QMTest to generate executables.")
+log("Results stored in executable-gen.qmr")
+status = run_and_log("qmtest -D %%(dbpath)s run "
+                     "-C %%(context_path)s --format=brief "
+                     "-o executable-gen.qmr"
+                     %% locals(),
+                     failure_ok=True)
+if status == 0 or (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 1):
+    log("Acceptable output status.")
+else:
+    error("qmtest exited unsuccessfully.")
+    sys.exit(1)
+
+## Clean the irrelevant non-executable output files; they take up a lot of
+## space.
+log("Cleaning up executable directory.")
+for junk in glob.glob(os.path.join(compiler_output_dir, "*.[sio]")):
+    os.unlink(junk)
+    log("    Deleted: %%s" %% junk)
+
+## We have the executables; all is well.  Now we'll run it again to
+## generate the baseline result file.
+log("Running QMTest again to generate baseline results.")
+baseline_basename = "%%s.qmr" %% target_triplet
+baseline = os.path.abspath(os.path.join("qm-baselines",
+                                        baseline_basename))
+log("Results stored in %%s" %% baseline)
+run_and_log("qmtest -D %%(dbpath)s run "
+            "-C %%(context_path)s --format=brief "
+            "-c V3Test.have_compiler=no "
+            "-o %%(baseline)s"
+            %% locals(),
+            failure_ok=True)
+if status == 0 or (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 1):
+    log("Acceptable output status.")
+else:
+    error("qmtest exited unsuccessfully.")
+    sys.exit(1)
+
+## All done.
+log("All done.")
+"""
+        
 if __name__ == "__main__":
     main(sys.argv[0], sys.argv[1:])
Index: gpp_init.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/gpp_init.py,v
retrieving revision 1.8
diff -u -r1.8 gpp_init.py
--- gpp_init.py	18 Mar 2004 18:42:17 -0000	1.8
+++ gpp_init.py	30 Mar 2004 18:09:13 -0000
@@ -82,7 +82,7 @@
                                 "testsuite_flags"),
                    "--build-includes"]
         result["GPPInit.testsuite_flags_command"] \
-            = "<pre>" + " ".join(command) + "</pre>"
+            = result.Quote(" ".join(command))
         try:
             executable = RedirectedExecutable()
             executable.Run(command)
Index: v3_test.py
===================================================================
RCS file: /home/qm/Repository/qmtest_gcc/v3_test.py,v
retrieving revision 1.2
diff -u -r1.2 v3_test.py
--- v3_test.py	18 Mar 2004 22:26:11 -0000	1.2
+++ v3_test.py	30 Mar 2004 18:09:13 -0000
@@ -41,7 +41,22 @@
                           "LD_LIBRARY_PATH_64", "DYLD_LIBRARY_PATH"]
 """All the different envvars that might mean LD_LIBRARY_PATH."""
 
-class V3Init(Resource):
+class V3Base(object):
+    """Methods required by all V3 classes."""
+
+    def _HaveCompiler(self, context):
+        """Returns true if we have a compiler."""
+
+        if not context.has_key("V3Test.have_compiler"):
+            # By default we assume there is a compiler.
+            return True
+        
+        # But if there is a context key, we trust it.
+        return qm.parse_boolean(context["V3Test.have_compiler"])
+
+
+
+class V3Init(Resource, V3Base):
     """All V3 tests depend on one of these for setup."""
 
     def SetUp(self, context, result):
@@ -51,6 +66,24 @@
         srcdir = self.GetDatabase().GetRoot()
         target = context["DejaGNUTest.target"]
 
+        # If there is a compiler output directory given, ensure the path
+        # is absolute, and ensure it exists.
+        if context.has_key("V3Test.compiler_output_dir"):
+            compiler_outdir = context["V3Test.compiler_output_dir"]
+            compiler_outdir = os.path.abspath(compiler_outdir)
+            context["V3Test.compiler_output_dir"] = compiler_outdir
+            if not os.path.exists(compiler_outdir):
+                os.mkdir(compiler_outdir)
+        else:
+            compiler_outdir = None
+                
+        if not self._HaveCompiler(context) and compiler_outdir is None:
+            result.SetOutcome(result.ERROR,
+                              "If have_compiler is false, then "
+                              "V3Test.compiler_output_dir must be "
+                              "provided")
+            return
+
         # 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
@@ -60,11 +93,12 @@
         standalone = os.path.exists(standalone_marker)
         if standalone:
             standalone_root = os.path.join(srcdir, "..")
-        context["V3Init.is_standalone"] = standalone
+        context["V3Test.is_standalone"] = standalone
 
         # Find the compiler.
-        compilers = context["CompilerTable.compiler_table"]
-        compiler = compilers["cplusplus"]
+        if self._HaveCompiler(context):
+            compilers = context["CompilerTable.compiler_table"]
+            compiler = compilers["cplusplus"]
 
 
         if not standalone:
@@ -100,16 +134,16 @@
             # 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"):
+            if context.has_key("V3Test.outdir"):
                 result.SetOutcome(result.ERROR,
-                                  "Set V3Init.scratch_dir, not outdir")
+                                  "Set V3Test.scratch_dir, not outdir")
                 return
-            outdir = context["V3Init.scratch_dir"]
+            outdir = context["V3Test.scratch_dir"]
             outdir = os.path.abspath(outdir)
             if not os.path.exists(outdir):
                 os.mkdir(outdir)
             
-        context["V3Init.outdir"] = outdir
+        context["V3Test.outdir"] = outdir
 
         # Ensure that the message format files are available.
         # This requires different commands depending on whether we're
@@ -118,40 +152,55 @@
             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"]
+            if self._HaveCompiler(context):
+                # 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"]
+            else:
+                # We're standalone without a compiler; we'll use the
+                # locale dir in the compiler output directory directly.
+                locale_dir = os.path.join(compiler_outdir, "qm_locale")
+            # Either way, we need to provide the locale directory as an
+            # environment variable, _not_ as a #define.
+            context["V3Init.env_V3_LOCALEDIR"] = locale_dir
 
-        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
+        # Now do the actual compiling, if possible.
+        if self._HaveCompiler(context):
+            make_executable = RedirectedExecutable()
+            status = make_executable.Run(make_command, dir=locale_dir)
+            if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
+                q_stdout = result.Quote(make_executable.stdout)
+                q_stderr = result.Quote(make_executable.stderr)
+                result.SetOutcome(result.ERROR,
+                                  "Error building locale information",
+                                  {"status": str(status),
+                                   "stdout": q_stdout,
+                                   "stderr": q_stderr,
+                                   "command": " ".join(make_command),
+                                   })
+                return
+
+            if compiler_outdir is not None:
+                co_ld = os.path.join(compiler_outdir, "qm_locale")
+                if os.path.exists(co_ld):
+                    shutil.rmtree(co_ld, ignore_errors=True)
+                shutil.copytree(locale_dir, co_ld)
             
 
         # Copy data files.
@@ -197,9 +246,9 @@
             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
+        context["V3Test.libpaths"] = libpaths
+        context["V3Test.ld_library_path"] = ld_library_path
+        result["V3Test.ld_library_path"] = ld_library_path
 
         # Calculate default g++ flags.  Both branches create basic_flags
         # and default_flags.
@@ -215,7 +264,11 @@
             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.
+            # doesn't seem to hurt.  Only exception is that we
+            # purposefully leave out -DLOCALEDIR when doing standalone
+            # testing, so that it will be picked up from the environment
+            # instead.  This ensures that binary-only tests can be moved
+            # after being compiled.
             basic_flags = [# v3.4 only:
                            "-D_GLIBCXX_ASSERT",
                            # v3.3 only:
@@ -224,50 +277,65 @@
                            "-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
+        context["V3Test.basic_cxx_flags"] = basic_flags
+        context["V3Test.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
+            # Ensure libv3test.a exists in 'outdir'.
+            if self._HaveCompiler(context):
+                # 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):
+                    q_stdout = result.Quote(make_executable.stdout)
+                    q_stderr = result.Quote(make_executable.stderr)
+                    command_str = " ".join(make_command),
+                    result.SetOutcome(result.ERROR,
+                                      "Error building libv3test.a",
+                                      {"status": str(status),
+                                       "stdout": q_stdout,
+                                       "stderr": q_stderr,
+                                       "command": command_str,
+                                       })
+                    return
+
+                # If we have an compiler output dir, use it.
+                if compiler_outdir is not None:
+                    shutil.copy(os.path.join(outdir, "libv3test.a"),
+                                os.path.join(compiler_outdir,
+                                             "libv3test.a"))
+            else:
+                # No compiler, so we just copy it out of the compiler
+                # output dir.
+                shutil.copy(os.path.join(compiler_outdir,
+                                         "libv3test.a"),
+                            os.path.join(outdir, "libv3test.a"))
 
         
     def _CalcBuildTreeFlags(self, result, context, blddir, compiler):
@@ -283,8 +351,7 @@
             if os.path.isfile(command):
                 break
 
-        result["V3Init.testsuite_flags_command"] = \
-            "<pre>" + command + "</pre>"
+        result["V3Test.testsuite_flags_command"] = result.Quote(command)
 
         executable = RedirectedExecutable()
         executable.Run([command, "--cxxflags"])
@@ -304,6 +371,8 @@
 
         return (basic_flags, default_flags)
 
+# How DejaGNU does this, for reference:
+#
 # dg-runtest calls dg-test calls "libstdc++-dg-test prog do_what
 # DEFAULT_CXXFLAGS" (DEFAULT_CXXFLAGS as in normal.exp)
 # Which calls
@@ -313,7 +382,7 @@
 #   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):
+class V3DGTest(DGTest, GCCTestBase, V3Base):
     """A 'V3DGTest' is a libstdc++-v3 test using the 'dg' driver.
 
     This test class emulates the 'lib/libstdc++.exp' and 'lib/prune.exp
@@ -324,15 +393,23 @@
 
     _language = "cplusplus"
 
-    _libdir_context_property = "V3Init.libpaths"
+    _libdir_context_property = "V3Test.libpaths"
 
     def Run(self, context, result):
 
         self._SetUp(context)
-        self._RunDGTest(context["V3Init.basic_cxx_flags"],
-                        context["V3Init.default_cxx_flags"],
+
+        if context.has_key("V3Test.compiler_output_dir"):
+            # When using a special output directory, we always save the
+            # executables.
+            keep_output = 1
+        else:
+            keep_output = 0
+        self._RunDGTest(context["V3Test.basic_cxx_flags"],
+                        context["V3Test.default_cxx_flags"],
                         context,
-                        result)
+                        result,
+                        keep_output=keep_output)
         
 
     def _PruneOutput(self, output):
@@ -351,14 +428,16 @@
 
         env = {}
         for name in _ld_library_path_names:
-            env[name] = context["V3Init.ld_library_path"]
+            env[name] = context["V3Test.ld_library_path"]
+        if context.has_key("V3Init.env_V3_LOCALEDIR"):
+            env["V3_LOCALEDIR"] = context["V3Init.env_V3_LOCALEDIR"]
         return env
 
 
     def _RunTargetExecutable(self, context, result, file, dir = None):
 
         if dir is None:
-            dir = context["V3Init.outdir"]
+            dir = context["V3Test.outdir"]
 
         sup = super(V3DGTest, self)
         return sup._RunTargetExecutable(context, result, file, dir)
@@ -380,9 +459,52 @@
         return (output, file)
 
 
+    def _RunDGToolPortion(self, path, tool_flags, context, result):
+        """Don't run the compiler if in pre-compiled mode."""
+
+        if not self._HaveCompiler(context):
+            # Don't run the compiler, just pretend we did.
+            return self._GetOutputFile(context, self._kind, path)
+            
+        return super(V3DGTest, self)._RunDGToolPortion(path, tool_flags,
+                                                       context, result)
+            
+
+    def _RunDGExecutePortion(self, file, context, result):
+        """Emit an UNTESTED result if not compiling and not running."""
+
+        if (not self._HaveCompiler(context)
+            and self._kind != DGTest.KIND_RUN):
+            # We didn't run the compiler, and we're not going to run the
+            # executable; we'd better emit something here because we're
+            # not doing it anywhere else.
+            result["V3DGTest.explanation_1"] = (
+                "This is a compiler test, and we are running in no "
+                "compiler mode.  Skipped.")
+            # Magic marker for the TET output stream to pick up on:
+            result["test_not_relevant_to_testing_mode"] = "true"
+            self._RecordDejaGNUOutcome(result,
+                                       self.UNTESTED, self._name)
+            return
+                
+        super(V3DGTest, self)._RunDGExecutePortion(file,
+                                                   context, result)
+
+
     def _GetOutputFile(self, context, kind, path):
 
-        base = os.path.basename(path)
+        if context.has_key("V3Test.compiler_output_dir"):
+            dir = context["V3Test.compiler_output_dir"]
+            srcdir = self.GetDatabase().GetRoot()
+            path = os.path.normpath(path)
+            srcdir = os.path.normpath(srcdir)
+            assert path.startswith(srcdir)
+            base = path[len(srcdir):]
+            base = base.replace("/", "_")
+        else:
+            dir = context.GetTemporaryDirectory()
+            base = os.path.basename(path)
+
         if kind != self.KIND_PRECOMPILE:
             base = os.path.splitext(base)[0]
         base += { DGTest.KIND_PREPROCESS : ".i",
@@ -393,7 +515,7 @@
                   GCCTestBase.KIND_PRECOMPILE : ".gch",
                   }[kind]
 
-        return os.path.join(context.GetTemporaryDirectory(), base)
+        return os.path.join(dir, base)
 
 
     def _DGrequire_iconv(self, line_num, args, context):
@@ -416,6 +538,16 @@
 
         charset = args[0]
 
+        # First check to see if we have a compiler.  We can't do
+        # anything useful without one.
+        if not self._HaveCompiler(context):
+            # No compiler; we'll go ahead and hope for the best.
+            # Better would be to save the test programs to the output
+            # directory, but this is difficult; on the other hand, not
+            # doing so may cause spurious failures if a character set is
+            # not in fact supported by our local libiconv...
+            return
+
         # Check to see if iconv does exist and work.
         # First by creating and compiling a test program...
         tmpdir = context.GetTemporaryDirectory()
@@ -439,9 +571,9 @@
         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 += context["V3Test.basic_cxx_flags"]
+        options += context["V3Test.default_cxx_flags"]
+        libpaths = context["V3Test.libpaths"]
         options += ["-L" + p for p in libpaths]
 
         if context.has_key("GCCTest.libiconv"):
@@ -471,6 +603,7 @@
 
 
 
+# How the real GCC tree does things:
 # check-abi first builds
 #    abi_check
 #    baseline_symbols
@@ -484,12 +617,12 @@
 #
 #
 # new-abi-baseline is what actually generates a new baseline.
-# it does it with ${extract_symvers} ../src/.libs/libstdc++.so ${baseline_file}
+# 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:
+# But there are some special cases, in particular:
 #    x86_64-*-linux*     -> x86_64-linux-gnu
 #    alpha*-*-freebsd5*  -> alpha-freebsd5
 #    i*86-*-freebsd4*    -> i386-freebsd4
@@ -497,9 +630,10 @@
 #    sparc*-*-freebsd5*  -> sparc-freebsd5
 #
 # extract_symvers = $(glibcxx_srcdir)/scripts/extract_symvers
-# extract_symvers is actually just a shell script
+# extract_symvers is actually just a shell script; we don't need to
+# compile it.
         
-class V3ABITest(Test):
+class V3ABITest(Test, V3Base):
     """A 'V3ABITest' checks the ABI of libstdc++ against a baseline.
 
     Depends on context variable 'V3Test.abi_baseline_file'."""
@@ -509,25 +643,48 @@
         # Some variables we'll need throughout.
         executable = RedirectedExecutable()
         tmpdir = context.GetTemporaryDirectory()
-        outdir = context["V3Init.outdir"]
+        outdir = context["V3Test.outdir"]
         srcdir = self.GetDatabase().GetRoot()
+        if context.has_key("V3Test.compiler_output_dir"):
+            compiler_outdir = context["V3Test.compiler_output_dir"]
+        else:
+            compiler_outdir = None
 
         # 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 self._HaveCompiler(context):
+            # If we have no compiler, we must find it in the compiler
+            # output dir.
+            if compiler_outdir is None:
+                result.SetOutcome(result.ERROR,
+                                  "No compiler output dir, "
+                                  "but no compiler either.")
+                return
+            abi_check = os.path.join(compiler_outdir, "abi_check")
+        else:
+            # Otherwise, we have to try building it.
+            abi_check = os.path.join(outdir, "abi_check")
+            status = executable.Run(["make", "abi_check"], dir=outdir)
+            quote = result.Quote
+            result["make_abi_check_stdout"] = quote(executable.stdout)
+            result["make_abi_check_stderr"] = quote(executable.stderr)
+            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
+            # Ensure that the abi_check program does end up in the
+            # compiler output dir, if necessary.
+            if compiler_outdir is not None:
+                shutil.copy(abi_check,
+                            os.path.join(compiler_outdir, "abi_check"))
+        
         if not os.path.isfile(abi_check):
             result.SetOutcome(result.ERROR,
-                              "No abi_check program '%s'" % abi_check)
+                              "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",
@@ -554,12 +711,12 @@
             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>")
+        # First use ldd to find the libstdc++ in use.  'abi_check' is a
+        # handy C++ program; we'll check which library it's linked
+        # against.
+        status = executable.Run(["ldd", abi_check], dir=outdir)
+        result["ldd_stdout"] = result.Quote(executable.stdout)
+        result["ldd_stderr"] = result.Quote(executable.stderr)
         result["ldd_status"] = str(status)
         if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
             result.SetOutcome(result.ERROR,
@@ -580,10 +737,9 @@
         status = executable.Run([extract_symvers,
                                  libstdcpp,
                                  curr_symbols])
-        result["extract_symvers_stdout"] = ("<pre>" + executable.stdout
-                                            + "</pre>")
-        result["extract_symvers_stderr"] = ("<pre>" + executable.stderr
-                                            + "</pre>")
+        quote = result.Quote
+        result["extract_symvers_stdout"] = quote(executable.stdout)
+        result["extract_symvers_stderr"] = quote(executable.stderr)
         result["extract_symvers_status"] = str(status)
         if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
             result.SetOutcome(result.ERROR, "Error extracting symbols")
@@ -597,10 +753,9 @@
         # 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>")
+        quote = result.Quote
+        result["comparison_stdout"] = quote(executable.stdout)
+        result["comparison_stderr"] = quote(executable.stderr)
         result["comparison_status"] = str(status)
         if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
             result.SetOutcome(result.ERROR,


More information about the qmtest mailing list