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