be5d3729a6598d9339f87ba4d8d7def647549c00
[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.10'
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 rpmbuild = whereis('rpmbuild') or 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_local_tar_gz_dir = os.path.join(cwd_build, "test-local-tar-gz")
155 test_zip_dir          = os.path.join(cwd_build, "test-zip")
156 test_src_zip_dir      = os.path.join(cwd_build, "test-src-zip")
157 test_local_zip_dir    = os.path.join(cwd_build, "test-local-zip")
158
159 unpack_tar_gz_dir     = os.path.join(cwd_build, "unpack-tar-gz")
160 unpack_zip_dir        = os.path.join(cwd_build, "unpack-zip")
161
162 if platform == "win32":
163     tar_hflag = ''
164     python_project_subinst_dir = None
165     project_script_subinst_dir = 'Scripts'
166 else:
167     tar_hflag = 'h'
168     python_project_subinst_dir = lib_project
169     project_script_subinst_dir = 'bin'
170
171
172 zcat = 'gzip -d -c'
173     
174 #
175 # Figure out if we can handle .zip files.
176 #
177 zipit = None
178 unzipit = None
179 try:
180     import zipfile
181
182     def zipit(env, target, source):
183         print "Zipping %s:" % str(target[0])
184         def visit(arg, dirname, names):
185             for name in names:
186                 path = os.path.join(dirname, name)
187                 if os.path.isfile(path):
188                     arg.write(path)
189         zf = zipfile.ZipFile(str(target[0]), 'w')
190         olddir = os.getcwd()
191         os.chdir(env['CD'])
192         try: os.path.walk(env['PSV'], visit, zf)
193         finally: os.chdir(olddir)
194         zf.close()
195
196     def unzipit(env, target, source):
197         print "Unzipping %s:" % str(source[0])
198         zf = zipfile.ZipFile(str(source[0]), 'r')
199         for name in zf.namelist():
200             dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
201             dir = os.path.dirname(dest)
202             try:
203                 os.makedirs(dir)
204             except:
205                 pass
206             print dest,name
207             # if the file exists, then delete it before writing
208             # to it so that we don't end up trying to write to a symlink:
209             if os.path.isfile(dest) or os.path.islink(dest):
210                 os.unlink(dest)
211             if not os.path.isdir(dest):
212                 open(dest, 'w').write(zf.read(name))
213
214 except:
215     if unzip and zip:
216         zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
217         unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
218
219 def SCons_revision(target, source, env):
220     """Interpolate specific values from the environment into a file.
221     
222     This is used to copy files into a tree that gets packaged up
223     into the source file package.
224     """
225     t = str(target[0])
226     s = source[0].rstr()
227     # Note:  We don't use $VERSION from the environment so that
228     # this routine will change when the version number changes
229     # and things will get rebuilt properly.
230     global version
231     inf = open(s, 'rb')
232     outf = open(t, 'wb')
233     for line in inf.readlines():
234         # Note:  We construct the __*__ substitution strings here
235         # so that they don't get replaced when this file gets
236         # copied into the tree for packaging.
237         line = string.replace(line, '__BUILD'     + '__', env['BUILD'])
238         line = string.replace(line, '__BUILDSYS'  + '__', env['BUILDSYS'])
239         line = string.replace(line, '__DATE'      + '__', env['DATE'])
240         line = string.replace(line, '__DEVELOPER' + '__', env['DEVELOPER'])
241         line = string.replace(line, '__FILE'      + '__', s)
242         line = string.replace(line, '__REVISION'  + '__', env['REVISION'])
243         line = string.replace(line, '__VERSION'   + '__',  version)
244         outf.write(line)
245     inf.close()
246     outf.close()
247     os.chmod(t, os.stat(s)[0])
248
249 revbuilder = Builder(action = SCons_revision)
250
251 env = Environment(
252                    ENV                 = ENV,
253  
254                    BUILD               = build_id,
255                    BUILDSYS            = build_system,
256                    DATE                = date,
257                    DEVELOPER           = developer,
258                    REVISION            = revision,
259                    VERSION             = version,
260                    DH_COMPAT           = 2,
261
262                    TAR_HFLAG           = tar_hflag,
263
264                    ZIP                 = zip,
265                    ZIPFLAGS            = '-r',
266                    UNZIP               = unzip,
267                    UNZIPFLAGS          = '-o -d $UNPACK_ZIP_DIR',
268
269                    ZCAT                = zcat,
270
271                    RPMBUILD            = rpmbuild,
272                    RPM2CPIO            = 'rpm2cpio',
273
274                    TEST_DEB_DIR        = test_deb_dir,
275                    TEST_RPM_DIR        = test_rpm_dir,
276                    TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
277                    TEST_SRC_ZIP_DIR    = test_src_zip_dir,
278                    TEST_TAR_GZ_DIR     = test_tar_gz_dir,
279                    TEST_ZIP_DIR        = test_zip_dir,
280
281                    UNPACK_TAR_GZ_DIR   = unpack_tar_gz_dir,
282                    UNPACK_ZIP_DIR      = unpack_zip_dir,
283
284                    BUILDERS            = { 'SCons_revision' : revbuilder },
285
286                    PYTHON              = sys.executable
287                  )
288
289 #
290 # Define SCons packages.
291 #
292 # In the original, more complicated packaging scheme, we were going
293 # to have separate packages for:
294 #
295 #       python-scons    only the build engine
296 #       scons-script    only the script
297 #       scons           the script plus the build engine
298 #
299 # We're now only delivering a single "scons" package, but this is still
300 # "built" as two sub-packages (the build engine and the script), so
301 # the definitions remain here, even though we're not using them for
302 # separate packages.
303 #
304
305 python_scons = {
306         'pkg'           : 'python-' + project,
307         'src_subdir'    : 'engine',
308         'inst_subdir'   : os.path.join('lib', 'python1.5', 'site-packages'),
309
310         'debian_deps'   : [
311                             'debian/changelog',
312                             'debian/control',
313                             'debian/copyright',
314                             'debian/dirs',
315                             'debian/docs',
316                             'debian/postinst',
317                             'debian/prerm',
318                             'debian/rules',
319                           ],
320
321         'files'         : [ 'LICENSE.txt',
322                             'README.txt',
323                             'setup.cfg',
324                             'setup.py',
325                           ],
326
327         'filemap'       : {
328                             'LICENSE.txt' : '../LICENSE.txt'
329                           },
330 }
331
332 #
333 # The original packaging scheme would have have required us to push
334 # the Python version number into the package name (python1.5-scons,
335 # python2.0-scons, etc.), which would have required a definition
336 # like the following.  Leave this here in case we ever decide to do
337 # this in the future, but note that this would require some modification
338 # to src/engine/setup.py before it would really work.
339 #
340 #python2_scons = {
341 #        'pkg'          : 'python2-' + project,
342 #        'src_subdir'   : 'engine',
343 #        'inst_subdir'  : os.path.join('lib', 'python2.1', 'site-packages'),
344 #
345 #        'debian_deps'  : [
346 #                            'debian/changelog',
347 #                            'debian/control',
348 #                            'debian/copyright',
349 #                            'debian/dirs',
350 #                            'debian/docs',
351 #                            'debian/postinst',
352 #                            'debian/prerm',
353 #                            'debian/rules',
354 #                          ],
355 #
356 #        'files'        : [
357 #                            'LICENSE.txt',
358 #                            'README.txt',
359 #                            'setup.cfg',
360 #                            'setup.py',
361 #                          ],
362 #        'filemap'      : {
363 #                            'LICENSE.txt' : '../LICENSE.txt',
364 #                          },
365 #}
366 #
367
368 scons_script = {
369         'pkg'           : project + '-script',
370         'src_subdir'    : 'script',
371         'inst_subdir'   : 'bin',
372
373         'debian_deps'   : [
374                             'debian/changelog',
375                             'debian/control',
376                             'debian/copyright',
377                             'debian/dirs',
378                             'debian/docs',
379                             'debian/postinst',
380                             'debian/prerm',
381                             'debian/rules',
382                           ],
383
384         'files'         : [
385                             'LICENSE.txt',
386                             'README.txt',
387                             'setup.cfg',
388                             'setup.py',
389                           ],
390
391         'filemap'       : {
392                             'LICENSE.txt' : '../LICENSE.txt',
393                             'scons'       : 'scons.py',
394                            }
395 }
396
397 scons = {
398         'pkg'           : project,
399
400         'debian_deps'   : [ 
401                             'debian/changelog',
402                             'debian/control',
403                             'debian/copyright',
404                             'debian/dirs',
405                             'debian/docs',
406                             'debian/postinst',
407                             'debian/prerm',
408                             'debian/rules',
409                           ],
410
411         'files'         : [ 
412                             'CHANGES.txt',
413                             'LICENSE.txt',
414                             'README.txt',
415                             'RELEASE.txt',
416                             'os_spawnv_fix.diff',
417                             'scons.1',
418                             'script/scons.bat',
419                             'setup.cfg',
420                             'setup.py',
421                           ],
422
423         'filemap'       : {
424                             'scons.1' : '../doc/man/scons.1',
425                           },
426
427         'subpkgs'       : [ python_scons, scons_script ],
428
429         'subinst_dirs'  : {
430                              'python-' + project : python_project_subinst_dir,
431                              project + '-script' : project_script_subinst_dir,
432                            },
433 }
434
435 src_deps = []
436 src_files = []
437
438 for p in [ scons ]:
439     #
440     # Initialize variables with the right directories for this package.
441     #
442     pkg = p['pkg']
443     pkg_version = "%s-%s" % (pkg, version)
444
445     src = 'src'
446     if p.has_key('src_subdir'):
447         src = os.path.join(src, p['src_subdir'])
448
449     build = os.path.join('build', pkg)
450
451     tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
452     platform_tar_gz = os.path.join(build,
453                                    'dist',
454                                    "%s.%s.tar.gz" % (pkg_version, platform))
455     zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
456     platform_zip = os.path.join(build,
457                                 'dist',
458                                 "%s.%s.zip" % (pkg_version, platform))
459     win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
460
461     #
462     # Update the environment with the relevant information
463     # for this package.
464     #
465     # We can get away with calling setup.py using a directory path
466     # like this because we put a preamble in it that will chdir()
467     # to the directory in which setup.py exists.
468     #
469     setup_py = os.path.join(build, 'setup.py')
470     env.Update(PKG = pkg,
471                PKG_VERSION = pkg_version,
472                SETUP_PY = setup_py)
473     Local(setup_py)
474
475     #
476     # Read up the list of source files from our MANIFEST.in.
477     # This list should *not* include LICENSE.txt, MANIFEST,
478     # README.txt, or setup.py.  Make a copy of the list for the
479     # destination files.
480     #
481     manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
482     src_files = map(lambda x: x[:-1],
483                     open(manifest_in).readlines())
484     raw_files = src_files[:]
485     dst_files = src_files[:]
486
487     MANIFEST_in_list = []
488
489     if p.has_key('subpkgs'):
490         #
491         # This package includes some sub-packages.  Read up their
492         # MANIFEST.in files, and add them to our source and destination
493         # file lists, modifying them as appropriate to add the
494         # specified subdirs.
495         #
496         for sp in p['subpkgs']:
497             ssubdir = sp['src_subdir']
498             isubdir = p['subinst_dirs'][sp['pkg']]
499             MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
500             MANIFEST_in_list.append(MANIFEST_in)
501             f = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
502             raw_files.extend(f)
503             src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), f))
504             if isubdir:
505                 f = map(lambda x, i=isubdir: os.path.join(i, x), f)
506             dst_files.extend(f)
507             for k in sp['filemap'].keys():
508                 f = sp['filemap'][k]
509                 if f:
510                     k = os.path.join(sp['src_subdir'], k)
511                     p['filemap'][k] = os.path.join(sp['src_subdir'], f)
512
513     #
514     # Now that we have the "normal" source files, add those files
515     # that are standard for each distribution.  Note that we don't
516     # add these to dst_files, because they don't get installed.
517     # And we still have the MANIFEST to add.
518     #
519     src_files.extend(p['files'])
520
521     #
522     # Now run everything in src_file through the sed command we
523     # concocted to expand __FILE__, __VERSION__, etc.
524     #
525     for b in src_files:
526         s = p['filemap'].get(b, b)
527         env.SCons_revision(os.path.join(build, b), os.path.join(src, s))
528
529     #
530     # NOW, finally, we can create the MANIFEST, which we do
531     # by having Python spit out the contents of the src_files
532     # array we've carefully created.  After we've added
533     # MANIFEST itself to the array, of course.
534     #
535     src_files.append("MANIFEST")
536     MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
537
538     def write_src_files(target, source, **kw):
539         global src_files
540         src_files.sort()
541         f = open(str(target[0]), 'wb')
542         for file in src_files:
543             f.write(file + "\n")
544         f.close()
545         return 0
546     env.Command(os.path.join(build, 'MANIFEST'),
547                 MANIFEST_in_list,
548                 write_src_files)
549
550     #
551     # Now go through and arrange to create whatever packages we can.
552     #
553     build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
554     apply(Local, build_src_files, {})
555
556     distutils_formats = []
557
558     distutils_targets = [ win32_exe ]
559
560     install_targets = distutils_targets[:]
561
562     if gzip:
563
564         distutils_formats.append('gztar')
565
566         src_deps.append(tar_gz)
567
568         distutils_targets.extend([ tar_gz, platform_tar_gz ])
569         install_targets.extend([ tar_gz, platform_tar_gz ])
570
571         #
572         # Unpack the tar.gz archive created by the distutils into
573         # build/unpack-tar-gz/scons-{version}.
574         #
575         # We'd like to replace the last three lines with the following:
576         #
577         #       tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
578         #
579         # but that gives heartburn to Cygwin's tar, so work around it
580         # with separate zcat-tar-rm commands.
581         #
582         unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
583                                          os.path.join(u, pv, x),
584                                   src_files)
585         env.Command(unpack_tar_gz_files, tar_gz, [
586                     "rm -rf %s" % os.path.join(unpack_tar_gz_dir, pkg_version),
587                     "$ZCAT $SOURCES > .temp",
588                     "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
589                     "rm -f .temp",
590         ])
591     
592         #
593         # Run setup.py in the unpacked subdirectory to "install" everything
594         # into our build/test subdirectory.  The runtest.py script will set
595         # PYTHONPATH so that the tests only look under build/test-{package},
596         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
597         # and unittest.py).  This makes sure that our tests pass with what
598         # we really packaged, not because of something hanging around in
599         # the development directory.
600         #
601         # We can get away with calling setup.py using a directory path
602         # like this because we put a preamble in it that will chdir()
603         # to the directory in which setup.py exists.
604         #
605         dfiles = map(lambda x, d=test_tar_gz_dir: os.path.join(d, x), dst_files)
606         env.Command(dfiles, unpack_tar_gz_files, [
607             "rm -rf %s" % os.path.join(unpack_tar_gz_dir, pkg_version, 'build'),
608             "rm -rf $TEST_TAR_GZ_DIR",
609             "$PYTHON %s install --prefix=$TEST_TAR_GZ_DIR" % \
610                 os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
611         ])
612
613     if zipit:
614
615         distutils_formats.append('zip')
616
617         src_deps.append(zip)
618
619         distutils_targets.extend([ zip, platform_zip ])
620         install_targets.extend([ zip, platform_zip ])
621
622         #
623         # Unpack the zip archive created by the distutils into
624         # build/unpack-zip/scons-{version}.
625         #
626         unpack_zip_files = map(lambda x, u=unpack_zip_dir, pv=pkg_version:
627                                       os.path.join(u, pv, x),
628                                src_files)
629
630         env.Command(unpack_zip_files, zip, unzipit)
631
632         #
633         # Run setup.py in the unpacked subdirectory to "install" everything
634         # into our build/test subdirectory.  The runtest.py script will set
635         # PYTHONPATH so that the tests only look under build/test-{package},
636         # and under etc (for the testing modules TestCmd.py, TestSCons.py,
637         # and unittest.py).  This makes sure that our tests pass with what
638         # we really packaged, not because of something hanging around in
639         # the development directory.
640         #
641         # We can get away with calling setup.py using a directory path
642         # like this because we put a preamble in it that will chdir()
643         # to the directory in which setup.py exists.
644         #
645         dfiles = map(lambda x, d=test_zip_dir: os.path.join(d, x), dst_files)
646         env.Command(dfiles, unpack_zip_files, [
647             "rm -rf %s" % os.path.join(unpack_zip_dir, pkg_version, 'build'),
648             "rm -rf $TEST_ZIP_DIR",
649             "$PYTHON %s install --prefix=$TEST_ZIP_DIR" % \
650                 os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
651         ])
652
653     if rpmbuild:
654         topdir = os.path.join(os.getcwd(), build, 'build',
655                               'bdist.' + platform, 'rpm')
656
657         BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
658         RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
659         SOURCESdir = os.path.join(topdir, 'SOURCES')
660         SPECSdir = os.path.join(topdir, 'SPECS')
661         SRPMSdir = os.path.join(topdir, 'SRPMS')
662
663         specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
664         sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version);
665         noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
666         src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
667
668         env.InstallAs(specfile, os.path.join('rpm', "%s.spec" % pkg))
669         env.InstallAs(sourcefile, tar_gz)
670
671         targets = [ noarch_rpm, src_rpm ]
672         cmd = "$RPMBUILD --define '_topdir $(%s$)' -ba $SOURCES" % topdir
673         if not os.path.isdir(BUILDdir):
674             cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
675         env.Command(targets, specfile, cmd)
676         env.Depends(targets, sourcefile)
677
678         install_targets.extend(targets)
679
680         dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
681                      dst_files)
682         env.Command(dfiles,
683                     noarch_rpm,
684                     "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
685
686     if dh_builddeb and fakeroot:
687         # Our Debian packaging builds directly into build/dist,
688         # so we don't need to add the .debs to install_targets.
689         deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version))
690         for d in p['debian_deps']:
691             b = env.SCons_revision(os.path.join(build, d), d)
692             env.Depends(deb, b)
693             Local(b)
694         env.Command(deb, build_src_files, [
695             "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build,
696                     ])
697
698         old = os.path.join('lib', 'scons', '')
699         new = os.path.join('lib', 'python2.1', 'site-packages', '')
700         def xxx(s, old=old, new=new):
701             if s[:len(old)] == old:
702                 s = new + s[len(old):]
703             return os.path.join('usr', s)
704         dfiles = map(lambda x, t=test_deb_dir: os.path.join(t, x),
705                      map(xxx, dst_files))
706         env.Command(dfiles,
707                     deb,
708                     "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)")
709
710     #
711     # Use the Python distutils to generate the appropriate packages.
712     #
713     commands = [
714         "rm -rf %s" % os.path.join(build, 'build', 'lib'),
715         "rm -rf %s" % os.path.join(build, 'build', 'scripts'),
716     ]
717
718     if distutils_formats:
719         commands.append("rm -rf %s" % os.path.join(build,
720                                                    'build',
721                                                    'bdist.' + platform,
722                                                    'dumb'))
723         for format in distutils_formats:
724             commands.append("$PYTHON $SETUP_PY bdist_dumb -f %s" % format)
725
726         commands.append("$PYTHON $SETUP_PY sdist --formats=%s" %  \
727                             string.join(distutils_formats, ','))
728
729     commands.append("$PYTHON $SETUP_PY bdist_wininst")
730
731     env.Command(distutils_targets, build_src_files, commands)
732
733     #
734     # Now create local packages for people who want to let people
735     # build their SCons-buildable packages without having to
736     # install SCons.
737     #
738     s_l_v = '%s-local-%s' % (pkg, version)
739
740     local = os.path.join('build', pkg + '-local')
741     cwd_local = os.path.join(os.getcwd(), local)
742     cwd_local_slv = os.path.join(os.getcwd(), local, s_l_v)
743
744     local_tar_gz = os.path.join('build', 'dist', "%s.tar.gz" % s_l_v)
745     local_zip = os.path.join('build', 'dist', "%s.zip" % s_l_v)
746
747     commands = [
748         "rm -rf %s" % local,
749         "$PYTHON $SETUP_PY install --install-script=%s --install-lib=%s --no-compile" % \
750                                                 (cwd_local, cwd_local_slv),
751         "mv %s/scons %s/scons.py" % (local, local),
752     ]
753
754     rf = filter(lambda x: x != "scons", raw_files)
755     rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
756     rf.append("scons.py")
757     local_targets = map(lambda x, s=local: os.path.join(s, x), rf)
758
759     env.Command(local_targets, build_src_files, commands)
760
761     scons_LICENSE = os.path.join(local, 'scons-LICENSE')
762     env.SCons_revision(scons_LICENSE, 'LICENSE-local')
763     local_targets.append(scons_LICENSE)
764
765     scons_README = os.path.join(local, 'scons-README')
766     env.SCons_revision(scons_README, 'README-local')
767     local_targets.append(scons_README)
768
769     if gzip:
770         env.Command(local_tar_gz,
771                     local_targets,
772                     "cd %s && tar czf $( ${TARGET.abspath} $) *" % local)
773
774         unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
775                                     os.path.join(d, x),
776                              rf)
777         commands = ["rm -rf %s" % test_local_tar_gz_dir,
778                     "mkdir %s" % test_local_tar_gz_dir,
779                     "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
780
781         env.Command(unpack_targets, local_tar_gz, commands)
782
783     if zipit:
784         zipenv = env.Copy(CD = local, PSV = '.')
785         zipenv.Command(local_zip, local_targets, zipit)
786
787         unpack_targets = map(lambda x, d=test_local_zip_dir:
788                                     os.path.join(d, x),
789                              rf)
790         commands = ["rm -rf %s" % test_local_zip_dir,
791                     "mkdir %s" % test_local_zip_dir,
792                     unzipit]
793
794         zipenv = env.Copy(UNPACK_ZIP_DIR = test_local_zip_dir)
795         zipenv.Command(unpack_targets, local_zip, unzipit)
796
797     #
798     # And, lastly, install the appropriate packages in the
799     # appropriate subdirectory.
800     #
801     b_d_files = env.Install(os.path.join('build', 'dist'), install_targets)
802     Local(b_d_files)
803
804 #
805 #
806 #
807 Export('env')
808
809 SConscript('etc/SConscript')
810
811 #
812 # Documentation.
813 #
814 BuildDir('build/doc', 'doc')
815
816 Export('env', 'whereis')
817
818 SConscript('build/doc/SConscript')
819
820 #
821 # If we're running in the actual Aegis project, pack up a complete
822 # source archive from the project files and files in the change,
823 # so we can share it with helpful developers who don't use Aegis.
824 #
825
826 if change:
827     df = []
828     cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change
829     for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()):
830         a = string.split(line)
831         if a[1] == "remove":
832             df.append(a[-1])
833
834     cmd = "aegis -list -terse pf 2>/dev/null"
835     pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
836     cmd = "aegis -list -terse cf 2>/dev/null"
837     cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
838     u = {}
839     for f in pf + cf:
840         u[f] = 1
841     for f in df:
842         del u[f]
843     sfiles = filter(lambda x: x[-9:] != '.aeignore' and x[-9:] != '.sconsign',
844                     u.keys())
845
846     if sfiles:
847         ps = "%s-src" % project
848         psv = "%s-%s" % (ps, version)
849         b_ps = os.path.join('build', ps)
850         b_psv = os.path.join('build', psv)
851         b_psv_stamp = b_psv + '-stamp'
852
853         src_tar_gz = os.path.join('build', 'dist', '%s.tar.gz' % psv)
854         src_zip = os.path.join('build', 'dist', '%s.zip' % psv)
855
856         Local(src_tar_gz, src_zip)
857
858         for file in sfiles:
859             env.SCons_revision(os.path.join(b_ps, file), file)
860
861         b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
862         cmds = [
863             "rm -rf %s" % b_psv,
864             "cp -rp %s %s" % (b_ps, b_psv),
865             "find %s -name .sconsign -exec rm {} \\;" % b_psv,
866             "touch $TARGET",
867         ]
868
869         env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)
870
871         apply(Local, b_ps_files, {})
872
873         if gzip:
874
875             env.Command(src_tar_gz, b_psv_stamp,
876                         "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)
877     
878             #
879             # Unpack the archive into build/unpack/scons-{version}.
880             #
881             unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, psv=psv:
882                                              os.path.join(u, psv, x),
883                                       sfiles)
884     
885             #
886             # We'd like to replace the last three lines with the following:
887             #
888             #   tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
889             #
890             # but that gives heartburn to Cygwin's tar, so work around it
891             # with separate zcat-tar-rm commands.
892             env.Command(unpack_tar_gz_files, src_tar_gz, [
893                 "rm -rf %s" % os.path.join(unpack_tar_gz_dir, psv),
894                 "$ZCAT $SOURCES > .temp",
895                 "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
896                 "rm -f .temp",
897             ])
898     
899             #
900             # Run setup.py in the unpacked subdirectory to "install" everything
901             # into our build/test subdirectory.  The runtest.py script will set
902             # PYTHONPATH so that the tests only look under build/test-{package},
903             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
904             # and unittest.py).  This makes sure that our tests pass with what
905             # we really packaged, not because of something hanging around in
906             # the development directory.
907             #
908             # We can get away with calling setup.py using a directory path
909             # like this because we put a preamble in it that will chdir()
910             # to the directory in which setup.py exists.
911             #
912             dfiles = map(lambda x, d=test_src_tar_gz_dir: os.path.join(d, x),
913                             dst_files)
914             ENV = env.Dictionary('ENV')
915             ENV['SCONS_LIB_DIR'] = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
916             ENV['USERNAME'] = developer
917             env.Copy(ENV = ENV).Command(dfiles, unpack_tar_gz_files, [
918                 "rm -rf %s" % os.path.join(unpack_tar_gz_dir,
919                                            psv,
920                                            'build',
921                                            'scons',
922                                            'build'),
923                 "rm -rf $TEST_SRC_TAR_GZ_DIR",
924                 "cd %s && $PYTHON %s %s" % \
925                     (os.path.join(unpack_tar_gz_dir, psv),
926                      os.path.join('src', 'script', 'scons.py'),
927                      os.path.join('build', 'scons')),
928                 "$PYTHON %s install --prefix=$TEST_SRC_TAR_GZ_DIR" % \
929                     os.path.join(unpack_tar_gz_dir,
930                                  psv,
931                                  'build',
932                                  'scons',
933                                  'setup.py'),
934             ])
935
936         if zipit:
937
938             zipenv = env.Copy(CD = 'build', PSV = psv)
939             zipenv.Command(src_zip, b_psv_stamp, zipit)
940     
941             #
942             # Unpack the archive into build/unpack/scons-{version}.
943             #
944             unpack_zip_files = map(lambda x, u=unpack_zip_dir, psv=psv:
945                                              os.path.join(u, psv, x),
946                                       sfiles)
947     
948             env.Command(unpack_zip_files, src_zip, unzipit)
949     
950             #
951             # Run setup.py in the unpacked subdirectory to "install" everything
952             # into our build/test subdirectory.  The runtest.py script will set
953             # PYTHONPATH so that the tests only look under build/test-{package},
954             # and under etc (for the testing modules TestCmd.py, TestSCons.py,
955             # and unittest.py).  This makes sure that our tests pass with what
956             # we really packaged, not because of something hanging around in
957             # the development directory.
958             #
959             # We can get away with calling setup.py using a directory path
960             # like this because we put a preamble in it that will chdir()
961             # to the directory in which setup.py exists.
962             #
963             dfiles = map(lambda x, d=test_src_zip_dir: os.path.join(d, x),
964                             dst_files)
965             ENV = env.Dictionary('ENV')
966             ENV['SCONS_LIB_DIR'] = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
967             ENV['USERNAME'] = developer
968             env.Copy(ENV = ENV).Command(dfiles, unpack_zip_files, [
969                 "rm -rf %s" % os.path.join(unpack_zip_dir,
970                                            psv,
971                                            'build',
972                                            'scons',
973                                            'build'),
974                 "rm -rf $TEST_SRC_ZIP_DIR",
975                 "cd %s && $PYTHON %s %s" % \
976                     (os.path.join(unpack_zip_dir, psv),
977                      os.path.join('src', 'script', 'scons.py'),
978                      os.path.join('build', 'scons')),
979                 "$PYTHON %s install --prefix=$TEST_SRC_ZIP_DIR" % \
980                     os.path.join(unpack_zip_dir,
981                                  psv,
982                                  'build',
983                                  'scons',
984                                  'setup.py'),
985             ])