http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / bin / scons-test.py
1 #!/usr/bin/env python
2 #
3 # A script that takes an scons-src-{version}.zip file, unwraps it in
4 # a temporary location, and calls runtest.py to execute one or more of
5 # its tests.
6 #
7 # The default is to download the latest scons-src archive from the SCons
8 # web site, and to execute all of the tests.
9 #
10 # With a little more work, this will become the basis of an automated
11 # testing and reporting system that anyone will be able to use to
12 # participate in testing SCons on their system and regularly reporting
13 # back the results.  A --xml option is a stab at gathering a lot of
14 # relevant information about the system, the Python version, etc.,
15 # so that problems on different platforms can be identified sooner.
16 #
17
18 import getopt
19 import imp
20 import os
21 import os.path
22 import sys
23 import tempfile
24 import time
25 import zipfile
26
27 try:
28     # try Python 3.x style
29     from urllib.request import urlretrieve
30 except ImportError:
31     # nope, must be 2.x; this hack is equivalent
32     import imp
33     # protect import from fixer
34     urlretrieve = imp.load_module('urllib',
35                                   *imp.find_module('urllib')).urlretrieve
36
37 helpstr = """\
38 Usage: scons-test.py [-f zipfile] [-o outdir] [-v] [--xml] [runtest arguments]
39 Options:
40   -f FILE                     Specify input .zip FILE name
41   -o DIR, --out DIR           Change output directory name to DIR
42   -v, --verbose               Print file names when extracting
43   --xml                       XML output
44 """
45
46 opts, args = getopt.getopt(sys.argv[1:],
47                            "f:o:v",
48                            ['file=', 'out=', 'verbose', 'xml'])
49
50 format = None
51 outdir = None
52 printname = lambda x: x
53 inputfile = 'http://scons.sourceforge.net/scons-src-latest.zip'
54
55 for o, a in opts:
56     if o == '-f' or o == '--file':
57         inputfile = a
58     elif o == '-o' or o == '--out':
59         outdir = a
60     elif o == '-v' or o == '--verbose':
61         def printname(x):
62             print x
63     elif o == '--xml':
64         format = o
65
66 startdir = os.getcwd()
67
68 tempfile.template = 'scons-test.'
69 tempdir = tempfile.mktemp()
70
71 if not os.path.exists(tempdir):
72     os.mkdir(tempdir)
73     def cleanup(tempdir=tempdir):
74         import shutil
75         os.chdir(startdir)
76         shutil.rmtree(tempdir)
77     sys.exitfunc = cleanup
78
79 # Fetch the input file if it happens to be across a network somewhere.
80 # Ohmigod, does Python make this simple...
81 inputfile, headers = urlretrieve(inputfile)
82
83 # Unzip the header file in the output directory.  We use our own code
84 # (lifted from scons-unzip.py) to make the output subdirectory name
85 # match the basename of the .zip file.
86 zf = zipfile.ZipFile(inputfile, 'r')
87
88 if outdir is None:
89     name, _ = os.path.splitext(os.path.basename(inputfile))
90     outdir = os.path.join(tempdir, name)
91
92 def outname(n, outdir=outdir):
93     l = []
94     while True:
95         n, tail = os.path.split(n)
96         if not n:
97             break
98         l.append(tail)
99     l.append(outdir)
100     l.reverse()
101     return os.path.join(*l)
102
103 for name in zf.namelist():
104     dest = outname(name)
105     dir = os.path.dirname(dest)
106     try:
107         os.makedirs(dir)
108     except:
109         pass
110     printname(dest)
111     # if the file exists, then delete it before writing
112     # to it so that we don't end up trying to write to a symlink:
113     if os.path.isfile(dest) or os.path.islink(dest):
114         os.unlink(dest)
115     if not os.path.isdir(dest):
116         open(dest, 'w').write(zf.read(name))
117
118 os.chdir(outdir)
119
120 # Load (by hand) the SCons modules we just unwrapped so we can
121 # extract their version information.  Note that we have to override
122 # SCons.Script.main() with a do_nothing() function, because loading up
123 # the 'scons' script will actually try to execute SCons...
124 src_script = os.path.join(outdir, 'src', 'script')
125 src_engine = os.path.join(outdir, 'src', 'engine')
126 src_engine_SCons = os.path.join(src_engine, 'SCons')
127
128 fp, pname, desc = imp.find_module('SCons', [src_engine])
129 SCons = imp.load_module('SCons', fp, pname, desc)
130
131 fp, pname, desc = imp.find_module('Script', [src_engine_SCons])
132 SCons.Script = imp.load_module('Script', fp, pname, desc)
133
134 def do_nothing():
135     pass
136 SCons.Script.main = do_nothing
137
138 fp, pname, desc = imp.find_module('scons', [src_script])
139 scons = imp.load_module('scons', fp, pname, desc)
140 fp.close()
141
142 # Default is to run all the tests by passing the -a flags to runtest.py.
143 if not args:
144     runtest_args = '-a'
145 else:
146     runtest_args = ' '.join(args)
147
148 if format == '--xml':
149
150     print "<scons_test_run>"
151     print "  <sys>"
152     sys_keys = ['byteorder', 'exec_prefix', 'executable', 'maxint', 'maxunicode', 'platform', 'prefix', 'version', 'version_info']
153     for k in sys_keys:
154         print "    <%s>%s</%s>" % (k, sys.__dict__[k], k)
155     print "  </sys>"
156
157     fmt = '%a %b %d %H:%M:%S %Y'
158     print "  <time>"
159     print "    <gmtime>%s</gmtime>" % time.strftime(fmt, time.gmtime())
160     print "    <localtime>%s</localtime>" % time.strftime(fmt, time.localtime())
161     print "  </time>"
162
163     print "  <tempdir>%s</tempdir>" % tempdir
164
165     def print_version_info(tag, module):
166         print "    <%s>" % tag
167         print "      <version>%s</version>" % module.__version__
168         print "      <build>%s</build>" % module.__build__
169         print "      <buildsys>%s</buildsys>" % module.__buildsys__
170         print "      <date>%s</date>" % module.__date__
171         print "      <developer>%s</developer>" % module.__developer__
172         print "    </%s>" % tag
173
174     print "  <scons>"
175     print_version_info("script", scons)
176     print_version_info("engine", SCons)
177     print "  </scons>"
178
179     environ_keys = [
180         'PATH',
181         'SCONSFLAGS',
182         'SCONS_LIB_DIR',
183         'PYTHON_ROOT',
184         'QTDIR',
185
186         'COMSPEC',
187         'INTEL_LICENSE_FILE',
188         'INCLUDE',
189         'LIB',
190         'MSDEVDIR',
191         'OS',
192         'PATHEXT',
193         'SystemRoot',
194         'TEMP',
195         'TMP',
196         'USERNAME',
197         'VXDOMNTOOLS',
198         'WINDIR',
199         'XYZZY'
200
201         'ENV',
202         'HOME',
203         'LANG',
204         'LANGUAGE',
205         'LOGNAME',
206         'MACHINE',
207         'OLDPWD',
208         'PWD',
209         'OPSYS',
210         'SHELL',
211         'TMPDIR',
212         'USER',
213     ]
214
215     print "  <environment>"
216     for key in sorted(environ_keys):
217         value = os.environ.get(key)
218         if value:
219             print "    <variable>"
220             print "      <name>%s</name>" % key
221             print "      <value>%s</value>" % value
222             print "    </variable>"
223     print "  </environment>"
224
225     command = '"%s" runtest.py -q -o - --xml %s' % (sys.executable, runtest_args)
226     #print command
227     os.system(command)
228     print "</scons_test_run>"
229
230 else:
231
232     def print_version_info(tag, module):
233         print "\t%s: v%s.%s, %s, by %s on %s" % (tag,
234                                                  module.__version__,
235                                                  module.__build__,
236                                                  module.__date__,
237                                                  module.__developer__,
238                                                  module.__buildsys__)
239
240     print "SCons by Steven Knight et al.:"
241     print_version_info("script", scons)
242     print_version_info("engine", SCons)
243
244     command = '"%s" runtest.py %s' % (sys.executable, runtest_args)
245     #print command
246     os.system(command)
247
248 # Local Variables:
249 # tab-width:4
250 # indent-tabs-mode:nil
251 # End:
252 # vim: set expandtab tabstop=4 shiftwidth=4: