Update strings to 0.98.2.
[scons.git] / SConstruct
1 #
2 # SConstruct file to build scons packages during development.
3 #
4 # See the README file for an overview of how SCons is built and tested.
5 #
6
7 # When this gets changed, you must also change the copyright_years string
8 # in QMTest/TestSCons.py so the test scripts look for the right string.
9 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
10
11 # This gets inserted into the man pages to reflect the month of release.
12 month_year = 'April 2008'
13
14 #
15 # __COPYRIGHT__
16 #
17 # Permission is hereby granted, free of charge, to any person obtaining
18 # a copy of this software and associated documentation files (the
19 # "Software"), to deal in the Software without restriction, including
20 # without limitation the rights to use, copy, modify, merge, publish,
21 # distribute, sublicense, and/or sell copies of the Software, and to
22 # permit persons to whom the Software is furnished to do so, subject to
23 # the following conditions:
24 #
25 # The above copyright notice and this permission notice shall be included
26 # in all copies or substantial portions of the Software.
27 #
28 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
29 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
30 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #
36
37 import distutils.util
38 import fnmatch
39 import os
40 import os.path
41 import re
42 import stat
43 import string
44 import sys
45 import tempfile
46
47 project = 'scons'
48 default_version = '0.98.2'
49 copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
50
51 SConsignFile()
52
53 #
54 # An internal "whereis" routine to figure out if a given program
55 # is available on this system.
56 #
57 def whereis(file):
58     for dir in string.split(os.environ['PATH'], os.pathsep):
59         f = os.path.join(dir, file)
60         if os.path.isfile(f):
61             try:
62                 st = os.stat(f)
63             except:
64                 continue
65             if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
66                 return f
67     return None
68
69 #
70 # We let the presence or absence of various utilities determine whether
71 # or not we bother to build certain pieces of things.  This should allow
72 # people to still do SCons packaging work even if they don't have all
73 # of the utilities installed (e.g. RPM).
74 #
75 dh_builddeb = whereis('dh_builddeb')
76 fakeroot = whereis('fakeroot')
77 gzip = whereis('gzip')
78 rpmbuild = whereis('rpmbuild') or whereis('rpm')
79 svn = whereis('svn')
80 unzip = whereis('unzip')
81 zip = whereis('zip')
82
83 #
84 # Now grab the information that we "build" into the files.
85 #
86 date = ARGUMENTS.get('DATE')
87 if not date:
88     import time
89     date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
90
91 developer = ARGUMENTS.get('DEVELOPER')
92 if not developer:
93     for variable in ['USERNAME', 'LOGNAME', 'USER']:
94         developer = os.environ.get(variable)
95         if developer:
96             break
97
98 build_system = ARGUMENTS.get('BUILD_SYSTEM')
99 if not build_system:
100     import socket
101     build_system = string.split(socket.gethostname(), '.')[0]
102
103 version = ARGUMENTS.get('VERSION', '')
104 if not version:
105     version = default_version
106
107 revision = ARGUMENTS.get('REVISION', '')
108 if not revision and svn:
109     svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
110     m = re.search('Revision: (\d+)', svn_info)
111     if m:
112         revision = m.group(1)
113
114 checkpoint = ARGUMENTS.get('CHECKPOINT', '')
115 if checkpoint:
116     if checkpoint == 'd':
117         import time
118         checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
119     elif checkpoint == 'r':
120         checkpoint = 'r' + revision
121     version = version + '.' + checkpoint
122
123 svn_status = None
124 svn_status_lines = []
125
126 if svn:
127     svn_status = os.popen("%s status --verbose 2> /dev/null" % svn, "r").read()
128     svn_status_lines = svn_status[:-1].split('\n')
129
130 build_id = ARGUMENTS.get('BUILD_ID')
131 if build_id is None:
132     if revision:
133         build_id = 'r' + revision
134         if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
135             build_id = build_id + '[MODIFIED]'
136     else:
137         build_id = ''
138
139 python_ver = sys.version[0:3]
140
141 platform = distutils.util.get_platform()
142
143 # Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
144 # built with the --enable-shared option.
145
146 ENV = { 'PATH' : os.environ['PATH'] }
147 for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
148     if os.environ.has_key(key):
149         ENV[key] = os.environ[key]
150
151 build_dir = ARGUMENTS.get('BUILDDIR', 'build')
152 if not os.path.isabs(build_dir):
153     build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
154
155 command_line_variables = [
156     ("BUILDDIR=",       "The directory in which to build the packages.  " +
157                         "The default is the './build' subdirectory."),
158
159     ("BUILD_ID=",       "An identifier for the specific build." +
160                         "The default is the Subversion revision number."),
161
162     ("BUILD_SYSTEM=",   "The system on which the packages were built.  " +
163                         "The default is whatever hostname is returned " +
164                         "by socket.gethostname()."),
165
166     ("CHECKPOINT=",     "The specific checkpoint release being packaged, " +
167                         "which will be appended to the VERSION string.  " +
168                         "A value of CHECKPOINT=d will generate a string " +
169                         "of 'd' plus today's date in the format YYYMMDD.  " +
170                         "A value of CHECKPOINT=r will generate a " +
171                         "string of 'r' plus the Subversion revision " +
172                         "number.  Any other CHECKPOINT= string will be " +
173                         "used as is.  There is no default value."),
174
175     ("DATE=",           "The date string representing when the packaging " +
176                         "build occurred.  The default is the day and time " +
177                         "the SConstruct file was invoked, in the format " +
178                         "YYYY/MM/DD HH:MM:SS."),
179
180     ("DEVELOPER=",      "The developer who created the packages.  " +
181                         "The default is the first set environment " +
182                         "variable from the list $USERNAME, $LOGNAME, $USER."),
183
184     ("REVISION=",       "The revision number of the source being built.  " +
185                         "The default is the Subversion revision returned " +
186                         "'svn info', with an appended string of " +
187                         "'[MODIFIED]' if there are any changes in the " +
188                         "working copy."),
189
190     ("VERSION=",        "The SCons version being packaged.  The default " +
191                         "is the hard-coded value '%s' " % default_version +
192                         "from this SConstruct file."),
193 ]
194
195 Default('.', build_dir)
196
197 packaging_flavors = [
198     ('deb',             "A .deb package.  (This is currently not supported.)"),
199
200     ('rpm',             "A RedHat Package Manager file."),
201
202     ('tar-gz',          "The normal .tar.gz file for end-user installation."),
203
204     ('src-tar-gz',      "A .tar.gz file containing all the source " +
205                         "(including tests and documentation)."),
206
207     ('local-tar-gz',    "A .tar.gz file for dropping into other software " +
208                         "for local use."),
209
210     ('zip',             "The normal .zip file for end-user installation."),
211
212     ('src-zip',         "A .zip file containing all the source " +
213                         "(including tests and documentation)."),
214
215     ('local-zip',       "A .zip file for dropping into other software " +
216                         "for local use."),
217 ]
218
219 test_deb_dir          = os.path.join(build_dir, "test-deb")
220 test_rpm_dir          = os.path.join(build_dir, "test-rpm")
221 test_tar_gz_dir       = os.path.join(build_dir, "test-tar-gz")
222 test_src_tar_gz_dir   = os.path.join(build_dir, "test-src-tar-gz")
223 test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
224 test_zip_dir          = os.path.join(build_dir, "test-zip")
225 test_src_zip_dir      = os.path.join(build_dir, "test-src-zip")
226 test_local_zip_dir    = os.path.join(build_dir, "test-local-zip")
227
228 unpack_tar_gz_dir     = os.path.join(build_dir, "unpack-tar-gz")
229 unpack_zip_dir        = os.path.join(build_dir, "unpack-zip")
230
231 if platform == "win32":
232     tar_hflag = ''
233     python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
234     project_script_subinst_dir = 'Scripts'
235 else:
236     tar_hflag = 'h'
237     python_project_subinst_dir = os.path.join("lib", project)
238     project_script_subinst_dir = 'bin'
239
240
241
242 import textwrap
243
244 indent_fmt = '  %-26s  '
245
246 Help("""\
247 The following aliases build packages of various types, and unpack the
248 contents into build/test-$PACKAGE subdirectories, which can be used by the
249 runtest.py -p option to run tests against what's been actually packaged:
250
251 """)
252
253 aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
254 aliases.sort()
255
256 for alias, help_text in aliases:
257     tw = textwrap.TextWrapper(
258         width = 78,
259         initial_indent = indent_fmt % alias,
260         subsequent_indent = indent_fmt % '' + '  ',
261     )
262     Help(tw.fill(help_text) + '\n')
263
264 Help("""
265 The following command-line variables can be set:
266
267 """)
268
269 for variable, help_text in command_line_variables:
270     tw = textwrap.TextWrapper(
271         width = 78,
272         initial_indent = indent_fmt % variable,
273         subsequent_indent = indent_fmt % '' + '  ',
274     )
275     Help(tw.fill(help_text) + '\n')
276
277
278
279 zcat = 'gzip -d -c'
280
281 #
282 # Figure out if we can handle .zip files.
283 #
284 zipit = None
285 unzipit = None
286 try:
287     import zipfile
288
289     def zipit(env, target, source):
290         print "Zipping %s:" % str(target[0])
291         def visit(arg, dirname, names):
292             for name in names:
293                 path = os.path.join(dirname, name)
294                 if os.path.isfile(path):
295                     arg.write(path)
296         zf = zipfile.ZipFile(str(target[0]), 'w')
297         olddir = os.getcwd()
298         os.chdir(env['CD'])
299         try: os.path.walk(env['PSV'], visit, zf)
300         finally: os.chdir(olddir)
301         zf.close()
302
303     def unzipit(env, target, source):
304         print "Unzipping %s:" % str(source[0])
305         zf = zipfile.ZipFile(str(source[0]), 'r')
306         for name in zf.namelist():
307             dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
308             dir = os.path.dirname(dest)
309             try:
310                 os.makedirs(dir)
311             except:
312                 pass
313             print dest,name
314             # if the file exists, then delete it before writing
315             # to it so that we don't end up trying to write to a symlink:
316             if os.path.isfile(dest) or os.path.islink(dest):
317                 os.unlink(dest)
318             if not os.path.isdir(dest):
319                 open(dest, 'wb').write(zf.read(name))
320
321 except:
322     if unzip and zip:
323         zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
324         unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
325
326 def SCons_revision(target, source, env):
327     """Interpolate specific values from the environment into a file.
328
329     This is used to copy files into a tree that gets packaged up
330     into the source file package.
331     """
332     t = str(target[0])
333     s = source[0].rstr()
334     contents = open(s, 'rb').read()
335     # Note:  We construct the __*__ substitution strings here
336     # so that they don't get replaced when this file gets
337     # copied into the tree for packaging.
338     contents = string.replace(contents, '__BUILD'     + '__', env['BUILD'])
339     contents = string.replace(contents, '__BUILDSYS'  + '__', env['BUILDSYS'])
340     contents = string.replace(contents, '__COPYRIGHT' + '__', env['COPYRIGHT'])
341     contents = string.replace(contents, '__DATE'      + '__', env['DATE'])
342     contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
343     contents = string.replace(contents, '__FILE'      + '__', str(source[0]))
344     contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
345     contents = string.replace(contents, '__REVISION'  + '__', env['REVISION'])
346     contents = string.replace(contents, '__VERSION'   + '__', env['VERSION'])
347     contents = string.replace(contents, '__NULL'      + '__', '')
348     open(t, 'wb').write(contents)
349     os.chmod(t, os.stat(s)[0])
350
351 revbuilder = Builder(action = Action(SCons_revision,
352                                      varlist=['COPYRIGHT', 'VERSION']))
353
354 def soelim(target, source, env):
355     """
356     Interpolate files included in [gnt]roff source files using the
357     .so directive.
358
359     This behaves somewhat like the soelim(1) wrapper around groff, but
360     makes us independent of whether the actual underlying implementation
361     includes an soelim() command or the corresponding command-line option
362     to groff(1).  The key behavioral difference is that this doesn't
363     recursively include .so files from the include file.  Not yet, anyway.
364     """
365     t = str(target[0])
366     s = str(source[0])
367     dir, f = os.path.split(s)
368     tfp = open(t, 'w')
369     sfp = open(s, 'r')
370     for line in sfp.readlines():
371         if line[:4] in ['.so ', "'so "]:
372             sofile = os.path.join(dir, line[4:-1])
373             tfp.write(open(sofile, 'r').read())
374         else:
375             tfp.write(line)
376     sfp.close()
377     tfp.close()
378
379 def soscan(node, env, path):
380     c = node.get_contents()
381     return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
382
383 soelimbuilder = Builder(action = Action(soelim),
384                         source_scanner = Scanner(soscan))
385
386 # When copying local files from a Repository (Aegis),
387 # just make copies, don't symlink them.
388 SetOption('duplicate', 'copy')
389
390 env = Environment(
391                    ENV                 = ENV,
392
393                    BUILD               = build_id,
394                    BUILDDIR            = build_dir,
395                    BUILDSYS            = build_system,
396                    COPYRIGHT           = copyright,
397                    DATE                = date,
398                    DEVELOPER           = developer,
399                    DISTDIR             = os.path.join(build_dir, 'dist'),
400                    MONTH_YEAR          = month_year,
401                    REVISION            = revision,
402                    VERSION             = version,
403                    DH_COMPAT           = 2,
404
405                    TAR_HFLAG           = tar_hflag,
406
407                    ZIP                 = zip,
408                    ZIPFLAGS            = '-r',
409                    UNZIP               = unzip,
410                    UNZIPFLAGS          = '-o -d $UNPACK_ZIP_DIR',
411
412                    ZCAT                = zcat,
413
414                    RPMBUILD            = rpmbuild,
415                    RPM2CPIO            = 'rpm2cpio',
416
417                    TEST_DEB_DIR        = test_deb_dir,
418                    TEST_RPM_DIR        = test_rpm_dir,
419                    TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
420                    TEST_SRC_ZIP_DIR    = test_src_zip_dir,
421                    TEST_TAR_GZ_DIR     = test_tar_gz_dir,
422                    TEST_ZIP_DIR        = test_zip_dir,
423
424                    UNPACK_TAR_GZ_DIR   = unpack_tar_gz_dir,
425                    UNPACK_ZIP_DIR      = unpack_zip_dir,
426
427                    BUILDERS            = { 'SCons_revision' : revbuilder,
428                                            'SOElim' : soelimbuilder },
429
430                    PYTHON              = '"%s"' % sys.executable,
431                    PYTHONFLAGS         = '-tt',
432                  )
433
434 Version_values = [Value(version), Value(build_id)]
435
436 #
437 # Define SCons packages.
438 #
439 # In the original, more complicated packaging scheme, we were going
440 # to have separate packages for:
441 #
442 #       python-scons    only the build engine
443 #       scons-script    only the script
444 #       scons           the script plus the build engine
445 #
446 # We're now only delivering a single "scons" package, but this is still
447 # "built" as two sub-packages (the build engine and the script), so
448 # the definitions remain here, even though we're not using them for
449 # separate packages.
450 #
451
452 python_scons = {
453         'pkg'           : 'python-' + project,
454         'src_subdir'    : 'engine',
455         'inst_subdir'   : os.path.join('lib', 'python1.5', 'site-packages'),
456         'rpm_dir'       : '/usr/lib/scons',
457
458         'debian_deps'   : [
459                             'debian/changelog',
460                             'debian/control',
461                             'debian/copyright',
462                             'debian/dirs',
463                             'debian/docs',
464                             'debian/postinst',
465                             'debian/prerm',
466                             'debian/rules',
467                           ],
468
469         'files'         : [ 'LICENSE.txt',
470                             'README.txt',
471                             'setup.cfg',
472                             'setup.py',
473                           ],
474
475         'filemap'       : {
476                             'LICENSE.txt' : '../LICENSE.txt'
477                           },
478
479         'buildermap'    : {},
480
481         'extra_rpm_files' : [],
482
483         'explicit_deps' : {
484                             'SCons/__init__.py' : Version_values,
485                           },
486 }
487
488 # Figure out the name of a .egg-info file that might be generated
489 # as part of the RPM package.  There are two complicating factors.
490 #
491 # First, the RPM spec file we generate will just execute "python", not
492 # necessarily the one in sys.executable.  If *that* version of python has
493 # a distutils that knows about Python eggs, then setup.py will generate a
494 # .egg-info file, so we have to execute any distutils logic in a subshell.
495 #
496 # Second, we can't just have the subshell check for the existence of the
497 # distutils.command.install_egg_info module and generate the expected
498 # file name by hand, the way we used to, because different systems can
499 # have slightly different .egg-info naming conventions.  (Specifically,
500 # Ubuntu overrides the default behavior to remove the Python version
501 # string from the .egg-info file name.)  The right way to do this is to
502 # actually call into the install_egg_info() class to have it generate
503 # the expected name for us.
504 #
505 # This is all complicated enough that we do it by writing an in-line
506 # script to a temporary file and then feeding it to a separate invocation
507 # of "python" to tell us the actual name of the generated .egg-info file.
508
509 print_egg_info_name = """
510 try:
511     from distutils.dist import Distribution
512     from distutils.command.install_egg_info import install_egg_info
513 except ImportError:
514     pass
515 else:
516     dist = Distribution({'name' : "scons", 'version' : '%s'})
517     i = install_egg_info(dist)
518     i.finalize_options()
519     import os.path
520     print os.path.split(i.outputs[0])[1]
521 """ % version
522
523 try:
524     fd, tfname = tempfile.mkstemp()
525     tfp = os.fdopen(fd, "w")
526     tfp.write(print_egg_info_name)
527     tfp.close()
528     egg_info_file = os.popen("python %s" % tfname).read()[:-1]
529     if egg_info_file:
530         python_scons['extra_rpm_files'].append(egg_info_file)
531 finally:
532     try:
533         os.unlink(tfname)
534     except EnvironmentError:
535         pass
536
537 #
538 # The original packaging scheme would have have required us to push
539 # the Python version number into the package name (python1.5-scons,
540 # python2.0-scons, etc.), which would have required a definition
541 # like the following.  Leave this here in case we ever decide to do
542 # this in the future, but note that this would require some modification
543 # to src/engine/setup.py before it would really work.
544 #
545 #python2_scons = {
546 #        'pkg'          : 'python2-' + project,
547 #        'src_subdir'   : 'engine',
548 #        'inst_subdir'  : os.path.join('lib', 'python2.2', 'site-packages'),
549 #
550 #        'debian_deps'  : [
551 #                            'debian/changelog',
552 #                            'debian/control',
553 #                            'debian/copyright',
554 #                            'debian/dirs',
555 #                            'debian/docs',
556 #                            'debian/postinst',
557 #                            'debian/prerm',
558 #                            'debian/rules',
559 #                          ],
560 #
561 #        'files'        : [
562 #                            'LICENSE.txt',
563 #                            'README.txt',
564 #                            'setup.cfg',
565 #                            'setup.py',
566 #                          ],
567 #        'filemap'      : {
568 #                            'LICENSE.txt' : '../LICENSE.txt',
569 #                          },
570 #        'buildermap'    : {},
571 #}
572 #
573
574 scons_script = {
575         'pkg'           : project + '-script',
576         'src_subdir'    : 'script',
577         'inst_subdir'   : 'bin',
578         'rpm_dir'       : '/usr/bin',
579
580         'debian_deps'   : [
581                             'debian/changelog',
582                             'debian/control',
583                             'debian/copyright',
584                             'debian/dirs',
585                             'debian/docs',
586                             'debian/postinst',
587                             'debian/prerm',
588                             'debian/rules',
589                           ],
590
591         'files'         : [
592                             'LICENSE.txt',
593                             'README.txt',
594                             'setup.cfg',
595                             'setup.py',
596                           ],
597
598         'filemap'       : {
599                             'LICENSE.txt'       : '../LICENSE.txt',
600                             'scons'             : 'scons.py',
601                             'sconsign'          : 'sconsign.py',
602                             'scons-time'        : 'scons-time.py',
603                            },
604
605         'buildermap'    : {},
606
607         'extra_rpm_files' : [
608                             'scons-' + version,
609                             'sconsign-' + version,
610                             'scons-time-' + version,
611                           ],
612
613         'explicit_deps' : {
614                             'scons'       : Version_values,
615                             'sconsign'    : Version_values,
616                           },
617 }
618
619 scons = {
620         'pkg'           : project,
621
622         'debian_deps'   : [
623                             'debian/changelog',
624                             'debian/control',
625                             'debian/copyright',
626                             'debian/dirs',
627                             'debian/docs',
628                             'debian/postinst',
629                             'debian/prerm',
630                             'debian/rules',
631                           ],
632
633         'files'         : [
634                             'CHANGES.txt',
635                             'LICENSE.txt',
636                             'README.txt',
637                             'RELEASE.txt',
638                             'os_spawnv_fix.diff',
639                             'scons.1',
640                             'sconsign.1',
641                             'scons-time.1',
642                             'script/scons.bat',
643                             #'script/scons-post-install.py',
644                             'setup.cfg',
645                             'setup.py',
646                           ],
647
648         'filemap'       : {
649                             'scons.1' : '$BUILDDIR/doc/man/scons.1',
650                             'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
651                             'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
652                           },
653
654         'buildermap'    : {
655                             'scons.1' : env.SOElim,
656                             'sconsign.1' : env.SOElim,
657                             'scons-time.1' : env.SOElim,
658                           },
659
660         'subpkgs'       : [ python_scons, scons_script ],
661
662         'subinst_dirs'  : {
663                              'python-' + project : python_project_subinst_dir,
664                              project + '-script' : project_script_subinst_dir,
665                            },
666 }
667
668 scripts = ['scons', 'sconsign', 'scons-time']
669
670 src_deps = []
671 src_files = []
672
673 for p in [ scons ]:
674     #
675     # Initialize variables with the right directories for this package.
676     #
677     pkg = p['pkg']
678     pkg_version = "%s-%s" % (pkg, version)
679
680     src = 'src'
681     if p.has_key('src_subdir'):
682         src = os.path.join(src, p['src_subdir'])
683
684     build = os.path.join(build_dir, pkg)
685
686     tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
687     platform_tar_gz = os.path.join(build,
688                                    'dist',
689                                    "%s.%s.tar.gz" % (pkg_version, platform))
690     zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
691     platform_zip = os.path.join(build,
692                                 'dist',
693                                 "%s.%s.zip" % (pkg_version, platform))
694     win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
695
696     #
697     # Update the environment with the relevant information
698     # for this package.
699     #
700     # We can get away with calling setup.py using a directory path
701     # like this because we put a preamble in it that will chdir()
702     # to the directory in which setup.py exists.
703     #
704     setup_py = os.path.join(build, 'setup.py')
705     env.Replace(PKG = pkg,
706                 PKG_VERSION = pkg_version,
707                 SETUP_PY = '"%s"' % setup_py)
708     Local(setup_py)
709
710     #
711     # Read up the list of source files from our MANIFEST.in.
712     # This list should *not* include LICENSE.txt, MANIFEST,
713     # README.txt, or setup.py.  Make a copy of the list for the
714     # destination files.
715     #
716     manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
717     src_files = map(lambda x: x[:-1],
718                     open(manifest_in).readlines())
719     raw_files = src_files[:]
720     dst_files = src_files[:]
721     rpm_files = []
722
723     MANIFEST_in_list = []
724
725     if p.has_key('subpkgs'):
726         #
727         # This package includes some sub-packages.  Read up their
728         # MANIFEST.in files, and add them to our source and destination
729         # file lists, modifying them as appropriate to add the
730         # specified subdirs.
731         #
732         for sp in p['subpkgs']:
733             ssubdir = sp['src_subdir']
734             isubdir = p['subinst_dirs'][sp['pkg']]
735             MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
736             MANIFEST_in_list.append(MANIFEST_in)
737             files = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
738             raw_files.extend(files)
739             src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), files))
740             for f in files:
741                 r = os.path.join(sp['rpm_dir'], f)
742                 rpm_files.append(r)
743                 if f[-3:] == ".py":
744                     rpm_files.append(r + 'c')
745             for f in sp.get('extra_rpm_files', []):
746                 r = os.path.join(sp['rpm_dir'], f)
747                 rpm_files.append(r)
748             files = map(lambda x, i=isubdir: os.path.join(i, x), files)
749             dst_files.extend(files)
750             for k, f in sp['filemap'].items():
751                 if f:
752                     k = os.path.join(ssubdir, k)
753                     p['filemap'][k] = os.path.join(ssubdir, f)
754             for f, deps in sp['explicit_deps'].items():
755                 f = os.path.join(build, ssubdir, f)
756                 env.Depends(f, deps)
757
758     #
759     # Now that we have the "normal" source files, add those files
760     # that are standard for each distribution.  Note that we don't
761     # add these to dst_files, because they don't get installed.
762     # And we still have the MANIFEST to add.
763     #
764     src_files.extend(p['files'])
765
766     #
767     # Now run everything in src_file through the sed command we
768     # concocted to expand __FILE__, __VERSION__, etc.
769     #
770     for b in src_files:
771         s = p['filemap'].get(b, b)
772         if not s[0] == '$' and not os.path.isabs(s):
773             s = os.path.join(src, s)
774         builder = p['buildermap'].get(b, env.SCons_revision)
775         x = builder(os.path.join(build, b), s)
776         Local(x)
777
778     #
779     # NOW, finally, we can create the MANIFEST, which we do
780     # by having Python spit out the contents of the src_files
781     # array we've carefully created.  After we've added
782     # MANIFEST itself to the array, of course.
783     #
784     src_files.append("MANIFEST")
785     MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
786
787     def write_src_files(target, source, **kw):
788         global src_files
789         src_files.sort()
790         f = open(str(target[0]), 'wb')
791         for file in src_files:
792             f.write(file + "\n")
793         f.close()
794         return 0
795     env.Command(os.path.join(build, 'MANIFEST'),
796                 MANIFEST_in_list,
797                 write_src_files)
798
799     #
800     # Now go through and arrange to create whatever packages we can.
801     #
802     build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
803     apply(Local, build_src_files, {})
804
805     distutils_formats = []
806
807     distutils_targets = [ win32_exe ]
808
809     dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
810     Local(dist_distutils_targets)
811     AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
812
813     if not gzip:
814         print "gzip not found; skipping .tar.gz package for %s." % pkg
815     else:
816
817         distutils_formats.append('gztar')
818
819         src_deps.append(tar_gz)
820
821         distutils_targets.extend([ tar_gz, platform_tar_gz ])
822
823         dist_tar_gz             = env.Install('$DISTDIR', tar_gz)
824         dist_platform_tar_gz    = env.Install('$DISTDIR', platform_tar_gz)
825         Local(dist_tar_gz, dist_platform_tar_gz)
826         AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
827         AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
828
829         #
830         # Unpack the tar.gz archive created by the distutils into
831         # build/unpack-tar-gz/scons-{version}.
832         #
833         # We'd like to replace the last three lines with the following:
834         #
835         #       tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
836         #
837         # but that gives heartburn to Cygwin's tar, so work around it
838         # with separate zcat-tar-rm commands.
839         #
840         unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
841                                          os.path.join(u, pv, x),
842                                   src_files)
843         env.Command(unpack_tar_gz_files, dist_tar_gz, [
844                     Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
845                     "$ZCAT $SOURCES > .temp",
846                     "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
847                     Delete(".temp"),
848         ])
849
850         #
851         # Run setup.py in the unpacked subdirectory to "install" everything
852         # into our build/test subdirectory.  The runtest.py script will set
853         # PYTHONPATH so that the tests only look under build/test-{package},
854         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
855         # and unittest.py).  This makes sure that our tests pass with what
856         # we really packaged, not because of something hanging around in
857         # the development directory.
858         #
859         # We can get away with calling setup.py using a directory path
860         # like this because we put a preamble in it that will chdir()
861         # to the directory in which setup.py exists.
862         #
863         dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
864         env.Command(dfiles, unpack_tar_gz_files, [
865             Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
866             Delete("$TEST_TAR_GZ_DIR"),
867             '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
868                 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
869         ])
870
871         #
872         # Generate portage files for submission to Gentoo Linux.
873         #
874         gentoo = os.path.join(build, 'gentoo')
875         ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
876         digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
877         env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
878         def Digestify(target, source, env):
879             import md5
880             def hexdigest(s):
881                 """Return a signature as a string of hex characters.
882                 """
883                 # NOTE:  This routine is a method in the Python 2.0 interface
884                 # of the native md5 module, but we want SCons to operate all
885                 # the way back to at least Python 1.5.2, which doesn't have it.
886                 h = string.hexdigits
887                 r = ''
888                 for c in s:
889                     i = ord(c)
890                     r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
891                 return r
892             src = source[0].rfile()
893             contents = open(str(src)).read()
894             sig = hexdigest(md5.new(contents).digest())
895             bytes = os.stat(str(src))[6]
896             open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig,
897                                                                 src.name,
898                                                                 bytes))
899         env.Command(digest, tar_gz, Digestify)
900
901     if not zipit:
902         print "zip not found; skipping .zip package for %s." % pkg
903     else:
904
905         distutils_formats.append('zip')
906
907         src_deps.append(zip)
908
909         distutils_targets.extend([ zip, platform_zip ])
910
911         dist_zip            = env.Install('$DISTDIR', zip)
912         dist_platform_zip   = env.Install('$DISTDIR', platform_zip)
913         Local(dist_zip, dist_platform_zip)
914         AddPostAction(dist_zip, Chmod(dist_zip, 0644))
915         AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
916
917         #
918         # Unpack the zip archive created by the distutils into
919         # build/unpack-zip/scons-{version}.
920         #
921         unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
922                                       os.path.join(u, pv, x),
923                                src_files)
924
925         env.Command(unpack_zip_files, dist_zip, [
926             Delete(os.path.join(unpack_zip_dir, pkg_version)),
927             unzipit,
928         ])
929
930         #
931         # Run setup.py in the unpacked subdirectory to "install" everything
932         # into our build/test subdirectory.  The runtest.py script will set
933         # PYTHONPATH so that the tests only look under build/test-{package},
934         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
935         # and unittest.py).  This makes sure that our tests pass with what
936         # we really packaged, not because of something hanging around in
937         # the development directory.
938         #
939         # We can get away with calling setup.py using a directory path
940         # like this because we put a preamble in it that will chdir()
941         # to the directory in which setup.py exists.
942         #
943         dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
944         env.Command(dfiles, unpack_zip_files, [
945             Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
946             Delete("$TEST_ZIP_DIR"),
947             '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
948                 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
949         ])
950
951     if not rpmbuild:
952         msg = "@echo \"Warning:  Can not build 'rpm':  no rpmbuild utility found\""
953         AlwaysBuild(Alias('rpm', [], msg))
954     else:
955         topdir = os.path.join(build, 'build',
956                               'bdist.' + platform, 'rpm')
957
958         buildroot = os.path.join(build_dir, 'rpm-buildroot')
959
960         BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
961         RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
962         SOURCESdir = os.path.join(topdir, 'SOURCES')
963         SPECSdir = os.path.join(topdir, 'SPECS')
964         SRPMSdir = os.path.join(topdir, 'SRPMS')
965
966         specfile_in = os.path.join('rpm', "%s.spec.in" % pkg)
967         specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
968         sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
969         noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
970         src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
971
972         def spec_function(target, source, env):
973             """Generate the RPM .spec file from the template file.
974
975             This fills in the %files portion of the .spec file with a
976             list generated from our MANIFEST(s), so we don't have to
977             maintain multiple lists.
978             """
979             c = open(str(source[0]), 'rb').read()
980             c = string.replace(c, '__VERSION' + '__', env['VERSION'])
981             c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
982             open(str(target[0]), 'wb').write(c)
983
984         rpm_files.sort()
985         rpm_files_str = string.join(rpm_files, "\n") + "\n"
986         rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str)
987         rpm_spec_action = Action(spec_function, varlist=['RPM_FILES'])
988         rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action)
989
990         env.InstallAs(sourcefile, tar_gz)
991         Local(sourcefile)
992
993         targets = [ noarch_rpm, src_rpm ]
994         cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
995         if not os.path.isdir(BUILDdir):
996             cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
997         t = env.Command(targets, specfile, cmd)
998         env.Depends(t, sourcefile)
999
1000         dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
1001         dist_src_rpm    = env.Install('$DISTDIR', src_rpm)
1002         Local(dist_noarch_rpm, dist_src_rpm)
1003         AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
1004         AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
1005
1006         dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
1007                      dst_files)
1008         env.Command(dfiles,
1009                     dist_noarch_rpm,
1010                     "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
1011
1012     if dh_builddeb and fakeroot:
1013         # Our Debian packaging builds directly into build/dist,
1014         # so we don't need to Install() the .debs.
1015         deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
1016         for d in p['debian_deps']:
1017             b = env.SCons_revision(os.path.join(build, d), d)
1018             env.Depends(deb, b)
1019             Local(b)
1020         env.Command(deb, build_src_files, [
1021             "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
1022                     ])
1023
1024         old = os.path.join('lib', 'scons', '')
1025         new = os.path.join('lib', 'python' + python_ver, 'site-packages', '')
1026         def xxx(s, old=old, new=new):
1027             if s[:len(old)] == old:
1028                 s = new + s[len(old):]
1029             return os.path.join('usr', s)
1030         dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
1031                      map(xxx, dst_files))
1032         env.Command(dfiles,
1033                     deb,
1034                     "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
1035
1036
1037     #
1038     # Use the Python distutils to generate the appropriate packages.
1039     #
1040     commands = [
1041         Delete(os.path.join(build, 'build', 'lib')),
1042         Delete(os.path.join(build, 'build', 'scripts')),
1043     ]
1044
1045     if distutils_formats:
1046         commands.append(Delete(os.path.join(build,
1047                                             'build',
1048                                             'bdist.' + platform,
1049                                             'dumb')))
1050         for format in distutils_formats:
1051             commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)
1052
1053         commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" %  \
1054                             string.join(distutils_formats, ','))
1055
1056     commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst")
1057
1058     env.Command(distutils_targets, build_src_files, commands)
1059
1060     #
1061     # Now create local packages for people who want to let people
1062     # build their SCons-buildable packages without having to
1063     # install SCons.
1064     #
1065     s_l_v = '%s-local-%s' % (pkg, version)
1066
1067     local = pkg + '-local'
1068     build_dir_local = os.path.join(build_dir, local)
1069     build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
1070
1071     dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
1072     dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
1073     AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
1074     AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
1075
1076     commands = [
1077         Delete(build_dir_local),
1078         '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
1079                                                 (build_dir_local, build_dir_local_slv),
1080     ]
1081
1082     for script in scripts:
1083         #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
1084         local_script = os.path.join(build_dir_local, script)
1085         commands.append(Move(local_script + '.py', local_script))
1086
1087     rf = filter(lambda x: not x in scripts, raw_files)
1088     rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
1089     for script in scripts:
1090         rf.append("%s.py" % script)
1091     local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
1092
1093     env.Command(local_targets, build_src_files, commands)
1094
1095     scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
1096     l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
1097     local_targets.append(l)
1098     Local(l)
1099
1100     scons_README = os.path.join(build_dir_local, 'scons-README')
1101     l = env.SCons_revision(scons_README, 'README-local')
1102     local_targets.append(l)
1103     Local(l)
1104
1105     if gzip:
1106         env.Command(dist_local_tar_gz,
1107                     local_targets,
1108                     "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
1109
1110         unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
1111                                     os.path.join(d, x),
1112                              rf)
1113         commands = [Delete(test_local_tar_gz_dir),
1114                     Mkdir(test_local_tar_gz_dir),
1115                     "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
1116
1117         env.Command(unpack_targets, dist_local_tar_gz, commands)
1118
1119     if zipit:
1120         env.Command(dist_local_zip, local_targets, zipit,
1121                     CD = build_dir_local, PSV = '.')
1122
1123         unpack_targets = map(lambda x, d=test_local_zip_dir:
1124                                     os.path.join(d, x),
1125                              rf)
1126         commands = [Delete(test_local_zip_dir),
1127                     Mkdir(test_local_zip_dir),
1128                     unzipit]
1129
1130         env.Command(unpack_targets, dist_local_zip, unzipit,
1131                     UNPACK_ZIP_DIR = test_local_zip_dir)
1132
1133 #
1134 #
1135 #
1136 Export('build_dir', 'env')
1137
1138 SConscript('QMTest/SConscript')
1139
1140 #
1141 #
1142 #
1143 files = [
1144     'runtest.py',
1145 ]
1146
1147 def copy(target, source, env):
1148     t = str(target[0])
1149     s = str(source[0])
1150     open(t, 'wb').write(open(s, 'rb').read())
1151
1152 for file in files:
1153     # Guarantee that real copies of these files always exist in
1154     # build/.  If there's a symlink there, then this is an Aegis
1155     # build and we blow them away now so that they'll get "built" later.
1156     p = os.path.join(build_dir, file)
1157     if os.path.islink(p):
1158         os.unlink(p)
1159     if not os.path.isabs(p):
1160         p = '#' + p
1161     sp = env.Command(p, file, copy)
1162     Local(sp)
1163
1164 #
1165 # Documentation.
1166 #
1167 Export('build_dir', 'env', 'whereis')
1168
1169 SConscript('doc/SConscript')
1170
1171 #
1172 # If we're running in a Subversion working directory, pack up a complete
1173 # source archive from the project files and files in the change.
1174 #
1175
1176 if not svn_status:
1177    "Not building in a Subversion tree; skipping building src package."
1178 else:
1179     slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
1180     sentries = map(lambda l: l.split()[-1], slines)
1181     sfiles = filter(os.path.isfile, sentries)
1182
1183     remove_patterns = [
1184         '.svnt/*',
1185         '*.aeignore',
1186         '*.cvsignore',
1187         'www/*',
1188     ]
1189
1190     for p in remove_patterns:
1191         sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
1192
1193     if sfiles:
1194         ps = "%s-src" % project
1195         psv = "%s-%s" % (ps, version)
1196         b_ps = os.path.join(build_dir, ps)
1197         b_psv = os.path.join(build_dir, psv)
1198         b_psv_stamp = b_psv + '-stamp'
1199
1200         src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
1201         src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
1202
1203         Local(src_tar_gz, src_zip)
1204
1205         for file in sfiles:
1206             env.SCons_revision(os.path.join(b_ps, file), file)
1207
1208         b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
1209         cmds = [
1210             Delete(b_psv),
1211             Copy(b_psv, b_ps),
1212             Touch("$TARGET"),
1213         ]
1214
1215         env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
1216
1217         apply(Local, b_ps_files, {})
1218
1219         if gzip:
1220
1221             env.Command(src_tar_gz, b_psv_stamp,
1222                         "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
1223
1224             #
1225             # Unpack the archive into build/unpack/scons-{version}.
1226             #
1227             unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
1228                                              os.path.join(u, psv, x),
1229                                       sfiles)
1230
1231             #
1232             # We'd like to replace the last three lines with the following:
1233             #
1234             #   tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
1235             #
1236             # but that gives heartburn to Cygwin's tar, so work around it
1237             # with separate zcat-tar-rm commands.
1238             env.Command(unpack_tar_gz_files, src_tar_gz, [
1239                 Delete(os.path.join(unpack_tar_gz_dir, psv)),
1240                 "$ZCAT $SOURCES > .temp",
1241                 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
1242                 Delete(".temp"),
1243             ])
1244
1245             #
1246             # Run setup.py in the unpacked subdirectory to "install" everything
1247             # into our build/test subdirectory.  The runtest.py script will set
1248             # PYTHONPATH so that the tests only look under build/test-{package},
1249             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1250             # and unittest.py).  This makes sure that our tests pass with what
1251             # we really packaged, not because of something hanging around in
1252             # the development directory.
1253             #
1254             # We can get away with calling setup.py using a directory path
1255             # like this because we put a preamble in it that will chdir()
1256             # to the directory in which setup.py exists.
1257             #
1258             dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
1259                             dst_files)
1260             scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
1261             ENV = env.Dictionary('ENV').copy()
1262             ENV['SCONS_LIB_DIR'] = scons_lib_dir
1263             ENV['USERNAME'] = developer
1264             env.Command(dfiles, unpack_tar_gz_files,
1265                 [
1266                 Delete(os.path.join(unpack_tar_gz_dir,
1267                                     psv,
1268                                     'build',
1269                                     'scons',
1270                                     'build')),
1271                 Delete("$TEST_SRC_TAR_GZ_DIR"),
1272                 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1273                     (os.path.join(unpack_tar_gz_dir, psv),
1274                      os.path.join('src', 'script', 'scons.py'),
1275                      os.path.join('build', 'scons')),
1276                 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
1277                     os.path.join(unpack_tar_gz_dir,
1278                                  psv,
1279                                  'build',
1280                                  'scons',
1281                                  'setup.py'),
1282                 ],
1283                 ENV = ENV)
1284
1285         if zipit:
1286
1287             env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)
1288
1289             #
1290             # Unpack the archive into build/unpack/scons-{version}.
1291             #
1292             unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
1293                                              os.path.join(u, psv, x),
1294                                       sfiles)
1295
1296             env.Command(unpack_zip_files, src_zip, [
1297                 Delete(os.path.join(unpack_zip_dir, psv)),
1298                 unzipit
1299             ])
1300
1301             #
1302             # Run setup.py in the unpacked subdirectory to "install" everything
1303             # into our build/test subdirectory.  The runtest.py script will set
1304             # PYTHONPATH so that the tests only look under build/test-{package},
1305             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
1306             # and unittest.py).  This makes sure that our tests pass with what
1307             # we really packaged, not because of something hanging around in
1308             # the development directory.
1309             #
1310             # We can get away with calling setup.py using a directory path
1311             # like this because we put a preamble in it that will chdir()
1312             # to the directory in which setup.py exists.
1313             #
1314             dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
1315                             dst_files)
1316             scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
1317             ENV = env.Dictionary('ENV').copy()
1318             ENV['SCONS_LIB_DIR'] = scons_lib_dir
1319             ENV['USERNAME'] = developer
1320             env.Command(dfiles, unpack_zip_files,
1321                 [
1322                 Delete(os.path.join(unpack_zip_dir,
1323                                     psv,
1324                                     'build',
1325                                     'scons',
1326                                     'build')),
1327                 Delete("$TEST_SRC_ZIP_DIR"),
1328                 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
1329                     (os.path.join(unpack_zip_dir, psv),
1330                      os.path.join('src', 'script', 'scons.py'),
1331                      os.path.join('build', 'scons')),
1332                 '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
1333                     os.path.join(unpack_zip_dir,
1334                                  psv,
1335                                  'build',
1336                                  'scons',
1337                                  'setup.py'),
1338                 ],
1339                 ENV = ENV)
1340
1341 for pf, help_text in packaging_flavors:
1342     Alias(pf, [
1343         os.path.join(build_dir, 'test-'+pf),
1344         os.path.join(build_dir, 'QMTest'),
1345         os.path.join(build_dir, 'runtest.py'),
1346     ])