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.
15 # Copyright 2001, 2002, 2003 Steven Knight
17 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
24 from TestCommon import *
25 from TestCommon import __all__
27 __all__.extend([ 'TestSCons',
38 python = python_executable
48 """Test whether -lfrtbegin is required. This can probably be done in
49 a more reliable way, but using popen3 is relatively efficient."""
55 stderr = popen2.popen3('gcc -v')[2]
59 for l in stderr.readlines():
60 list = string.split(l)
61 if len(list) > 3 and list[:2] == ['gcc', 'version']:
62 if list[2][:2] == '3.':
63 libs = ['frtbegin'] + libs
68 if sys.platform == 'cygwin':
69 # On Cygwin, os.path.normcase() lies, so just report back the
70 # fact that the underlying Win32 OS is case-insensitive.
71 def case_sensitive_suffixes(s1, s2):
74 def case_sensitive_suffixes(s1, s2):
75 return (os.path.normcase(s1) != os.path.normcase(s2))
78 if sys.platform == 'win32':
79 fortran_lib = gccFortranLibs()
80 elif sys.platform == 'cygwin':
81 fortran_lib = gccFortranLibs()
82 elif string.find(sys.platform, 'irix') != -1:
85 fortran_lib = gccFortranLibs()
88 class TestSCons(TestCommon):
89 """Class for testing SCons.
91 This provides a common place for initializing SCons tests,
92 eliminating the need to begin every test with the same repeated
96 def __init__(self, **kw):
97 """Initialize an SCons testing object.
99 If they're not overridden by keyword arguments, this
100 initializes the object with the following default values:
102 program = 'scons' if it exists,
104 interpreter = 'python'
108 The workdir value means that, by default, a temporary workspace
109 directory is created for a TestSCons environment. In addition,
110 this method changes directory (chdir) to the workspace directory,
111 so an explicit "chdir = '.'" on all of the run() method calls
114 if not kw.has_key('program'):
115 kw['program'] = os.environ.get('SCONS')
116 if not kw['program']:
117 if os.path.exists('scons'):
118 kw['program'] = 'scons'
120 kw['program'] = 'scons.py'
121 if not kw.has_key('interpreter') and not os.environ.get('SCONS_EXEC'):
122 kw['interpreter'] = python
123 if not kw.has_key('match'):
124 kw['match'] = match_exact
125 if not kw.has_key('workdir'):
127 apply(TestCommon.__init__, [self], kw)
129 def detect(self, var, prog=None):
131 Detect a program named 'prog' by first checking the construction
132 variable named 'var' and finally searching the path used by
133 SCons. If either method fails to detect the program, then false
134 is returned, otherwise the full path to prog is returned. If
135 prog is None, then the value of the environment variable will be
139 import SCons.Environment
140 env = SCons.Environment.Environment()
144 return env[var] == prog and env.WhereIs(prog)
148 def detect_tool(self, tool, prog=None):
150 Given a tool (i.e., tool specification that would be passed
151 to the "tools=" parameter of Environment()) and one a program that
152 corresponds to that tool, return true if and only if we can find
153 that tool using Environment.Detect().
155 By default, progs is set to the value passed into the tools parameter.
160 import SCons.Environment
163 env=SCons.Environment.Environment(tools=[tool])
164 except (SCons.Errors.UserError, SCons.Errors.InternalError):
166 return env.Detect([prog])
168 def wrap_stdout(self, build_str = "", read_str = "", error = 0):
169 """Wraps standard output string(s) in the normal
170 "Reading ... done" and "Building ... done" strings
173 term = "scons: building terminated because of errors.\n"
175 term = "scons: done building targets.\n"
176 return "scons: Reading SConscript files ...\n" + \
178 "scons: done reading SConscript files.\n" + \
179 "scons: Building targets ...\n" + \
183 def up_to_date(self, options = None, arguments = None, read_str = "", **kw):
185 for arg in string.split(arguments):
186 s = s + "scons: `%s' is up to date.\n" % arg
188 arguments = options + " " + arguments
189 kw['arguments'] = arguments
190 kw['stdout'] = self.wrap_stdout(read_str = read_str, build_str = s)
191 apply(self.run, [], kw)
193 def not_up_to_date(self, options = None, arguments = None, **kw):
194 """Asserts that none of the targets listed in arguments is
195 up to date, but does not make any assumptions on other targets.
196 This function is most useful in conjunction with the -n option.
199 for arg in string.split(arguments):
200 s = s + "(?!scons: `%s' is up to date.)" % arg
202 arguments = options + " " + arguments
203 kw['arguments'] = arguments
204 kw['stdout'] = self.wrap_stdout(build_str="("+s+"[^\n]*\n)*")
205 kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
206 kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
207 old_match_func = self.match_func
208 self.match_func = match_re_dotall
209 apply(self.run, [], kw)
210 self.match_func = old_match_func