2 # SConstruct file to build scons packages during development.
6 # Copyright (c) 2001, 2002 Steven Knight
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:
16 # The above copyright notice and this permission notice shall be included
17 # in all copies or substantial portions of the Software.
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.
37 default_version = '0.06'
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.
48 for dir in string.split(os.environ['PATH'], os.pathsep):
49 f = os.path.join(dir, file)
55 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
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.
65 aegis = whereis('aegis')
66 aesub = whereis('aesub')
68 dh_builddeb = whereis('dh_builddeb')
69 fakeroot = whereis('fakeroot')
71 # My installation on Red Hat doesn't like any debhelper version
72 # beyond 2, so let's use 2 as the default on any non-Debian build.
73 if os.path.isfile('/etc/debian_version'):
79 # Now grab the information that we "build" into the files (using sed).
82 date = ARGUMENTS['date']
84 date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
86 if ARGUMENTS.has_key('developer'):
87 developer = ARGUMENTS['developer']
88 elif os.environ.has_key('USERNAME'):
89 developer = os.environ['USERNAME']
90 elif os.environ.has_key('LOGNAME'):
91 developer = os.environ['LOGNAME']
92 elif os.environ.has_key('USER'):
93 developer = os.environ['USER']
96 revision = ARGUMENTS['version']
99 revision = os.popen(aesub + " \\$version", "r").read()[:-1]
101 revision = default_version
103 a = string.split(revision, '.')
109 revision = string.join(arr, '.')
111 # Here's how we'd turn the calculated $revision into our package $version.
112 # This makes it difficult to coordinate with other files (debian/changelog
113 # and rpm/scons.spec) that hard-code the version number, so just go with
114 # the flow for now and hard code it here, too.
118 # if str[0] == 'C' or str[0] == 'D':
120 # while len(str) > 2 and str[0] == '0':
123 #arr = map(lambda x, xxx=xxx: xxx(x), arr)
124 #version = string.join(arr, '.')
125 version = default_version
128 change = ARGUMENTS['change']
131 change = os.popen(aesub + " \\$change", "r").read()[:-1]
133 change = default_version
135 python_ver = sys.version[0:3]
137 platform = distutils.util.get_platform()
139 if platform == "win32":
142 archsuffix = "tar.gz"
144 ENV = { 'PATH' : os.environ['PATH'] }
145 if os.environ.has_key('AEGIS_PROJECT'):
146 ENV['AEGIS_PROJECT'] = os.environ['AEGIS_PROJECT']
148 lib_project = os.path.join("lib", project)
150 unpack_dir = os.path.join(os.getcwd(), "build", "unpack")
152 test_arch_dir = os.path.join(os.getcwd(),
154 "test-%s" % string.replace(archsuffix, '.', '-'))
156 test_src_arch_dir = os.path.join(os.getcwd(),
158 "test-src-%s" % string.replace(archsuffix,
162 test_rpm_dir = os.path.join(os.getcwd(), "build", "test-rpm")
164 test_deb_dir = os.path.join(os.getcwd(), "build", "test-deb")
166 def SCons_revision(target, source, env):
167 """Interpolate specific values from the environment into a file.
169 This is used to copy files into a tree that gets packaged up
170 into the source file package.
172 # Note: We don't use $VERSION from the environment so that
173 # this routine will change when the version number changes
174 # and things will get rebuilt properly.
176 print "SCons_revision() < %s > %s" % (source[0], target)
177 inf = open(source[0], 'rb')
178 outf = open(target, 'wb')
179 for line in inf.readlines():
180 # Note: We construct the __*__ substitution strings here
181 # so that they don't get replaced when this file gets
182 # copied into the tree for packaging.
183 line = string.replace(line, '_' + '_DATE__', env['DATE'])
184 line = string.replace(line, '_' + '_DEVELOPER__', env['DEVELOPER'])
185 line = string.replace(line, '_' + '_FILE__', source[0])
186 line = string.replace(line, '_' + '_REVISION__', env['REVISION'])
187 line = string.replace(line, '_' + '_VERSION__', version)
191 os.chmod(target, os.stat(source[0])[0])
193 revbuilder = Builder(name = 'SCons_revision', action = SCons_revision)
199 DEVELOPER = developer,
202 DH_COMPAT = dh_compat,
204 BUILDERS = [ revbuilder ],
208 # Define SCons packages.
210 # In the original, more complicated packaging scheme, we were going
211 # to have separate packages for:
213 # python-scons only the build engine
214 # scons-script only the script
215 # scons the script plus the build engine
217 # We're now only delivering a single "scons" package, but this is still
218 # "built" as two sub-packages (the build engine and the script), so
219 # the definitions remain here, even though we're not using them for
224 'pkg' : 'python-' + project,
225 'src_subdir' : 'engine',
226 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'),
233 'debian/python-scons.postinst',
234 'debian/python-scons.prerm',
237 'files' : [ 'LICENSE.txt',
244 'LICENSE.txt' : '../LICENSE.txt'
249 # The original packaging scheme would have have required us to push
250 # the Python version number into the package name (python1.5-scons,
251 # python2.0-scons, etc.), which would have required a definition
252 # like the following. Leave this here in case we ever decide to do
253 # this in the future, but note that this would require some modification
254 # to src/engine/setup.py before it would really work.
257 # 'pkg' : 'python2-' + project,
258 # 'src_subdir' : 'engine',
259 # 'inst_subdir' : os.path.join('lib', 'python2.1', 'site-packages'),
264 # 'debian/changelog',
265 # 'debian/copyright',
266 # 'debian/python2-scons.postinst',
267 # 'debian/python2-scons.prerm',
277 # 'LICENSE.txt' : '../LICENSE.txt',
283 'pkg' : project + '-script',
284 'src_subdir' : 'script',
285 'inst_subdir' : 'bin',
292 'debian/python-scons.postinst',
293 'debian/python-scons.prerm',
304 'LICENSE.txt' : '../LICENSE.txt',
305 'scons' : 'scons.py',
317 'debian/scons.postinst',
318 'debian/scons.prerm',
326 'os_spawnv_fix.diff',
334 'scons.1' : '../doc/man/scons.1',
337 'subpkgs' : [ python_scons, scons_script ],
340 'python-' + project : lib_project,
341 project + '-script' : 'bin',
350 # Initialize variables with the right directories for this package.
353 pkg_version = "%s-%s" % (pkg, version)
356 if p.has_key('src_subdir'):
357 src = os.path.join(src, p['src_subdir'])
359 build = os.path.join('build', pkg)
362 # Read up the list of source files from our MANIFEST.in.
363 # This list should *not* include LICENSE.txt, MANIFEST,
364 # README.txt, or setup.py. Make a copy of the list for the
367 src_files = map(lambda x: x[:-1],
368 open(os.path.join(src, 'MANIFEST.in')).readlines())
369 dst_files = src_files[:]
371 if p.has_key('subpkgs'):
373 # This package includes some sub-packages. Read up their
374 # MANIFEST.in files, and add them to our source and destination
375 # file lists, modifying them as appropriate to add the
378 for sp in p['subpkgs']:
379 ssubdir = sp['src_subdir']
380 isubdir = p['subinst_dirs'][sp['pkg']]
381 f = map(lambda x: x[:-1],
382 open(os.path.join(src, ssubdir, 'MANIFEST.in')).readlines())
383 src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), f))
384 dst_files.extend(map(lambda x, i=isubdir: os.path.join(i, x), f))
385 for k in sp['filemap'].keys():
388 k = os.path.join(sp['src_subdir'], k)
389 p['filemap'][k] = os.path.join(sp['src_subdir'], f)
392 # Now that we have the "normal" source files, add those files
393 # that are standard for each distribution. Note that we don't
394 # add these to dst_files, because they don't get installed.
395 # And we still have the MANIFEST to add.
397 src_files.extend(p['files'])
400 # Now run everything in src_file through the sed command we
401 # concocted to expand __FILE__, __VERSION__, etc.
404 s = p['filemap'].get(b, b)
405 env.SCons_revision(os.path.join(build, b), os.path.join(src, s))
408 # NOW, finally, we can create the MANIFEST, which we do
409 # by having Python spit out the contents of the src_files
410 # array we've carefully created. After we've added
411 # MANIFEST itself to the array, of course.
413 src_files.append("MANIFEST")
414 def copy(target, source, **kw):
417 f = open(target, 'wb')
418 for file in src_files:
422 env.Command(os.path.join(build, 'MANIFEST'),
423 os.path.join(src, 'MANIFEST.in'),
427 # Use the Python distutils to generate the packages.
429 archive = os.path.join(build, 'dist', "%s.%s" % (pkg_version, archsuffix))
430 platform_archive = os.path.join(build,
432 "%s.%s.%s" % (pkg_version,
435 win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version)
437 src_deps.append(archive)
439 build_targets = [ platform_archive, archive, win32_exe ]
440 install_targets = build_targets[:]
442 # We can get away with calling setup.py using a directory path
443 # like this because we put a preamble in it that will chdir()
444 # to the directory in which setup.py exists.
446 os.path.join(build, 'build', 'lib'),
447 os.path.join(build, 'build', 'scripts'),
449 setup_py = os.path.join(build, 'setup.py')
451 "rm -rf %s && python %s bdist" % (string.join(bdist_dirs), setup_py),
452 "python %s sdist" % setup_py,
453 "python %s bdist_wininst" % setup_py,
457 topdir = os.path.join(os.getcwd(), build, 'build',
458 'bdist.' + platform, 'rpm')
460 BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
461 RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
462 SOURCESdir = os.path.join(topdir, 'SOURCES')
463 SPECSdir = os.path.join(topdir, 'SPECS')
464 SRPMSdir = os.path.join(topdir, 'SRPMS')
466 specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version)
467 sourcefile = os.path.join(SOURCESdir,
468 "%s.%s" % (pkg_version, archsuffix));
469 rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version)
470 src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version)
472 env.InstallAs(specfile, os.path.join('rpm', "%s.spec" % pkg))
473 env.InstallAs(sourcefile, archive)
475 targets = [ rpm, src_rpm ]
476 cmd = "rpm --define '_topdir $(%s$)' -ba $SOURCES" % topdir
477 if not os.path.isdir(BUILDdir):
478 cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
479 env.Command(targets, specfile, cmd)
480 env.Depends(targets, sourcefile)
482 install_targets.extend(targets)
484 dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
488 "rpm2cpio $SOURCES | (cd %s && cpio -id)" % test_rpm_dir)
490 build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files)
492 if dh_builddeb and fakeroot:
493 # Debian builds directly into build/dist, so we don't
494 # need to add the .debs to the install_targets.
495 deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version))
496 env.Command(deb, build_src_files, [
497 "fakeroot make -f debian/rules VERSION=$VERSION DH_COMPAT=$DH_COMPAT ENVOKED_BY_CONSTRUCT=1 binary-%s" % pkg,
498 "env DH_COMPAT=$DH_COMPAT dh_clean"
500 env.Depends(deb, p['debian_deps'])
502 dfiles = map(lambda x, d=test_deb_dir: os.path.join(d, 'usr', x),
506 "dpkg --fsys-tarfile $SOURCES | (cd %s && tar -xf -)" % test_deb_dir)
509 # Now set up creation and installation of the packages.
511 env.Command(build_targets, build_src_files, commands)
512 env.Install(os.path.join('build', 'dist'), install_targets)
515 # Unpack the archive created by the distutils into
516 # build/unpack/scons-{version}.
518 unpack_files = map(lambda x, u=unpack_dir, pv=pkg_version:
519 os.path.join(u, pv, x),
523 # We'd like to replace the last three lines with the following:
525 # tar zxf %< -C $unpack_dir
527 # but that gives heartburn to Cygwin's tar, so work around it
528 # with separate zcat-tar-rm commands.
529 env.Command(unpack_files, archive, [
530 "rm -rf %s" % os.path.join(unpack_dir, pkg_version),
531 "zcat $SOURCES > .temp",
532 "tar xf .temp -C %s" % unpack_dir,
537 # Run setup.py in the unpacked subdirectory to "install" everything
538 # into our build/test subdirectory. The runtest.py script will set
539 # PYTHONPATH so that the tests only look under build/test-{package},
540 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
541 # and unittest.py). This makes sure that our tests pass with what
542 # we really packaged, not because of something hanging around in
543 # the development directory.
545 # We can get away with calling setup.py using a directory path
546 # like this because we put a preamble in it that will chdir()
547 # to the directory in which setup.py exists.
548 dfiles = map(lambda x, d=test_arch_dir: os.path.join(d, x), dst_files)
549 env.Command(dfiles, unpack_files, [
550 "rm -rf %s" % os.path.join(unpack_dir, pkg_version, 'build'),
551 "rm -rf %s" % test_arch_dir,
552 "python %s install --prefix=%s" % (os.path.join(unpack_dir,
564 SConscript('etc/SConscript')
569 BuildDir('build/doc', 'doc')
571 Export('env', 'whereis')
573 SConscript('build/doc/SConscript')
576 # If we're running in the actual Aegis project, pack up a complete
577 # source archive from the project files and files in the change,
578 # so we can share it with helpful developers who don't use Aegis.
583 cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change
584 for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()):
585 a = string.split(line)
592 cmd = "aegis -list -terse pf 2>/dev/null"
593 pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
594 cmd = "aegis -list -terse cf 2>/dev/null"
595 cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
601 sfiles = filter(lambda x: x[-9:] != '.aeignore'
602 and x[-8:] != '.consign'
603 and x[-9:] != '.sconsign',
607 ps = "%s-src" % project
608 psv = "%s-%s" % (ps, version)
609 b_ps = os.path.join('build', ps)
610 b_psv = os.path.join('build', psv)
612 src_archive = os.path.join('build', 'dist', '%s.tar.gz' % psv)
615 env.SCons_revision(os.path.join(b_ps, file), file)
617 b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), sfiles)
620 "cp -rp %s %s" % (b_ps, b_psv),
621 "find %s -name .consign -exec rm {} \\;" % b_psv,
622 "find %s -name .sconsign -exec rm {} \\;" % b_psv,
623 "tar czh -f $TARGET -C build %s" % psv,
626 env.Command(src_archive, src_deps + b_ps_files, cmds)
629 # Unpack the archive created by the distutils into
630 # build/unpack/scons-{version}.
632 unpack_files = map(lambda x, u=unpack_dir, psv=psv:
633 os.path.join(u, psv, x),
637 # We'd like to replace the last three lines with the following:
639 # tar zxf %< -C $unpack_dir
641 # but that gives heartburn to Cygwin's tar, so work around it
642 # with separate zcat-tar-rm commands.
643 env.Command(unpack_files, src_archive, [
644 "rm -rf %s" % os.path.join(unpack_dir, psv),
645 "zcat $SOURCES > .temp",
646 "tar xf .temp -C %s" % unpack_dir,
651 # Run setup.py in the unpacked subdirectory to "install" everything
652 # into our build/test subdirectory. The runtest.py script will set
653 # PYTHONPATH so that the tests only look under build/test-{package},
654 # and under etc (for the testing modules TestCmd.py, TestSCons.py,
655 # and unittest.py). This makes sure that our tests pass with what
656 # we really packaged, not because of something hanging around in
657 # the development directory.
659 # We can get away with calling setup.py using a directory path
660 # like this because we put a preamble in it that will chdir()
661 # to the directory in which setup.py exists.
662 dfiles = map(lambda x, d=test_src_arch_dir: os.path.join(d, x),
664 ENV = env.Dictionary('ENV')
665 ENV['SCONS_LIB_DIR'] = os.path.join(unpack_dir, psv, 'src', 'engine')
666 ENV['USERNAME'] = developer
667 env.Copy(ENV = ENV).Command(dfiles, unpack_files, [
668 "rm -rf %s" % os.path.join(unpack_dir,
673 "rm -rf %s" % test_src_arch_dir,
674 "cd %s && python %s %s" % \
675 (os.path.join(unpack_dir, psv),
676 os.path.join('src', 'script', 'scons.py'),
677 os.path.join('build', 'scons')),
678 "python %s install --prefix=%s" % (os.path.join(unpack_dir,