8cba239dc35d7178763c1c9c486528c98b983ecc
[scons.git] / etc / TestSCons.py
1 """
2 TestSCons.py:  a testing framework for the SCons software construction
3 tool.
4
5 A TestSCons environment object is created via the usual invocation:
6
7     test = TestSCons()
8
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.
13 """
14
15 # Copyright 2001, 2002, 2003 Steven Knight
16
17 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
18
19 import os
20 import os.path
21 import string
22 import sys
23
24 from TestCommon import *
25 from TestCommon import __all__
26
27 __all__.extend([ 'TestSCons',
28                  'python',
29                  '_exe',
30                  '_obj',
31                  '_shobj',
32                  'lib_',
33                  '_lib',
34                  'dll_',
35                  '_dll'
36                ])
37
38 python = python_executable
39 _exe = exe_suffix
40 _obj = obj_suffix
41 _shobj = shobj_suffix
42 _lib = lib_suffix
43 lib_ = lib_prefix
44 _dll = dll_suffix
45 dll_ = dll_prefix
46
47 def gccFortranLibs():
48     """Test whether -lfrtbegin is required.  This can probably be done in
49     a more reliable way, but using popen3 is relatively efficient."""
50
51     libs = ['g2c']
52
53     try:
54         import popen2
55         stderr = popen2.popen3('gcc -v')[2]
56     except OSError:
57         return libs
58
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
64                 break
65     return libs
66
67
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):
72         return 0
73 else:
74     def case_sensitive_suffixes(s1, s2):
75         return (os.path.normcase(s1) != os.path.normcase(s2))
76
77
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:
83     fortran_lib = ['ftn']
84 else:
85     fortran_lib = gccFortranLibs()
86
87
88 class TestSCons(TestCommon):
89     """Class for testing SCons.
90
91     This provides a common place for initializing SCons tests,
92     eliminating the need to begin every test with the same repeated
93     initializations.
94     """
95
96     def __init__(self, **kw):
97         """Initialize an SCons testing object.
98
99         If they're not overridden by keyword arguments, this
100         initializes the object with the following default values:
101
102                 program = 'scons' if it exists,
103                           else 'scons.py'
104                 interpreter = 'python'
105                 match = match_exact
106                 workdir = ''
107
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
112         is not necessary.
113         """
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'
119                 else:
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'):
126             kw['workdir'] = ''
127         apply(TestCommon.__init__, [self], kw)
128
129     def detect(self, var, prog=None):
130         """
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
136         used as prog.
137         """
138
139         import SCons.Environment
140         env = SCons.Environment.Environment()
141         try:
142             if prog is None:
143                 prog = env[var]
144             return env[var] == prog and env.WhereIs(prog)
145         except KeyError:
146             return None
147
148     def detect_tool(self, tool, prog=None):
149         """
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().
154
155         By default, progs is set to the value passed into the tools parameter.
156         """
157
158         if not prog:
159             prog = tool
160         import SCons.Environment
161         import SCons.Errors
162         try:
163             env=SCons.Environment.Environment(tools=[tool])
164         except (SCons.Errors.UserError, SCons.Errors.InternalError):
165             return None
166         return env.Detect([prog])
167
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
171         """
172         if error:
173             term = "scons: building terminated because of errors.\n"
174         else:
175             term = "scons: done building targets.\n"
176         return "scons: Reading SConscript files ...\n" + \
177                read_str + \
178                "scons: done reading SConscript files.\n" + \
179                "scons: Building targets ...\n" + \
180                build_str + \
181                term
182
183     def up_to_date(self, options = None, arguments = None, read_str = "", **kw):
184         s = ""
185         for arg in string.split(arguments):
186             s = s + "scons: `%s' is up to date.\n" % arg
187             if options:
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)
192
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.
197         """
198         s = ""
199         for  arg in string.split(arguments):
200             s = s + "(?!scons: `%s' is up to date.)" % arg
201             if options:
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