Propogate PYTHONPATH when building SCons.
[scons.git] / SConstruct
1 #
2 # SConstruct file to build scons packages during development.
3 #
4
5 #
6 # Copyright (c) 2001, 2002 Steven Knight
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining
9 # a copy of this software and associated documentation files (the
10 # "Software"), to deal in the Software without restriction, including
11 # without limitation the rights to use, copy, modify, merge, publish,
12 # distribute, sublicense, and/or sell copies of the Software, and to
13 # permit persons to whom the Software is furnished to do so, subject to
14 # the following conditions:
15 #
16 # The above copyright notice and this permission notice shall be included
17 # in all copies or substantial portions of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
20 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #
27
28 import distutils.util
29 import os
30 import os.path
31 import stat
32 import string
33 import sys
34 import time
35
36 project = 'scons'
37 default_version = '0.07'
38
39 Default('.')
40
41 #
42 # An internal "whereis" routine to figure out if we have a
43 # given program available.  Put it in the "cons::" package
44 # so subsidiary Conscript files can get at it easily, too.
45 #
46
47 def whereis(file):
48     for dir in string.split(os.environ['PATH'], os.pathsep):
49         f = os.path.join(dir, file)
50         if os.path.isfile(f):
51             try:
52                 st = os.stat(f)
53             except:
54                 continue
55             if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
56                 return f
57     return None
58
59 #
60 # We let the presence or absence of various utilities determine
61 # whether or not we bother to build certain pieces of things.
62 # This will allow people to still do SCons work even if they
63 # don't have Aegis or RPM installed, for example.
64 #
65 aegis = whereis('aegis')
66 aesub = whereis('aesub')
67 dh_builddeb = whereis('dh_builddeb')
68 fakeroot = whereis('fakeroot')
69 gzip = whereis('gzip')
70 rpm = whereis('rpm')
71 unzip = whereis('unzip')
72 zip = whereis('zip')
73
74 # My installation on Red Hat doesn't like any debhelper version
75 # beyond 2, so let's use 2 as the default on any non-Debian build.
76 if os.path.isfile('/etc/debian_version'):
77     dh_compat = 3
78 else:
79     dh_compat = 2
80
81 #
82 # Now grab the information that we "build" into the files (using sed).
83 #
84 try:
85     date = ARGUMENTS['date']
86 except:
87     date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
88     
89 if ARGUMENTS.has_key('developer'):
90     developer = ARGUMENTS['developer']
91 elif os.environ.has_key('USERNAME'):
92     developer = os.environ['USERNAME']
93 elif os.environ.has_key('LOGNAME'):
94     developer = os.environ['LOGNAME']
95 elif os.environ.has_key('USER'):
96     developer = os.environ['USER']
97
98 try:
99     revision = ARGUMENTS['version']
100 except:
101     if aesub:
102         revision = os.popen(aesub + " \\$version", "r").read()[:-1]
103     else:
104         revision = default_version
105
106 a = string.split(revision, '.')
107 arr = [a[0]]
108 for s in a[1:]:
109     if len(s) == 1:
110         s = '0' + s
111     arr.append(s)
112 revision = string.join(arr, '.')
113
114 # Here's how we'd turn the calculated $revision into our package $version.
115 # This makes it difficult to coordinate with other files (debian/changelog
116 # and rpm/scons.spec) that hard-code the version number, so just go with
117 # the flow for now and hard code it here, too.
118 #if len(arr) >= 2:
119 #    arr = arr[:-1]
120 #def xxx(str):
121 #    if str[0] == 'C' or str[0] == 'D':
122 #        str = str[1:]
123 #    while len(str) > 2 and str[0] == '0':
124 #        str = str[1:]
125 #    return str
126 #arr = map(lambda x, xxx=xxx: xxx(x), arr)
127 #version = string.join(arr, '.')
128 version = default_version
129
130 try:
131     change = ARGUMENTS['change']
132 except:
133     if aesub:
134         change = os.popen(aesub + " \\$change", "r").read()[:-1]
135     else:
136         change = default_version
137
138 python_ver = sys.version[0:3]
139
140 platform = distutils.util.get_platform()
141
142 ENV = { 'PATH' : os.environ['PATH'] }
143 for key in ['AEGIS_PROJECT', 'PYTHONPATH']:
144     if os.environ.has_key(key):
145         ENV[key] = os.environ[key]
146
147 lib_project = os.path.join("lib", project)
148
149 unpack_tar_gz_dir = os.path.join(os.getcwd(), "build", "unpack-tar-gz")
150
151 unpack_zip_dir = os.path.join(os.getcwd(), "build", "unpack-zip")
152
153 test_tar_gz_dir = os.path.join(os.getcwd(), "build", "test-tar-gz")
154 test_src_tar_gz_dir = os.path.join(os.getcwd(), "build", "test-src-tar-gz")
155
156 test_zip_dir = os.path.join(os.getcwd(), "build", "test-zip")
157 test_src_zip_dir = os.path.join(os.getcwd(), "build", "test-src-zip")
158
159 test_rpm_dir = os.path.join(os.getcwd(), "build", "test-rpm")
160
161 test_deb_dir = os.path.join(os.getcwd(), "build", "test-deb")
162
163 if platform == "win32":
164     tar_hflag = ''
165     python_project_subinst_dir = None
166     project_script_subinst_dir = 'Scripts'
167 else:
168     tar_hflag = 'h'
169     python_project_subinst_dir = lib_project
170     project_script_subinst_dir = 'bin'
171
172 #
173 # Figure out if we can handle .zip files.
174 #
175 try:
176     import zipfile
177
178     def zipit(env, target, source):
179         print "Zipping %s:" % str(target[0])
180         def visit(arg, dirname, names):
181             for name in names:
182                 path = os.path.join(dirname, name)
183                 if os.path.isfile(path):
184                     arg.write(path)
185         zf = zipfile.ZipFile(str(target[0]), 'w')
186         os.chdir('build')
187         os.path.walk(env['PSV'], visit, zf)
188         os.chdir('..')
189         zf.close()
190
191     def unzipit(env, target, source):
192         print "Unzipping %s:" % str(source[0])
193         zf = zipfile.ZipFile(str(source[0]), 'r')
194         for name in zf.namelist():
195             dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
196             dir = os.path.dirname(dest)
197             try:
198                 os.makedirs(dir)
199             except:
200                 pass
201             print dest,name
202             # if the file exists, then delete it before writing
203             # to it so that we don't end up trying to write to a symlink:
204             if os.path.exists(dest):
205                 os.unlink(dest)
206             open(dest, 'w').write(zf.read(name))
207
208 except:
209     if unzip and zip:
210         zipit = "cd build && $ZIP $ZIPFLAGS dist/${TARGET.file} $PSV"
211         unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
212
213 def SCons_revision(target, source, env):
214     """Interpolate specific values from the environment into a file.
215     
216     This is used to copy files into a tree that gets packaged up
217     into the source file package.
218     """
219     t = str(target[0])
220     s = str(source[0])
221     # Note:  We don't use $VERSION from the environment so that
222     # this routine will change when the version number changes
223     # and things will get rebuilt properly.
224     global version
225     print "SCons_revision() < %s > %s" % (s, t)
226     inf = open(s, 'rb')
227     outf = open(t, 'wb')
228     for line in inf.readlines():
229         # Note:  We construct the __*__ substitution strings here
230         # so that they don't get replaced when this file gets
231         # copied into the tree for packaging.
232         line = string.replace(line, '_' + '_DATE__', env['DATE'])
233         line = string.replace(line, '_' + '_DEVELOPER__', env['DEVELOPER'])
234         line = string.replace(line, '_' + '_FILE__', s)
235         line = string.replace(line, '_' + '_REVISION__', env['REVISION'])
236         line = string.replace(line, '_' + '_VERSION__', version)
237         outf.write(line)
238     inf.close()
239     outf.close()
240     os.chmod(t, os.stat(s)[0])
241
242 revbuilder = Builder(name = 'SCons_revision', action = SCons_revision)
243
244 env = Environment(
245                    ENV                 = ENV,
246  
247                    DATE                = date,
248                    DEVELOPER           = developer,
249                    REVISION            = revision,
250                    VERSION             = version,
251                    DH_COMPAT           = dh_compat,
252
253                    TAR_HFLAG           = tar_hflag,
254
255                    ZIP                 = zip,
256                    ZIPFLAGS            = '-r',
257                    UNZIP               = unzip,
258                    UNZIPFLAGS          = '-o -d $UNPACK_ZIP_DIR',
259
260                    TEST_DEB_DIR        = test_deb_dir,
261                    TEST_RPM_DIR        = test_rpm_dir,
262                    TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
263                    TEST_SRC_ZIP_DIR    = test_src_zip_dir,
264                    TEST_TAR_GZ_DIR     = test_tar_gz_dir,
265                    TEST_ZIP_DIR        = test_zip_dir,
266
267                    UNPACK_TAR_GZ_DIR   = unpack_tar_gz_dir,
268                    UNPACK_ZIP_DIR      = unpack_zip_dir,
269
270                    BUILDERS            = [ revbuilder ],
271                  )
272
273 #
274 # Define SCons packages.
275 #
276 # In the original, more complicated packaging scheme, we were going
277 # to have separate packages for:
278 #
279 #       python-scons    only the build engine
280 #       scons-script    only the script
281 #       scons           the script plus the build engine
282 #
283 # We're now only delivering a single "scons" package, but this is still
284 # "built" as two sub-packages (the build engine and the script), so
285 # the definitions remain here, even though we're not using them for
286 # separate packages.
287 #
288
289 python_scons = {
290         'pkg'           : 'python-' + project,
291         'src_subdir'    : 'engine',
292         'inst_subdir'   : os.path.join('lib', 'python1.5', 'site-packages'),
293
294         'debian_deps'   : [
295                             'debian/rules',
296                             'debian/control',
297                             'debian/changelog',
298                             'debian/copyright',
299                             'debian/python-scons.postinst',
300                             'debian/python-scons.prerm',
301                           ],
302
303         'files'         : [ 'LICENSE.txt',
304                             'README.txt',
305                             'setup.cfg',
306                             'setup.py',
307                           ],
308
309         'filemap'       : {
310                             'LICENSE.txt' : '../LICENSE.txt'
311                           },
312 }
313
314 #
315 # The original packaging scheme would have have required us to push
316 # the Python version number into the package name (python1.5-scons,
317 # python2.0-scons, etc.), which would have required a definition
318 # like the following.  Leave this here in case we ever decide to do
319 # this in the future, but note that this would require some modification
320 # to src/engine/setup.py before it would really work.
321 #
322 #python2_scons = {
323 #        'pkg'          : 'python2-' + project,
324 #        'src_subdir'   : 'engine',
325 #        'inst_subdir'  : os.path.join('lib', 'python2.1', 'site-packages'),
326 #
327 #        'debian_deps'  : [
328 #                           'debian/rules',
329 #                           'debian/control',
330 #                           'debian/changelog',
331 #                           'debian/copyright',
332 #                           'debian/python2-scons.postinst',
333 #                           'debian/python2-scons.prerm',
334 #                          ],
335 #
336 #        'files'        : [
337 #                            'LICENSE.txt',
338 #                            'README.txt',
339 #                            'setup.cfg',
340 #                            'setup.py',
341 #                          ],
342 #        'filemap'      : {
343 #                            'LICENSE.txt' : '../LICENSE.txt',
344 #                          },
345 #}
346 #
347
348 scons_script = {
349         'pkg'           : project + '-script',
350         'src_subdir'    : 'script',
351         'inst_subdir'   : 'bin',
352
353         'debian_deps'   : [
354                             'debian/rules',
355                             'debian/control',
356                             'debian/changelog',
357                             'debian/copyright',
358                             'debian/python-scons.postinst',
359                             'debian/python-scons.prerm',
360                           ],
361
362         'files'         : [
363                             'LICENSE.txt',
364                             'README.txt',
365                             'setup.cfg',
366                             'setup.py',
367                           ],
368
369         'filemap'       : {
370                             'LICENSE.txt' : '../LICENSE.txt',
371                             'scons'       : 'scons.py',
372                            }
373 }
374
375 scons = {
376         'pkg'           : project,
377
378         'debian_deps'   : [ 
379                             'debian/rules',
380                             'debian/control',
381                             'debian/changelog',
382                             'debian/copyright',
383                             'debian/scons.postinst',
384                             'debian/scons.prerm',
385                           ],
386
387         'files'         : [ 
388                             'CHANGES.txt',
389                             'LICENSE.txt',
390                             'README.txt',
391                             'RELEASE.txt',
392                             'os_spawnv_fix.diff',
393                             'scons.1',
394                             'script/scons.bat',
395                             'setup.cfg',
396                             'setup.py',
397                           ],
398
399         'filemap'       : {
400                             'scons.1' : '../doc/man/scons.1',
401                           },
402
403         'subpkgs'       : [ python_scons, scons_script ],
404
405         'subinst_dirs'  : {
406                              'python-' + project : python_project_subinst_dir,
407                              project + '-script' : project_script_subinst_dir,
408                            },
409 }
410
411 src_deps = []
412 src_files = []
413
414 for p in [ scons ]:
415     #
416     # Initialize variables with the right directories for this package.
417     #
418     pkg = p['pkg']
419     pkg_version = "%s-%s" % (pkg, version)
420
421     src = 'src'
422     if p.has_key('src_subdir'):
423         src = os.path.join(src, p['src_subdir'])
424
425     build = os.path.join('build', pkg)
426
427     tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
428     platform_tar_gz = os.path.join(build,
429                                    'dist',
430                                    "%s.%s.tar.gz" % (pkg_version, platform))
431     zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
432     platform_zip = os.path.join(build,
433                                 'dist',
434                                 "%s.%s.zip" % (pkg_version, platform))
435     win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
436
437     #
438     # Update the environment with the relevant information
439     # for this package.
440     #
441     # We can get away with calling setup.py using a directory path
442     # like this because we put a preamble in it that will chdir()
443     # to the directory in which setup.py exists.
444     #
445     env.Update(PKG = pkg,
446                PKG_VERSION = pkg_version,
447                SETUP_PY = os.path.join(build, 'setup.py'))
448
449     #
450     # Read up the list of source files from our MANIFEST.in.
451     # This list should *not* include LICENSE.txt, MANIFEST,
452     # README.txt, or setup.py.  Make a copy of the list for the
453     # destination files.
454     #
455     src_files = map(lambda x: x[:-1],
456                     open(os.path.join(src, 'MANIFEST.in')).readlines())
457     dst_files = src_files[:]
458
459     MANIFEST_in_list = []
460
461     if p.has_key('subpkgs'):
462         #
463         # This package includes some sub-packages.  Read up their
464         # MANIFEST.in files, and add them to our source and destination
465         # file lists, modifying them as appropriate to add the
466         # specified subdirs.
467         #
468         for sp in p['subpkgs']:
469             ssubdir = sp['src_subdir']
470             isubdir = p['subinst_dirs'][sp['pkg']]
471             MANIFEST_in = os.path.join(src, ssubdir, 'MANIFEST.in')
472             MANIFEST_in_list.append(MANIFEST_in)
473             f = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
474             src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), f))
475             if isubdir:
476                 f = map(lambda x, i=isubdir: os.path.join(i, x), f)
477             dst_files.extend(f)
478             for k in sp['filemap'].keys():
479                 f = sp['filemap'][k]
480                 if f:
481                     k = os.path.join(sp['src_subdir'], k)
482                     p['filemap'][k] = os.path.join(sp['src_subdir'], f)
483
484     #
485     # Now that we have the "normal" source files, add those files
486     # that are standard for each distribution.  Note that we don't
487     # add these to dst_files, because they don't get installed.
488     # And we still have the MANIFEST to add.
489     #
490     src_files.extend(p['files'])
491
492     #
493     # Now run everything in src_file through the sed command we
494     # concocted to expand __FILE__, __VERSION__, etc.
495     #
496     for b in src_files:
497         s = p['filemap'].get(b, b)
498         env.SCons_revision(os.path.join(build, b), os.path.join(src, s))
499
500     #
501     # NOW, finally, we can create the MANIFEST, which we do
502     # by having Python spit out the contents of the src_files
503     # array we've carefully created.  After we've added
504     # MANIFEST itself to the array, of course.
505     #
506     src_files.append("MANIFEST")
507     MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
508
509     def copy(target, source, **kw):
510         global src_files
511         src_files.sort()
512         f = open(str(target[0]), 'wb')
513         for file in src_files:
514             f.write(file + "\n")
515         f.close()
516         return 0
517     env.Command(os.path.join(build, 'MANIFEST'), MANIFEST_in_list, copy)
518
519     #
520     # Now go through and arrange to create whatever packages we can.
521     #
522     build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
523
524     distutils_formats = []
525
526     distutils_targets = [ win32_exe ]
527
528     install_targets = distutils_targets[:]
529
530     if gzip:
531
532         distutils_formats.append('gztar')
533
534         src_deps.append(tar_gz)
535
536         distutils_targets.extend([ tar_gz, platform_tar_gz ])
537         install_targets.extend([ tar_gz, platform_tar_gz ])
538
539         #
540         # Unpack the tar.gz archive created by the distutils into
541         # build/unpack-tar-gz/scons-{version}.
542         #
543         # We'd like to replace the last three lines with the following:
544         #
545         #       tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
546         #
547         # but that gives heartburn to Cygwin's tar, so work around it
548         # with separate zcat-tar-rm commands.
549         #
550         unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
551                                          os.path.join(u, pv, x),
552                                   src_files)
553         env.Command(unpack_tar_gz_files, tar_gz, [
554                     "rm -rf %s" % os.path.join(unpack_tar_gz_dir, pkg_version),
555                     "zcat $SOURCES > .temp",
556                     "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
557                     "rm -f .temp",
558         ])
559     
560         #
561         # Run setup.py in the unpacked subdirectory to "install" everything
562         # into our build/test subdirectory.  The runtest.py script will set
563         # PYTHONPATH so that the tests only look under build/test-{package},
564         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
565         # and unittest.py).  This makes sure that our tests pass with what
566         # we really packaged, not because of something hanging around in
567         # the development directory.
568         #
569         # We can get away with calling setup.py using a directory path
570         # like this because we put a preamble in it that will chdir()
571         # to the directory in which setup.py exists.
572         #
573         dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
574         env.Command(dfiles, unpack_tar_gz_files, [
575             "rm -rf %s" % os.path.join(unpack_tar_gz_dir, pkg_version, 'build'),
576             "rm -rf $TEST_TAR_GZ_DIR",
577             "python %s install --prefix=$TEST_TAR_GZ_DIR" % \
578                 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
579         ])
580
581     if zipit:
582
583         distutils_formats.append('zip')
584
585         src_deps.append(zip)
586
587         distutils_targets.extend([ zip, platform_zip ])
588         install_targets.extend([ zip, platform_zip ])
589
590         #
591         # Unpack the zip archive created by the distutils into
592         # build/unpack-zip/scons-{version}.
593         #
594         unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
595                                       os.path.join(u, pv, x),
596                                src_files)
597
598         env.Command(unpack_zip_files, zip, unzipit)
599
600         #
601         # Run setup.py in the unpacked subdirectory to "install" everything
602         # into our build/test subdirectory.  The runtest.py script will set
603         # PYTHONPATH so that the tests only look under build/test-{package},
604         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
605         # and unittest.py).  This makes sure that our tests pass with what
606         # we really packaged, not because of something hanging around in
607         # the development directory.
608         #
609         # We can get away with calling setup.py using a directory path
610         # like this because we put a preamble in it that will chdir()
611         # to the directory in which setup.py exists.
612         #
613         dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
614         env.Command(dfiles, unpack_zip_files, [
615             "rm -rf %s" % os.path.join(unpack_zip_dir, pkg_version, 'build'),
616             "rm -rf $TEST_ZIP_DIR",
617             "python %s install --prefix=$TEST_ZIP_DIR" % \
618                 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
619         ])
620
621     if rpm:
622         topdir = os.path.join(os.getcwd(), build, 'build',
623                               'bdist.' + platform, 'rpm')
624
625         BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
626         RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
627         SOURCESdir = os.path.join(topdir, 'SOURCES')
628         SPECSdir = os.path.join(topdir, 'SPECS')
629         SRPMSdir = os.path.join(topdir, 'SRPMS')
630
631         specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
632         sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
633         rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
634         src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
635
636         env.InstallAs(specfile, os.path.join('rpm', "%s.spec" % pkg))
637         env.InstallAs(sourcefile, tar_gz)
638
639         targets = [ rpm, src_rpm ]
640         cmd = "rpm --define '_topdir $(%s$)' -ba $SOURCES" % topdir
641         if not os.path.isdir(BUILDdir):
642             cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
643         env.Command(targets, specfile, cmd)
644         env.Depends(targets, sourcefile)
645
646         install_targets.extend(targets)
647
648         dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
649                      dst_files)
650         env.Command(dfiles,
651                     rpm,
652                     "rpm2cpio $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
653
654     if dh_builddeb and fakeroot:
655         # Our Debian packaging builds directly into build/dist,
656         # so we don't need to add the .debs to install_targets.
657         deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version))
658         env.Command(deb, build_src_files, [
659             "fakeroot make -f debian/rules VERSION=$VERSION DH_COMPAT=$DH_COMPAT ENVOKED_BY_CONSTRUCT=1 binary-$PKG",
660             "env DH_COMPAT=$DH_COMPAT dh_clean"
661                     ])
662         env.Depends(deb, p['debian_deps'])
663
664         dfiles = map(lambda x, d=test_deb_dir: os.path.join(d, 'usr', x),
665                      dst_files)
666         env.Command(dfiles,
667                     deb,
668                     "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
669
670     #
671     # Use the Python distutils to generate the appropriate packages.
672     #
673     commands = [
674         "rm -rf %s" % os.path.join(build, 'build', 'lib'),
675         "rm -rf %s" % os.path.join(build, 'build', 'scripts'),
676     ]
677
678     if distutils_formats:
679         commands.append("rm -rf %s" % os.path.join(build,
680                                                    'build',
681                                                    'bdist.' + platform,
682                                                    'dumb'))
683         for format in distutils_formats:
684             commands.append("python $SETUP_PY bdist_dumb -f %s" % format)
685
686         commands.append("python $SETUP_PY sdist --formats=%s" %  \
687                             string.join(distutils_formats, ','))
688
689     commands.append("python $SETUP_PY bdist_wininst")
690
691     env.Command(distutils_targets, build_src_files, commands)
692
693     #
694     # And, lastly, install the appropriate packages in the
695     # appropriate subdirectory.
696     #
697     env.Install(os.path.join('build', 'dist'), install_targets)
698
699 #
700 #
701 #
702 Export('env')
703
704 SConscript('etc/SConscript')
705
706 #
707 # Documentation.
708 #
709 BuildDir('build/doc', 'doc')
710
711 Export('env', 'whereis')
712
713 SConscript('build/doc/SConscript')
714
715 #
716 # If we're running in the actual Aegis project, pack up a complete
717 # source archive from the project files and files in the change,
718 # so we can share it with helpful developers who don't use Aegis.
719 #
720
721 if change:
722     df = []
723     cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change
724     for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()):
725         a = string.split(line)
726         if a[1] == "remove":
727             if a[3][0] == '(':
728                 df.append(a[4])
729             else:
730                 df.append(a[3])
731
732     cmd = "aegis -list -terse pf 2>/dev/null"
733     pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
734     cmd = "aegis -list -terse cf 2>/dev/null"
735     cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
736     u = {}
737     for f in pf + cf:
738         u[f] = 1
739     for f in df:
740         del u[f]
741     sfiles = filter(lambda x: x[-9:] != '.aeignore' and x[-9:] != '.sconsign',
742                     u.keys())
743
744     if sfiles:
745         ps = "%s-src" % project
746         psv = "%s-%s" % (ps, version)
747         b_ps = os.path.join('build', ps)
748         b_psv = os.path.join('build', psv)
749         b_psv_stamp = b_psv + '-stamp'
750
751         src_tar_gz = os.path.join('build', 'dist', '%s.tar.gz' % psv)
752         src_zip = os.path.join('build', 'dist', '%s.zip' % psv)
753
754         for file in sfiles:
755             env.SCons_revision(os.path.join(b_ps, file), file)
756
757         b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
758         cmds = [
759             "rm -rf %s" % b_psv,
760             "cp -rp %s %s" % (b_ps, b_psv),
761             "find %s -name .sconsign -exec rm {} \\;" % b_psv,
762             "touch $TARGET",
763         ]
764
765         env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
766
767         if gzip:
768
769             env.Command(src_tar_gz, b_psv_stamp,
770                         "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
771     
772             #
773             # Unpack the archive into build/unpack/scons-{version}.
774             #
775             unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
776                                              os.path.join(u, psv, x),
777                                       sfiles)
778     
779             #
780             # We'd like to replace the last three lines with the following:
781             #
782             #   tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
783             #
784             # but that gives heartburn to Cygwin's tar, so work around it
785             # with separate zcat-tar-rm commands.
786             env.Command(unpack_tar_gz_files, src_tar_gz, [
787                 "rm -rf %s" % os.path.join(unpack_tar_gz_dir, psv),
788                 "zcat $SOURCES > .temp",
789                 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
790                 "rm -f .temp",
791             ])
792     
793             #
794             # Run setup.py in the unpacked subdirectory to "install" everything
795             # into our build/test subdirectory.  The runtest.py script will set
796             # PYTHONPATH so that the tests only look under build/test-{package},
797             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
798             # and unittest.py).  This makes sure that our tests pass with what
799             # we really packaged, not because of something hanging around in
800             # the development directory.
801             #
802             # We can get away with calling setup.py using a directory path
803             # like this because we put a preamble in it that will chdir()
804             # to the directory in which setup.py exists.
805             #
806             dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
807                             dst_files)
808             ENV = env.Dictionary('ENV')
809             ENV['SCONS_LIB_DIR'] = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
810             ENV['USERNAME'] = developer
811             env.Copy(ENV = ENV).Command(dfiles, unpack_tar_gz_files, [
812                 "rm -rf %s" % os.path.join(unpack_tar_gz_dir,
813                                            psv,
814                                            'build',
815                                            'scons',
816                                            'build'),
817                 "rm -rf $TEST_SRC_TAR_GZ_DIR",
818                 "cd %s && python %s %s" % \
819                     (os.path.join(unpack_tar_gz_dir, psv),
820                      os.path.join('src', 'script', 'scons.py'),
821                      os.path.join('build', 'scons')),
822                 "python %s install --prefix=$TEST_SRC_TAR_GZ_DIR" % \
823                     os.path.join(unpack_tar_gz_dir,
824                                  psv,
825                                  'build',
826                                  'scons',
827                                  'setup.py'),
828             ])
829
830         if zipit:
831
832             env.Copy(PSV = psv).Command(src_zip, b_psv_stamp, zipit)
833     
834             #
835             # Unpack the archive into build/unpack/scons-{version}.
836             #
837             unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
838                                              os.path.join(u, psv, x),
839                                       sfiles)
840     
841             env.Command(unpack_zip_files, src_zip, unzipit)
842     
843             #
844             # Run setup.py in the unpacked subdirectory to "install" everything
845             # into our build/test subdirectory.  The runtest.py script will set
846             # PYTHONPATH so that the tests only look under build/test-{package},
847             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
848             # and unittest.py).  This makes sure that our tests pass with what
849             # we really packaged, not because of something hanging around in
850             # the development directory.
851             #
852             # We can get away with calling setup.py using a directory path
853             # like this because we put a preamble in it that will chdir()
854             # to the directory in which setup.py exists.
855             #
856             dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
857                             dst_files)
858             ENV = env.Dictionary('ENV')
859             ENV['SCONS_LIB_DIR'] = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
860             ENV['USERNAME'] = developer
861             env.Copy(ENV = ENV).Command(dfiles, unpack_zip_files, [
862                 "rm -rf %s" % os.path.join(unpack_zip_dir,
863                                            psv,
864                                            'build',
865                                            'scons',
866                                            'build'),
867                 "rm -rf $TEST_SRC_ZIP_DIR",
868                 "cd %s && python %s %s" % \
869                     (os.path.join(unpack_zip_dir, psv),
870                      os.path.join('src', 'script', 'scons.py'),
871                      os.path.join('build', 'scons')),
872                 "python %s install --prefix=$TEST_SRC_ZIP_DIR" % \
873                     os.path.join(unpack_zip_dir,
874                                  psv,
875                                  'build',
876                                  'scons',
877                                  'setup.py'),
878             ])