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