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.py237
1 files changed, 230 insertions, 7 deletions
diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py
index 0f6967e42a..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
@@ -22,13 +22,34 @@ python run_build_test.py -DFOO=bar myprog somearg otherarg
sets environment variable FOO=bar, then runs:
myprog somearg otherarg
-$LicenseInfo:firstyear=2009&license=internal$
-Copyright (c) 2009, Linden Research, Inc.
+$LicenseInfo:firstyear=2009&license=viewerlgpl$
+Second Life Viewer Source Code
+Copyright (C) 2009-2010, Linden Research, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation;
+version 2.1 of the License only.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
$/LicenseInfo$
"""
import os
import sys
+import errno
+import HTMLParser
+import re
+import signal
import subprocess
def main(command, libpath=[], vars={}):
@@ -65,10 +86,24 @@ def main(command, libpath=[], vars={}):
dirs = os.environ.get(var, "").split(os.pathsep)
# Append the sequence in libpath
print "%s += %r" % (var, libpath)
- dirs.extend(libpath)
+ for dir in libpath:
+ # append system paths at the end
+ if dir in ('/lib', '/usr/lib'):
+ dirs.append(dir)
+ # prepend non-system paths
+ else:
+ dirs.insert(0, dir)
+
+ # Filter out some useless pieces
+ clean_dirs = []
+ for dir in dirs:
+ if dir and dir not in ('', '.'):
+ clean_dirs.append(dir)
+
# Now rebuild the path string. This way we use a minimum of separators
# -- and we avoid adding a pointless separator when libpath is empty.
- os.environ[var] = os.pathsep.join(dirs)
+ os.environ[var] = os.pathsep.join(clean_dirs)
+ print "%s = %r" % (var, os.environ[var])
# Now handle arbitrary environment variables. The tricky part is ensuring
# that all the keys and values we try to pass are actually strings.
if vars:
@@ -78,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
@@ -107,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)