diff options
Diffstat (limited to 'SConstruct')
-rw-r--r-- | SConstruct | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..67d3e64 --- /dev/null +++ b/SConstruct @@ -0,0 +1,597 @@ +# +# SConstruct file to build scons during development. +# +# THIS IS NOT READY YET. DO NOT TRY TO BUILD SCons WITH ITSELF YET. +# + +# +# Copyright (c) 2001 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import distutils.util +import os +import os.path +import stat +import string +import sys +import time + +project = 'scons' + +#Default('.') # XXX Uncomment this when we're really ready + +# +# An internal "whereis" routine to figure out if we have a +# given program available. Put it in the "cons::" package +# so subsidiary Conscript files can get at it easily, too. +# + +def whereis(file): + for dir in string.split(os.environ['PATH'], os.pathsep): + f = os.path.join(dir, file) + try: + st = os.stat(f) + except: + continue + if stat.S_IMODE(st[stat.ST_MODE]) & 0111: + return f + return None + +# +# We let the presence or absence of various utilities determine +# whether or not we bother to build certain pieces of things. +# This will allow people to still do SCons work even if they +# don't have Aegis or RPM installed, for example. +# +aegis = whereis('aegis') +aesub = whereis('aesub') +rpm = whereis('rpm') +dh_builddeb = whereis('dh_builddeb') +fakeroot = whereis('fakeroot') + +# My installation on Red Hat doesn't like any debhelper version +# beyond 2, so let's use 2 as the default on any non-Debian build. +if os.path.isfile('/etc/debian_version'): + dh_compat = 3 +else: + dh_compat = 2 + +# +ARG = {} # XXX Remove this when we support command-line arguments +# +# Now grab the information that we "build" into the files (using sed). +# +try: + date = ARG['date'] +except: + date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time())) + +if ARG.has_key('developer'): + developer = ARG['developer'] +elif os.environ.has_key('USERNAME'): + developer = os.environ['USERNAME'] +elif os.environ.has_key('LOGNAME'): + developer = os.environ['LOGNAME'] +elif os.environ.has_key('USER'): + developer = os.environ['USER'] + +try: + revision = ARG['version'] +except: + if aesub: + revision = os.popen(aesub + " \\$version", "r").read()[:-1] + else: + revision = '0.04' + +a = string.split(revision, '.') +arr = [a[0]] +for s in a[1:]: + if len(s) == 1: + s = '0' + s + arr.append(s) +revision = string.join(arr, '.') + +# Here's how we'd turn the calculated $revision into our package $version. +# This makes it difficult to coordinate with other files (debian/changelog +# and rpm/scons.spec) that hard-code the version number, so just go with +# the flow for now and hard code it here, too. +#if len(arr) >= 2: +# arr = arr[:-1] +#def xxx(str): +# if str[0] == 'C' or str[0] == 'D': +# str = str[1:] +# while len(str) > 2 and str[0] == '0': +# str = str[1:] +# return str +#arr = map(lambda x, xxx=xxx: xxx(x), arr) +#version = string.join(arr, '.') +version = '0.04' + +try: + change = ARG['change'] +except: + if aesub: + change = os.popen(aesub + " \\$change", "r").read()[:-1] + else: + change = '0.04' + +python_ver = sys.version[0:3] + +platform = distutils.util.get_platform() + +if platform == "win32": + archsuffix = "zip" +else: + archsuffix = "tar.gz" + +test1_dir = os.path.join(os.getcwd(), "build", "test1") +test2_dir = os.path.join(os.getcwd(), "build", "test2") + +lib_project = os.path.join("lib", project) + +# Originally, we were going to package the build engine in a +# private SCons library that contained the version number, so +# we could easily have multiple side-by-side versions of SCons +# installed. Keep this around in case we ever want to go back +# to that scheme. Note that this also requires changes to +# runtest.py and src/setup.py. +#lib_project = os.path.join("lib", project + '-' + version) + +test1_lib_dir = os.path.join(test1_dir, lib_project) + +test2_lib_dir = os.path.join(test2_dir, + "lib", + "python" + python_ver, + "site-packages") + +unpack_dir = os.path.join(os.getcwd(), "build", "unpack") + +env = Environment( + ENV = { + 'AEGIS_PROJECT' : os.environ['AEGIS_PROJECT'], + 'PATH' : os.environ['PATH'], + }, + TEST1_LIB_DIR = test1_lib_dir, + TEST2_LIB_DIR = test2_lib_dir, + + DATE = date, + DEVELOPER = developer, + REVISION = revision, + VERSION = version, + DH_COMPAT = dh_compat, + + SED = 'sed', + SEDFLAGS = "$( -e 's+__DATE__+$DATE+' $)" + \ + " -e 's+__DEVELOPER__+$DEVELOPER+'" + \ + " -e 's+__FILE__+$SOURCES'+" + \ + " -e 's+__REVISION__+$REVISION'+" + \ + " -e 's+__VERSION__+$VERSION'+", + SEDCOM = '$SED $SEDFLAGS $SOURCES > $TARGET', + ) + +# +# Define SCons packages. +# +# In the original, more complicated packaging scheme, we were going +# to have separate packages for: +# +# python-scons only the build engine +# scons-script only the script +# scons the script plus the build engine +# +# We're now only delivering a single "scons" package, but this is still +# "built" as two sub-packages (the build engine and the script), so +# the definitions remain here, even though we're not using them for +# separate packages. +# + +python_scons = { + 'pkg' : 'python-' + project, + 'src_subdir' : 'engine', + 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'), + 'prefix' : test2_dir, + + 'debian_deps' : [ 'debian/rules debian/control', + 'debian/changelog debian/copyright', + 'debian/python-scons.postinst', + 'debian/python-scons.prerm', + ], + + 'files' : [ 'LICENSE.txt', + 'README.txt', + 'setup.cfg', + 'setup.py', + ], + + 'filemap' : { + 'LICENSE.txt' : '../LICENSE.txt' + }, +} + +# +# The original packaging scheme would have have required us to push +# the Python version number into the package name (python1.5-scons, +# python2.0-scons, etc.), which would have required a definition +# like the following. Leave this here in case we ever decide to do +# this in the future, but note that this would require some modification +# to src/engine/setup.py before it would really work. +# +#python2_scons = { +# 'pkg' : 'python2-' + project, +# 'src_subdir' : 'engine', +# 'inst_subdir' : os.path.join('lib', 'python2.1', 'site-packages'), +# 'prefix' : test2_dir, +# +# 'debian_deps' : [ 'debian/rules debian/control', +# 'debian/changelog debian/copyright', +# 'debian/python2-scons.postinst', +# 'debian/python2-scons.prerm', +# ], +# +# 'files' : [ +# 'LICENSE.txt', +# 'README.txt', +# 'setup.cfg', +# 'setup.py', +# ], +# 'filemap' : { +# 'LICENSE.txt' : '../LICENSE.txt', +# }, +#} +# + +scons_script = { + 'pkg' : project + '-script', + 'src_subdir' : 'script', + 'inst_subdir' : 'bin', + 'prefix' : test2_dir, + + 'debian_deps' : [ 'debian/rules debian/control', + 'debian/changelog debian/copyright', + 'debian/python-scons.postinst', + 'debian/python-scons.prerm', + ], + + 'files' : [ + 'LICENSE.txt', + 'README.txt', + 'setup.cfg', + 'setup.py', + ], + + 'filemap' : { + 'LICENSE.txt' : '../LICENSE.txt', + 'scons' : 'scons.py', + } +} + +scons = { + 'pkg' : project, + #'inst_subdir' : None, + 'prefix' : test1_dir, + + 'debian_deps' : [ + 'debian/rules debian/control', + 'debian/changelog debian/copyright', + 'debian/scons.postinst', + 'debian/scons.prerm', + ], + + 'files' : [ + 'CHANGES.txt', + 'LICENSE.txt', + 'README.txt', + 'RELEASE.txt', + 'os_spawnv_fix.diff', + 'scons.1', + 'script/scons.bat', + 'setup.cfg', + 'setup.py', + ], + + 'filemap' : { + 'scons.1' : '../doc/man/scons.1', + }, + + 'subpkgs' : [ python_scons, scons_script ], + + 'subinst_dirs' : { + 'python-' + project : lib_project, + project + '-script' : 'bin', + }, +} + +src_deps = [] + +for p in [ scons ]: + # + # Initialize variables with the right directories for this package. + # + pkg = p['pkg'] + + src = 'src' + try: + src = os.path.join(src, p['src_subdir']) + except KeyError: + pass + + build = os.path.join('build', pkg) + + prefix = p['prefix'] + install = prefix + try: + install = os.path.join(install, p['inst_subdir']) + except KeyError: + pass + + # + # Read up the list of source files from our MANIFEST.in. + # This list should *not* include LICENSE.txt, MANIFEST, + # README.txt, or setup.py. Make a copy of the list for the + # destination files. + # + src_files = map(lambda x: x[:-1], + open(os.path.join(src, 'MANIFEST.in')).readlines()) + dst_files = map(lambda x: os.path.join(install, x), src_files) + + if p.has_key('subpkgs'): + # + # This package includes some sub-packages. Read up their + # MANIFEST.in files, and add them to our source and destination + # file lists, modifying them as appropriate to add the + # specified subdirs. + # + for sp in p['subpkgs']: + ssubdir = sp['src_subdir'] + isubdir = p['subinst_dirs'][sp['pkg']] + f = map(lambda x: x[:-1], + open(os.path.join(src, ssubdir, 'MANIFEST.in')).readlines()) + src_files.extend(map(lambda x, s=sp['src_subdir']: + os.path.join(s, x), + f)) + dst_files.extend(map(lambda x, i=install, s=isubdir: + os.path.join(i, s, x), + f)) + for k in sp['filemap'].keys(): + f = sp['filemap'][k] + if f: + k = os.path.join(sp['src_subdir'], k) + p['filemap'][k] = os.path.join(sp['src_subdir'], f) + + # + # Now that we have the "normal" source files, add those files + # that are standard for each distribution. Note that we don't + # add these to dst_files, because they don't get installed. + # And we still have the MANIFEST to add. + # + src_files.extend(p['files']) + + # + # Now run everything in src_file through the sed command we + # concocted to expand __FILE__, __VERSION__, etc. + # + for b in src_files: + try: + s = p['filemap'][b] + except KeyError: + pass + env.Command(os.path.join(build, b), + os.path.join(src, s), + "$SEDCOM") + + # + # NOW, finally, we can create the MANIFEST, which we do + # by having Perl spit out the contents of the @src_files + # array we've carefully created. After we've added + # MANIFEST itself to the array, of course. + # + src_files.append("MANIFEST") + def copy(src, dest): + open(dest, 'wb').write(open(src, 'rb').read()) + return 0 + env.Command(os.path.join(build, 'MANIFEST'), + os.path.join(src, 'MANIFEST.in'), + copy) + + # + # Use the Python distutils to generate the packages. + # + archive = os.path.join(build, + 'dist', + "%s-%s.%s" % (pkg, version, archsuffix)) + + src_deps.append(archive) + + build_targets = [ + os.path.join(build, 'dist', "%s-%s.%s.%s" % (pkg, version, platform, archsuffix)), + archive, + os.path.join(build, 'dist', "%s-%s.win32.exe" % (pkg, version)), + ] + install_targets = build_targets + + # We can get away with calling setup.py using a directory path + # like this because we put a preamble in it that will chdir() + # to the directory in which setup.py exists. + bdist_dirs = [ + os.path.join(build, 'build', 'lib'), + os.path.join(build, 'build', 'scripts'), + ] + setup_py = os.path.join(build, 'setup.py') + commands = [ + "rm -rf %s && python %s bdist" % + (string.join(map(lambda x: str(x), bdist_dirs)), setup_py), + "python %s sdist" % setup_py, + "python %s bdist_wininst" % setup_py, + ] + + if rpm: + topdir = os.path.join(os.getcwd(), build, 'build', + 'bdist.' + platform, 'rpm') + + BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version) + RPMSdir = os.path.join(topdir, 'RPMS', 'noarch') + SOURCESdir = os.path.join(topdir, 'SOURCES') + SPECSdir = os.path.join(topdir, 'SPECS') + SRPMSdir = os.path.join(topdir, 'SRPMS') + + specfile = os.path.join(SPECSdir, "%s-%s-1.spec" % (pkg, version)) + sourcefile = os.path.join(SOURCESdir, "%s-%s.%s" % (pkg, version, archsuffix)); + rpm = os.path.join(RPMSdir, "%s-%s-1.noarch.rpm" % (pkg, version)) + src_rpm = os.path.join(SRPMSdir, "%s-%s-1.src.rpm" % (pkg, version)) + + env.InstallAs(specfile, os.path.join('rpm', "%s.spec" % pkg)) + env.InstallAs(sourcefile, archive) + + targets = [ rpm, src_rpm ] + cmd = "rpm --define '_topdir %s' -ba $TARGET" % topdir + if os.path.isdir(BUILDdir): + cmd = "mkdir -p " + BUILDdir + "; " + cmd + env.Command(targets, specfile, cmd) + env.Depends(targets, sourcefile) + + install_targets.extend(targets) + + build_src_files = map(lambda x, b=build: os.path.join(b, x), src_files) + + if dh_builddeb and fakeroot: + # Debian builds directly into build/dist, so we don't + # need to add the .debs to the install_targets. + deb = os.path.join('build', 'dist', "%s_%s-1.all.deb" % (pkg, version)) + env.Command(deb, build_src_files, [ + "fakeroot make -f debian/rules VERSION=$VERSION DH_COMPAT=$DH_COMPAT ENVOKED_BY_CONSTRUCT=1 binary-%s" % pkg, + "env DH_COMPAT=$DH_COMPAT dh_clean" + ]) + env.Depends(deb, p['debian_deps']) + + + # + # Now set up creation and installation of the packages. + # + env.Command(build_targets, build_src_files, commands) + env.Install(os.path.join('build', 'dist'), install_targets) + + # + # Unpack the archive created by the distutils into build/unpack. + # + d = os.path.join(unpack_dir, "%s-%s" % (pkg, version)) + unpack_files = map(lambda x, d=d: os.path.join(d, x), src_files) + + # We'd like to replace the last three lines with the following: + # + # tar zxf %< -C $unpack_dir + # + # but that gives heartburn to Cygwin's tar, so work around it + # with separate zcat-tar-rm commands. + env.Command(unpack_files, archive, [ + "rm -rf " + os.path.join(unpack_dir, '%s-%s' % (pkg, version)), + "zcat $SOURCES > .temp", + "tar xf .temp -C %s" % unpack_dir, + "rm -f .temp", + ]) + + # + # Run setup.py in the unpacked subdirectory to "install" everything + # into our build/test subdirectory. Auxiliary modules that we need + # (TestCmd.py, TestSCons.py, unittest.py) will be copied in by + # etc/Conscript. The runtest.py script will set PYTHONPATH so that + # the tests only look under build/test. This makes sure that our + # tests pass with what we really packaged, not because of something + # hanging around in the development directory. + # + # We can get away with calling setup.py using a directory path + # like this because we put a preamble in it that will chdir() + # to the directory in which setup.py exists. + env.Command(dst_files, unpack_files, [ + "rm -rf %s" % install, + "python %s install --prefix=%s" % (os.path.join(unpack_dir, + '%s-%s' % (pkg, version), + 'setup.py'), + prefix + ), + ]) + +# +# Arrange for supporting packages to be installed in the test directories. +# +Export('env', 'whereis') + +SConscript('etc/SConscript') + +# +# Documentation. +# +BuildDir('build/doc', 'doc') + +SConscript('build/doc/SConscript'); + + +# +# If we're running in the actual Aegis project, pack up a complete +# source archive from the project files and files in the change, +# so we can share it with helpful developers who don't use Aegis. +# +# First, lie and say that we've seen any files removed by this +# change, so they don't get added to the source files list +# that goes into the archive. +# + +if change: + df = [] + cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change + for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()): + a = string.split(line) + if a[1] == "remove": + df.append(a[3]) + + cmd = "aegis -list -terse pf 2>/dev/null" + pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines()) + cmd = "aegis -list -terse cf 2>/dev/null" + cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines()) + u = {} + for f in pf + cf: + u[f] = 1 + for f in df: + del u[f] + src_files = filter(lambda x: x[-9:] != '.aeignore' and x[-7:] != '.consign', + u.keys()) + + if src_files: + ps = "%s-src" % project + psv = "%s-src-%s" % (project, version) + b_ps = os.path.join('build', ps) + b_psv = os.path.join('build', psv) + + for file in src_files: + env.Command(os.path.join(b_ps, file), file, + [ "$SEDCOM", "chmod --reference=$SOURCE $TARGET" ]) + + b_ps_files = map(lambda x, d=b_ps: os.path.join(d, x), src_files) + cmds = [ + "rm -rf %s" % b_psv, + "cp -rp %s %s" % (b_ps, b_psv), + "find %s -name .consign -exec rm {} \\;" % b_psv, + "tar zcf $TARGET -C build %s" % psv, + ] + env.Command(os.path.join('build', + 'dist', + '%s-src-%s.tar.gz' % (project, version)), + src_deps + b_ps_files, cmds) |