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