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