2 TestSCons.py: a testing framework for the SCons software construction
5 A TestSCons environment object is created via the usual invocation:
9 TestScons is a subclass of TestCommon, which is in turn is a subclass
10 of TestCmd), and hence has available all of the methods and attributes
11 from those classes, as well as any overridden or additional methods or
12 attributes defined in this subclass.
17 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
29 except AttributeError:
32 for i in xrange(len(lists[0])):
33 result.append(tuple(map(lambda l, i=i: l[i], lists)))
37 from TestCommon import *
38 from TestCommon import __all__
40 # Some tests which verify that SCons has been packaged properly need to
41 # look for specific version file names. Replicating the version number
42 # here provides some independent verification that what we packaged
43 # conforms to what we expect.
45 default_version = '1.0.1'
47 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
49 # In the checked-in source, the value of SConsVersion in the following
50 # line must remain "__ VERSION __" (without the spaces) so the built
51 # version in build/QMTest/TestSCons.py contains the actual version
52 # string of the packages that have been built.
53 SConsVersion = '__VERSION__'
54 if SConsVersion == '__' + 'VERSION' + '__':
55 SConsVersion = default_version
57 __all__.extend([ 'TestSCons',
78 except AttributeError:
79 # Windows doesn't have a uname() function. We could use something like
80 # sys.platform as a fallback, but that's not really a "machine," so
81 # just leave it as None.
85 machine = machine_map.get(machine, machine)
87 python = python_executable
88 _python_ = '"' + python_executable + '"'
99 """Test whether -lfrtbegin is required. This can probably be done in
100 a more reliable way, but using popen3 is relatively efficient."""
106 stderr = popen2.popen3('gcc -v')[2]
110 for l in stderr.readlines():
111 list = string.split(l)
112 if len(list) > 3 and list[:2] == ['gcc', 'version']:
113 if list[2][:3] in ('4.1','4.2','4.3'):
114 libs = ['gfortranbegin']
116 if list[2][:2] in ('3.', '4.'):
117 libs = ['frtbegin'] + libs
122 if sys.platform == 'cygwin':
123 # On Cygwin, os.path.normcase() lies, so just report back the
124 # fact that the underlying Win32 OS is case-insensitive.
125 def case_sensitive_suffixes(s1, s2):
128 def case_sensitive_suffixes(s1, s2):
129 return (os.path.normcase(s1) != os.path.normcase(s2))
132 if sys.platform == 'win32':
133 fortran_lib = gccFortranLibs()
134 elif sys.platform == 'cygwin':
135 fortran_lib = gccFortranLibs()
136 elif string.find(sys.platform, 'irix') != -1:
137 fortran_lib = ['ftn']
139 fortran_lib = gccFortranLibs()
143 file_expr = r"""File "[^"]*", line \d+, in .+
146 # re.escape escapes too much.
148 for c in ['.', '[', ']', '(', ')', '*', '+', '?']: # Not an exhaustive list.
149 str = string.replace(str, c, '\\' + c)
156 except AttributeError:
157 # Pre-1.6 Python has no sys.version_info
158 version_string = string.split(sys.version)[0]
159 version_ints = map(int, string.split(version_string, '.'))
160 sys.version_info = tuple(version_ints + ['final', 0])
162 def python_version_string():
163 return string.split(sys.version)[0]
165 def python_minor_version_string():
166 return sys.version[:3]
168 def unsupported_python_version(version=sys.version_info):
169 return version < (1, 5, 2)
171 def deprecated_python_version(version=sys.version_info):
172 return version < (2, 2, 0)
174 if deprecated_python_version():
176 scons: warning: Support for pre-2.2 Python (%s) is deprecated.
177 If this will cause hardship, contact dev@scons.tigris.org.
180 deprecated_python_expr = re_escape(msg % python_version_string()) + file_expr
183 deprecated_python_expr = ""
187 class TestSCons(TestCommon):
188 """Class for testing SCons.
190 This provides a common place for initializing SCons tests,
191 eliminating the need to begin every test with the same repeated
195 scons_version = SConsVersion
197 def __init__(self, **kw):
198 """Initialize an SCons testing object.
200 If they're not overridden by keyword arguments, this
201 initializes the object with the following default values:
203 program = 'scons' if it exists,
205 interpreter = 'python'
209 The workdir value means that, by default, a temporary workspace
210 directory is created for a TestSCons environment. In addition,
211 this method changes directory (chdir) to the workspace directory,
212 so an explicit "chdir = '.'" on all of the run() method calls
215 self.orig_cwd = os.getcwd()
217 script_dir = os.environ['SCONS_SCRIPT_DIR']
222 if not kw.has_key('program'):
223 kw['program'] = os.environ.get('SCONS')
224 if not kw['program']:
225 if os.path.exists('scons'):
226 kw['program'] = 'scons'
228 kw['program'] = 'scons.py'
229 if not kw.has_key('interpreter') and not os.environ.get('SCONS_EXEC'):
230 kw['interpreter'] = [python, '-tt']
231 if not kw.has_key('match'):
232 kw['match'] = match_exact
233 if not kw.has_key('workdir'):
236 # Term causing test failures due to bogus readline init
237 # control character output on FC8
238 # TERM can cause test failures due to control chars in prompts etc.
239 os.environ['TERM'] = 'dumb'
241 if deprecated_python_version():
242 sconsflags = os.environ.get('SCONSFLAGS')
244 sconsflags = [sconsflags]
247 sconsflags = sconsflags + ['--warn=no-python-version']
248 os.environ['SCONSFLAGS'] = string.join(sconsflags)
250 apply(TestCommon.__init__, [self], kw)
253 if SCons.Node.FS.default_fs is None:
254 SCons.Node.FS.default_fs = SCons.Node.FS.FS()
256 def Environment(self, ENV=None, *args, **kw):
258 Return a construction Environment that optionally overrides
259 the default external environment with the specified ENV.
261 import SCons.Environment
266 return apply(SCons.Environment.Environment, args, kw)
267 except (SCons.Errors.UserError, SCons.Errors.InternalError):
270 def detect(self, var, prog=None, ENV=None, norm=None):
272 Detect a program named 'prog' by first checking the construction
273 variable named 'var' and finally searching the path used by
274 SCons. If either method fails to detect the program, then false
275 is returned, otherwise the full path to prog is returned. If
276 prog is None, then the value of the environment variable will be
279 env = self.Environment(ENV)
280 v = env.subst('$'+var)
287 result = env.WhereIs(prog)
288 if norm and os.sep != '/':
289 result = string.replace(result, os.sep, '/')
292 def detect_tool(self, tool, prog=None, ENV=None):
294 Given a tool (i.e., tool specification that would be passed
295 to the "tools=" parameter of Environment()) and a program that
296 corresponds to that tool, return true if and only if we can find
297 that tool using Environment.Detect().
299 By default, prog is set to the value passed into the tools parameter.
304 env = self.Environment(ENV, tools=[tool])
307 return env.Detect([prog])
309 def where_is(self, prog, path=None):
311 Given a program, search for it in the specified external PATH,
312 or in the actual external PATH is none is specified.
314 import SCons.Environment
315 env = SCons.Environment.Environment()
317 path = os.environ['PATH']
318 return env.WhereIs(prog, path)
320 def wrap_stdout(self, build_str = "", read_str = "", error = 0, cleaning = 0):
321 """Wraps standard output string(s) in the normal
322 "Reading ... done" and "Building ... done" strings
324 cap,lc = [ ('Build','build'),
325 ('Clean','clean') ][cleaning]
327 term = "scons: %sing terminated because of errors.\n" % lc
329 term = "scons: done %sing targets.\n" % lc
330 return "scons: Reading SConscript files ...\n" + \
332 "scons: done reading SConscript files.\n" + \
333 "scons: %sing targets ...\n" % cap + \
337 def up_to_date(self, options = None, arguments = None, read_str = "", **kw):
339 for arg in string.split(arguments):
340 s = s + "scons: `%s' is up to date.\n" % arg
342 arguments = options + " " + arguments
343 kw['arguments'] = arguments
344 stdout = self.wrap_stdout(read_str = read_str, build_str = s)
345 kw['stdout'] = re.escape(stdout)
346 kw['match'] = self.match_re_dotall
347 apply(self.run, [], kw)
349 def not_up_to_date(self, options = None, arguments = None, **kw):
350 """Asserts that none of the targets listed in arguments is
351 up to date, but does not make any assumptions on other targets.
352 This function is most useful in conjunction with the -n option.
355 for arg in string.split(arguments):
356 s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg)
358 arguments = options + " " + arguments
359 s = '('+s+'[^\n]*\n)*'
360 kw['arguments'] = arguments
361 stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE'))
362 kw['stdout'] = string.replace(stdout, 'ARGUMENTSGOHERE', s)
363 kw['match'] = self.match_re_dotall
364 apply(self.run, [], kw)
366 def diff_substr(self, expect, actual, prelen=20, postlen=40):
368 for x, y in zip(expect, actual):
370 return "Actual did not match expect at char %d:\n" \
373 % (i, repr(expect[i-prelen:i+postlen]),
374 repr(actual[i-prelen:i+postlen]))
376 return "Actual matched the expected output???"
378 def python_file_line(self, file, line):
380 Returns a Python error line for output comparisons.
382 The exec of the traceback line gives us the correct format for
383 this version of Python. Before 2.5, this yielded:
385 File "<string>", line 1, ?
387 Python 2.5 changed this to:
389 File "<string>", line 1, <module>
391 We stick the requested file name and line number in the right
392 places, abstracting out the version difference.
394 exec 'import traceback; x = traceback.format_stack()[-1]'
396 x = string.replace(x, '<string>', file)
397 x = string.replace(x, 'line 1,', 'line %s,' % line)
400 def normalize_pdf(self, s):
401 s = re.sub(r'/(Creation|Mod)Date \(D:[^)]*\)',
402 r'/\1Date (D:XXXX)', s)
403 s = re.sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]',
404 r'/ID [<XXXX> <XXXX>]', s)
405 s = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}',
407 s = re.sub(r'/Length \d+ *\n/Filter /FlateDecode\n',
408 r'/Length XXXX\n/Filter /FlateDecode\n', s)
416 begin_marker = '/FlateDecode\n>>\nstream\n'
417 end_marker = 'endstream\nendobj'
420 b = string.find(s, begin_marker, 0)
422 b = b + len(begin_marker)
423 e = string.find(s, end_marker, b)
424 encoded.append((b, e))
425 b = string.find(s, begin_marker, e + len(end_marker))
431 d = zlib.decompress(s[b:e])
432 d = re.sub(r'%%CreationDate: [^\n]*\n',
433 r'%%CreationDate: 1970 Jan 01 00:00:00\n', d)
434 d = re.sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d',
435 r'%DVIPSSource: TeX output 1970.01.01:0000', d)
436 d = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}',
441 s = string.join(r, '')
445 def paths(self,patterns):
455 def java_ENV(self, version=None):
457 Initialize with a default external environment that uses a local
458 Java SDK in preference to whatever's found in the default PATH.
461 return self._java_env[version]['ENV']
462 except AttributeError:
467 import SCons.Environment
468 env = SCons.Environment.Environment()
469 self._java_env[version] = env
474 '/usr/java/jdk%s*/bin' % version,
475 '/usr/lib/jvm/*-%s*/bin' % version,
476 '/usr/local/j2sdk%s*/bin' % version,
478 java_path = self.paths(patterns) + [env['ENV']['PATH']]
481 '/usr/java/latest/bin',
482 '/usr/lib/jvm/*/bin',
483 '/usr/local/j2sdk*/bin',
485 java_path = self.paths(patterns) + [env['ENV']['PATH']]
487 env['ENV']['PATH'] = string.join(java_path, os.pathsep)
490 def java_where_includes(self,version=None):
492 Return java include paths compiling java jni code
498 frame = '/System/Library/Frameworks/JavaVM.framework/Headers/jni.h'
500 frame = '/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/jni.h'%version
501 jni_dirs = ['/usr/lib/jvm/java-*-sun-%s*/include/jni.h'%version,
502 '/usr/java/jdk%s*/include/jni.h'%version,
505 dirs = self.paths(jni_dirs)
508 d=os.path.dirname(self.paths(jni_dirs)[0])
511 if sys.platform == 'win32':
512 result.append(os.path.join(d,'win32'))
513 elif sys.platform == 'linux2':
514 result.append(os.path.join(d,'linux'))
518 def java_where_java_home(self,version=None):
520 jar=self.java_where_jar(version)
521 home=os.path.normpath('%s/..'%jar)
524 def java_where_jar(self, version=None):
525 ENV = self.java_ENV(version)
526 if self.detect_tool('jar', ENV=ENV):
527 where_jar = self.detect('JAR', 'jar', ENV=ENV)
529 where_jar = self.where_is('jar', ENV['PATH'])
531 self.skip_test("Could not find Java jar, skipping test(s).\n")
534 def java_where_java(self, version=None):
536 Return a path to the java executable.
538 ENV = self.java_ENV(version)
539 where_java = self.where_is('java', ENV['PATH'])
541 self.skip_test("Could not find Java java, skipping test(s).\n")
544 def java_where_javac(self, version=None):
546 Return a path to the javac compiler.
548 ENV = self.java_ENV(version)
549 if self.detect_tool('javac'):
550 where_javac = self.detect('JAVAC', 'javac', ENV=ENV)
552 where_javac = self.where_is('javac', ENV['PATH'])
554 self.skip_test("Could not find Java javac, skipping test(s).\n")
555 self.run(program = where_javac,
556 arguments = '-version',
560 if string.find(self.stderr(), 'javac %s' % version) == -1:
561 fmt = "Could not find javac for Java version %s, skipping test(s).\n"
562 self.skip_test(fmt % version)
564 m = re.search(r'javac (\d\.\d)', self.stderr())
569 return where_javac, version
571 def java_where_javah(self, version=None):
572 ENV = self.java_ENV(version)
573 if self.detect_tool('javah'):
574 where_javah = self.detect('JAVAH', 'javah', ENV=ENV)
576 where_javah = self.where_is('javah', ENV['PATH'])
578 self.skip_test("Could not find Java javah, skipping test(s).\n")
581 def java_where_rmic(self, version=None):
582 ENV = self.java_ENV(version)
583 if self.detect_tool('rmic'):
584 where_rmic = self.detect('RMIC', 'rmic', ENV=ENV)
586 where_rmic = self.where_is('rmic', ENV['PATH'])
588 self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n")
591 def Qt_dummy_installation(self, dir='qt'):
592 # create a dummy qt installation
594 self.subdir( dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib'] )
596 self.write([dir, 'bin', 'mymoc.py'], """\
601 cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:', [])
605 for opt, arg in cmd_opts:
606 if opt == '-o': output = open(arg, 'wb')
607 elif opt == '-i': impl = 1
608 else: opt_string = opt_string + ' ' + opt
610 contents = open(a, 'rb').read()
611 a = string.replace(a, '\\\\', '\\\\\\\\')
612 subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }'
614 contents = re.sub( r'#include.*', '', contents )
615 output.write(string.replace(contents, 'Q_OBJECT', subst))
620 self.write([dir, 'bin', 'myuic.py'], """\
629 for arg in sys.argv[1:]:
631 output = open(arg, 'wb')
643 source = open(arg, 'rb')
646 output.write( '#include "' + impl + '"\\n' )
647 includes = re.findall('<include.*?>(.*?)</include>', source.read())
648 for incFile in includes:
649 # this is valid for ui.h files, at least
650 if os.path.exists(incFile):
651 output.write('#include "' + incFile + '"\\n')
653 output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" )
658 self.write([dir, 'include', 'my_qobject.h'], r"""
660 void my_qt_symbol(const char *arg);
663 self.write([dir, 'lib', 'my_qobject.cpp'], r"""
664 #include "../include/my_qobject.h"
666 void my_qt_symbol(const char *arg) {
671 self.write([dir, 'lib', 'SConstruct'], r"""
674 if sys.platform == 'win32':
675 env.StaticLibrary( 'myqt', 'my_qobject.cpp' )
677 env.SharedLibrary( 'myqt', 'my_qobject.cpp' )
680 self.run(chdir = self.workpath(dir, 'lib'),
683 match = self.match_re_dotall)
685 self.QT = self.workpath(dir)
687 self.QT_MOC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'mymoc.py'))
688 self.QT_UIC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'myuic.py'))
689 self.QT_LIB_DIR = self.workpath(dir, 'lib')
691 def Qt_create_SConstruct(self, place):
692 if type(place) is type([]):
693 place = apply(test.workpath, place)
694 self.write(place, """\
695 if ARGUMENTS.get('noqtdir', 0): QTDIR=None
697 env = Environment(QTDIR = QTDIR,
701 tools=['default','qt'])
703 if ARGUMENTS.get('variant_dir', 0):
704 if ARGUMENTS.get('chdir', 0):
708 dup=int(ARGUMENTS.get('dup', 1))
710 builddir = 'build_dup0'
714 VariantDir(builddir, '.', duplicate=dup)
716 sconscript = Dir(builddir).File('SConscript')
718 sconscript = File('SConscript')
720 SConscript( sconscript )
721 """ % (self.QT, self.QT_LIB, self.QT_MOC, self.QT_UIC))
723 def msvs_versions(self):
724 if not hasattr(self, '_msvs_versions'):
726 # Determine the SCons version and the versions of the MSVS
727 # environments installed on the test machine.
729 # We do this by executing SCons with an SConstruct file
730 # (piped on stdin) that spits out Python assignments that
731 # we can just exec(). We construct the SCons.__"version"__
732 # string in the input here so that the SCons build itself
733 # doesn't fill it in when packaging SCons.
736 print "self._scons_version =", repr(SCons.__%s__)
738 print "self._msvs_versions =", str(env['MSVS']['VERSIONS'])
741 self.run(arguments = '-n -q -Q -f -', stdin = input)
744 return self._msvs_versions
746 def vcproj_sys_path(self, fname):
749 orig = 'sys.path = [ join(sys'
751 enginepath = repr(os.path.join(self._cwd, '..', 'engine'))
752 replace = 'sys.path = [ %s, join(sys' % enginepath
754 contents = self.read(fname)
755 contents = string.replace(contents, orig, replace)
756 self.write(fname, contents)
758 def msvs_substitute(self, input, msvs_ver,
759 subdir=None, sconscript=None,
762 if not hasattr(self, '_msvs_versions'):
766 workpath = self.workpath(subdir)
768 workpath = self.workpath()
770 if sconscript is None:
771 sconscript = self.workpath('SConstruct')
774 python = sys.executable
776 if project_guid is None:
777 project_guid = "{E5466E26-0003-F18B-8F8A-BCD76C86388D}"
779 if os.environ.has_key('SCONS_LIB_DIR'):
780 exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % os.environ['SCONS_LIB_DIR']
782 exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%s'), join(sys.prefix, 'scons-%s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % (self._scons_version, self._scons_version)
783 exec_script_main_xml = string.replace(exec_script_main, "'", "'")
785 result = string.replace(input, r'<WORKPATH>', workpath)
786 result = string.replace(result, r'<PYTHON>', python)
787 result = string.replace(result, r'<SCONSCRIPT>', sconscript)
788 result = string.replace(result, r'<SCONS_SCRIPT_MAIN>', exec_script_main)
789 result = string.replace(result, r'<SCONS_SCRIPT_MAIN_XML>', exec_script_main_xml)
790 result = string.replace(result, r'<PROJECT_GUID>', project_guid)
793 def get_msvs_executable(self, version):
794 """Returns a full path to the executable (MSDEV or devenv)
795 for the specified version of Visual Studio.
797 common_msdev98_bin_msdev_com = ['Common', 'MSDev98', 'Bin', 'MSDEV.COM']
798 common7_ide_devenv_com = ['Common7', 'IDE', 'devenv.com']
799 common7_ide_vcexpress_exe = ['Common7', 'IDE', 'VCExpress.exe']
802 common_msdev98_bin_msdev_com,
805 common7_ide_devenv_com,
808 common7_ide_devenv_com,
811 common7_ide_devenv_com,
812 common7_ide_vcexpress_exe,
815 from SCons.Tool.msvs import get_msvs_install_dirs
816 vs_path = get_msvs_install_dirs(version)['VSINSTALLDIR']
817 for sp in sub_paths[version]:
818 p = apply(os.path.join, [vs_path] + sp)
819 if os.path.exists(p):
821 return apply(os.path.join, [vs_path] + sub_paths[version][0])
824 NCR = 0 # non-cached rebuild
825 CR = 1 # cached rebuild (up to date)
826 NCF = 2 # non-cached build failure
827 CF = 3 # cached build failure
829 if sys.platform == 'win32':
830 Configure_lib = 'msvcrt'
834 # to use cygwin compilers on cmd.exe -> uncomment following line
837 def checkLogAndStdout(self, checks, results, cached,
838 logfile, sconf_dir, sconstruct,
839 doCheckLog=1, doCheckStdout=1):
842 def __init__(self, p):
845 def matchPart(log, logfile, lastEnd, NoMatch=NoMatch):
846 m = re.match(log, logfile[lastEnd:])
848 raise NoMatch, lastEnd
849 return m.end() + lastEnd
851 #print len(os.linesep)
854 for i in range(len(ls)):
858 nols = nols + "[^" + ls[i] + "])"
863 logfile = self.read(self.workpath(logfile))
865 string.find( logfile, "scons: warning: The stored build "
866 "information has an unexpected class." ) >= 0):
868 sconf_dir = sconf_dir
869 sconstruct = sconstruct
871 log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls
872 if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
873 log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls
874 if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
877 for check,result,cache_desc in zip(checks, results, cached):
878 log = re.escape("scons: Configure: " + check) + ls
879 if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
882 for bld_desc in cache_desc: # each TryXXX
883 for ext, flag in bld_desc: # each file in TryBuild
884 file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext))
887 if ext in ['.c', '.cpp']:
888 log=log + re.escape(file + " <-") + ls
889 log=log + r"( \|" + nols + "*" + ls + ")+?"
891 log=log + "(" + nols + "*" + ls +")*?"
896 re.escape("scons: Configure: \"%s\" is up to date."
898 log=log+re.escape("scons: Configure: The original builder "
900 log=log+r"( \|.*"+ls+")+"
902 # non-cached rebuild failure
903 log=log + "(" + nols + "*" + ls + ")*?"
906 # cached rebuild failure
908 re.escape("scons: Configure: Building \"%s\" failed "
909 "in a previous run and all its sources are"
910 " up to date." % file) + ls
911 log=log+re.escape("scons: Configure: The original builder "
913 log=log+r"( \|.*"+ls+")+"
916 result = "(cached) " + result
917 rdstr = rdstr + re.escape(check) + re.escape(result) + "\n"
918 log=log + re.escape("scons: Configure: " + result) + ls + ls
919 if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
921 if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd)
922 if doCheckLog and lastEnd != len(logfile):
923 raise NoMatch, lastEnd
926 print "Cannot match log file against log regexp."
928 print "------------------------------------------------------"
929 print logfile[m.pos:]
930 print "------------------------------------------------------"
932 print "------------------------------------------------------"
934 print "------------------------------------------------------"
938 exp_stdout = self.wrap_stdout(".*", rdstr)
939 if not self.match_re_dotall(self.stdout(), exp_stdout):
940 print "Unexpected stdout: "
941 print "-----------------------------------------------------"
942 print repr(self.stdout())
943 print "-----------------------------------------------------"
944 print repr(exp_stdout)
945 print "-----------------------------------------------------"
948 def get_python_version(self):
950 Returns the Python version (just so everyone doesn't have to
951 hand-code slicing the right number of characters).
953 # see also sys.prefix documentation
954 return python_minor_version_string()
956 def get_platform_python(self):
958 Returns a path to a Python executable suitable for testing on
961 Mac OS X has no static libpython for SWIG to link against,
962 so we have to link against Apple's framwork version. However,
963 testing must use the executable version that corresponds to the
964 framework we link against, or else we get interpreter errors.
966 if sys.platform[:6] == 'darwin':
967 return sys.prefix + '/bin/python'
972 def get_quoted_platform_python(self):
974 Returns a quoted path to a Python executable suitable for testing on
977 Mac OS X has no static libpython for SWIG to link against,
978 so we have to link against Apple's framwork version. However,
979 testing must use the executable version that corresponds to the
980 framework we link against, or else we get interpreter errors.
982 if sys.platform[:6] == 'darwin':
983 return '"' + self.get_platform_python() + '"'
988 # def get_platform_sys_prefix(self):
990 # Returns a "sys.prefix" value suitable for linking on this platform.
992 # Mac OS X has a built-in Python but no static libpython,
993 # so we must link to it using Apple's 'framework' scheme.
995 # if sys.platform[:6] == 'darwin':
996 # fmt = '/System/Library/Frameworks/Python.framework/Versions/%s/'
997 # return fmt % self.get_python_version()
1001 def get_python_frameworks_flags(self):
1003 Returns a FRAMEWORKS value for linking with Python.
1005 Mac OS X has a built-in Python but no static libpython,
1006 so we must link to it using Apple's 'framework' scheme.
1008 if sys.platform[:6] == 'darwin':
1013 def get_python_inc(self):
1015 Returns a path to the Python include directory.
1017 Mac OS X has a built-in Python but no static libpython,
1018 so we must link to it using Apple's 'framework' scheme.
1020 if sys.platform[:6] == 'darwin':
1021 return sys.prefix + '/Headers'
1023 import distutils.sysconfig
1025 return os.path.join(sys.prefix, 'include',
1026 'python' + self.get_python_version())
1028 return distutils.sysconfig.get_python_inc()
1030 def get_python_library_path(self):
1032 Returns the full path of the Python static library (libpython*.a)
1034 if sys.platform[:6] == 'darwin':
1035 # Use the framework version (or try to) since that matches
1036 # the executable and headers we return elsewhere.
1037 python_lib = os.path.join(sys.prefix, 'Python')
1038 if os.path.exists(python_lib):
1040 python_version = self.get_python_version()
1041 python_lib = os.path.join(sys.prefix, 'lib',
1042 'python%s' % python_version, 'config',
1043 'libpython%s.a' % python_version)
1044 if os.path.exists(python_lib):
1046 # We can't find it, so maybe it's in the standard path
1049 def wait_for(self, fname, timeout=10.0, popen=None):
1051 Waits for the specified file name to exist.
1054 while not os.path.exists(fname):
1055 if timeout and waited >= timeout:
1056 sys.stderr.write('timed out waiting for %s to exist\n' % fname)
1063 waited = waited + 1.0
1065 def get_alt_cpp_suffix(self):
1067 Many CXX tests have this same logic.
1068 They all needed to determine if the current os supports
1069 files with .C and .c as different files or not
1070 in which case they are instructed to use .cpp instead of .C
1072 if not case_sensitive_suffixes('.c','.C'):
1073 alt_cpp_suffix = '.cpp'
1075 alt_cpp_suffix = '.C'
1076 return alt_cpp_suffix
1079 # In some environments, $AR will generate a warning message to stderr
1080 # if the library doesn't previously exist and is being created. One
1081 # way to fix this is to tell AR to be quiet (sometimes the 'c' flag),
1082 # but this is difficult to do in a platform-/implementation-specific
1083 # method. Instead, we will use the following as a stderr match for
1084 # tests that use AR so that we will view zero or more "ar: creating
1085 # <file>" messages to be successful executions of the test (see
1086 # test/AR.py for sample usage).
1088 noisy_ar=r'(ar: creating( archive)? \S+\n?)*'