summaryrefslogtreecommitdiff
path: root/indra/cmake/run_build_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'indra/cmake/run_build_test.py')
-rwxr-xr-x[-rw-r--r--]indra/cmake/run_build_test.py198
1 files changed, 195 insertions, 3 deletions
diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py
index 37aa75e364..a79d09a9ea 100644..100755
--- a/indra/cmake/run_build_test.py
+++ b/indra/cmake/run_build_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
"""\
@file run_build_test.py
@author Nat Goodspeed
@@ -46,6 +46,10 @@ $/LicenseInfo$
import os
import sys
+import errno
+import HTMLParser
+import re
+import signal
import subprocess
def main(command, libpath=[], vars={}):
@@ -109,7 +113,195 @@ def main(command, libpath=[], vars={}):
os.environ.update(dict([(str(key), str(value)) for key, value in vars.iteritems()]))
# Run the child process.
print "Running: %s" % " ".join(command)
- return subprocess.call(command)
+ # Make sure we see all relevant output *before* child-process output.
+ sys.stdout.flush()
+ try:
+ return subprocess.call(command)
+ except OSError as err:
+ # If the caller is trying to execute a test program that doesn't
+ # exist, we want to produce a reasonable error message rather than a
+ # traceback. This happens when the build is halted by errors, but
+ # CMake tries to proceed with testing anyway <eyeroll/>. However, do
+ # NOT attempt to handle any error but "doesn't exist."
+ if err.errno != errno.ENOENT:
+ raise
+ # In practice, the pathnames into CMake's build tree are so long as to
+ # obscure the name of the test program. Just print its basename.
+ print "No such program %s; check for preceding build errors" % \
+ os.path.basename(command[0])
+ # What rc should we simulate for missing executable? Windows produces
+ # 9009.
+ return 9009
+
+# swiped from vita, sigh, seems like a Bad Idea to introduce dependency
+def translate_rc(rc):
+ """
+ Accept an rc encoded as for subprocess.Popen.returncode:
+ None means still running
+ int >= 0 means terminated voluntarily with specified rc
+ int < 0 means terminated by signal (-rc)
+
+ Return a string explaining the outcome. In case of a signal, try to
+ name the corresponding symbol from the 'signal' module.
+ """
+ if rc is None:
+ return "still running"
+
+ if rc >= 0:
+ return "terminated with rc %s" % rc
+
+ if sys.platform.startswith("win"):
+ # From http://stackoverflow.com/questions/20629027/process-finished-with-exit-code-1073741571
+ # [-1073741571] is the signed integer representation of Microsoft's
+ # "stack overflow/stack exhaustion" error code 0xC00000FD.
+ # Anytime you see strange, large negative exit codes in windows, convert
+ # them to hex and then look them up in the ntstatus error codes
+ # http://msdn.microsoft.com/en-us/library/cc704588.aspx
+
+ # Python bends over backwards to give you all the integer precision
+ # you need, avoiding truncation. But only with 32-bit signed ints is
+ # -1073741571 equivalent to 0xC00000FD! Explicitly truncate before
+ # converting.
+ hexrc = "0x%X" % (rc & 0xFFFFFFFF)
+ # At this point, we're only trying to format the rc to make it easier
+ # for a human being to understand. Any exception here -- file doesn't
+ # exist, HTML parsing error, unrecognized table structure, unknown key
+ # -- should NOT kill the script! It should only cause us to shrug and
+ # present our caller with the best information available.
+ try:
+ table = get_windows_table()
+ symbol, desc = table[hexrc]
+ except Exception, err:
+ print >>sys.stderr, "(%s -- carrying on)" % err
+ return "terminated with rc %s (%s)" % (rc, hexrc)
+ else:
+ return "terminated with rc %s: %s: %s" % (hexrc, symbol, desc)
+
+ else:
+ # On Posix, negative rc means the child was terminated by signal -rc.
+ rc = -rc
+ for attr in dir(signal):
+ if attr.startswith('SIG') and getattr(signal, attr) == rc:
+ strc = attr
+ break
+ else:
+ strc = str(rc)
+ return "terminated by signal %s" % strc
+
+class TableParser(HTMLParser.HTMLParser):
+ """
+ This HTMLParser subclass is designed to parse the table we know exists
+ in windows-rcs.html, hopefully without building in too much knowledge of
+ the specific way that table is currently formatted.
+ """
+ # regular expression matching any string containing only whitespace
+ whitespace = re.compile(r'\s*$')
+
+ def __init__(self):
+ # Because Python 2.x's HTMLParser is an old-style class, we must use
+ # old-style syntax to forward the __init__() call -- not super().
+ HTMLParser.HTMLParser.__init__(self)
+ # this will collect all the data, eventually
+ self.table = []
+ # Stack whose top (last item) indicates where to append current
+ # element data. When empty, don't collect data at all.
+ self.dest = []
+
+ def handle_starttag(self, tag, attrs):
+ if tag == "table":
+ # This is the outermost tag we recognize. Collect nested elements
+ # within self.table.
+ self.dest.append(self.table)
+ elif tag in ("tr", "td"):
+ # Nested elements whose contents we want to capture as sublists.
+ # To the list currently designated by the top of the dest stack,
+ # append a new empty sublist.
+ self.dest[-1].append([])
+ # Now push THAT new, empty list as the new top of the dest stack.
+ self.dest.append(self.dest[-1][-1])
+ elif tag == "p":
+ # We could handle <p> ... </p> just like <tr> or <td>, but that
+ # introduces an unnecessary extra level of nesting. Just skip.
+ pass
+ else:
+ # For any tag we don't recognize (notably <th>), push a new, empty
+ # list to the top of the dest stack. This new list is NOT
+ # referenced by anything in self.table; thus, when we pop it, any
+ # data we've collected inside that list will be discarded.
+ self.dest.append([])
+
+ def handle_endtag(self, tag):
+ # Because we avoid pushing self.dest for <p> in handle_starttag(), we
+ # must refrain from popping it for </p> here.
+ if tag != "p":
+ # For everything else, including unrecognized tags, pop the dest
+ # stack, reverting to outer collection.
+ self.dest.pop()
+
+ def handle_startendtag(self, tag, attrs):
+ # The table of interest contains <td> entries of the form:
+ # <p>0x00000000<br />STATUS_SUCCESS</p>
+ # The <br/> is very useful -- we definitely want two different data
+ # items for "0x00000000" and "STATUS_SUCCESS" -- but we don't need or
+ # want it to push, then discard, an empty list as it would if we let
+ # the default HTMLParser.handle_startendtag() call handle_starttag()
+ # followed by handle_endtag(). Just ignore <br/> or any other
+ # singleton tag.
+ pass
+
+ def handle_data(self, data):
+ # Outside the <table> of interest, self.dest is empty. Do not bother
+ # collecting data when self.dest is empty.
+ # HTMLParser calls handle_data() with every chunk of whitespace
+ # between tags. That would be lovely if our eventual goal was to
+ # reconstitute the original input stream with its existing formatting,
+ # but for us, whitespace only clutters the table. Ignore it.
+ if self.dest and not self.whitespace.match(data):
+ # Here we're within our <table> and we have non-whitespace data.
+ # Append it to the list designated by the top of the dest stack.
+ self.dest[-1].append(data)
+
+# cache for get_windows_table()
+_windows_table = None
+
+def get_windows_table():
+ global _windows_table
+ # If we already loaded _windows_table, no need to load it all over again.
+ if _windows_table:
+ return _windows_table
+
+ # windows-rcs.html was fetched on 2015-03-24 with the following command:
+ # curl -o windows-rcs.html \
+ # https://msdn.microsoft.com/en-us/library/cc704588.aspx
+ parser = TableParser()
+ with open(os.path.join(os.path.dirname(__file__), "windows-rcs.html")) as hf:
+ # We tried feeding the file data to TableParser in chunks, to avoid
+ # buffering the entire file as a single string. Unfortunately its
+ # handle_data() cannot tell the difference between distinct calls
+ # separated by HTML tags, and distinct calls necessitated by a chunk
+ # boundary. Sigh! Read in the whole file. At the time this was
+ # written, it was only 500KB anyway.
+ parser.feed(hf.read())
+ parser.close()
+ table = parser.table
+
+ # With our parser, any <tr><th>...</th></tr> row leaves a table entry
+ # consisting only of an empty list. Remove any such.
+ while table and not table[0]:
+ table.pop(0)
+
+ # We expect rows of the form:
+ # [['0x00000000', 'STATUS_SUCCESS'],
+ # ['The operation completed successfully.']]
+ # The latter list will have multiple entries if Microsoft embedded <br/>
+ # or <p> ... </p> in the text, in which case joining with '\n' is
+ # appropriate.
+ # Turn that into a dict whose key is the hex string, and whose value is
+ # the pair (symbol, desc).
+ _windows_table = dict((key, (symbol, '\n'.join(desc)))
+ for (key, symbol), desc in table)
+
+ return _windows_table
if __name__ == "__main__":
from optparse import OptionParser
@@ -138,5 +330,5 @@ if __name__ == "__main__":
vars=dict([(pair.split('=', 1) + [""])[:2] for pair in opts.vars]))
if rc not in (None, 0):
print >>sys.stderr, "Failure running: %s" % " ".join(args)
- print >>sys.stderr, "Error: %s" % rc
+ print >>sys.stderr, "Error %s: %s" % (rc, translate_rc(rc))
sys.exit((rc < 0) and 255 or rc)