# See the README file for an overview of how SCons is built and tested.
#
-# When this gets changed, you also need to change test/option-v.py
-# so it looks for the right string.
-copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007'
+# When this gets changed, you must also change the copyright_years string
+# in QMTest/TestSCons.py so the test scripts look for the right string.
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
# This gets inserted into the man pages to reflect the month of release.
-month_year = 'January 2007'
+month_year = 'September 2008'
#
# __COPYRIGHT__
#
import distutils.util
+import fnmatch
import os
import os.path
import re
-import socket
import stat
import string
import sys
-import time
+import tempfile
project = 'scons'
-default_version = '0.97'
+default_version = '1.0.1'
copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
SConsignFile()
return None
#
-# We let the presence or absence of various utilities determine
-# whether or not we bother to build certain pieces of things.
-# This should allow people to still do SCons work even if they
-# don't have Aegis or RPM installed, for example.
+# We let the presence or absence of various utilities determine whether
+# or not we bother to build certain pieces of things. This should allow
+# people to still do SCons packaging work even if they don't have all
+# of the utilities installed (e.g. RPM).
#
-aegis = whereis('aegis')
-aesub = whereis('aesub')
dh_builddeb = whereis('dh_builddeb')
fakeroot = whereis('fakeroot')
gzip = whereis('gzip')
rpmbuild = whereis('rpmbuild') or whereis('rpm')
+svn = whereis('svn')
unzip = whereis('unzip')
zip = whereis('zip')
#
# Now grab the information that we "build" into the files.
#
-try:
- date = ARGUMENTS['date']
-except:
+date = ARGUMENTS.get('DATE')
+if not date:
+ import time
date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time()))
-if ARGUMENTS.has_key('developer'):
- developer = ARGUMENTS['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']
-
-if ARGUMENTS.has_key('build_system'):
- build_system = ARGUMENTS['build_system']
-else:
+developer = ARGUMENTS.get('DEVELOPER')
+if not developer:
+ for variable in ['USERNAME', 'LOGNAME', 'USER']:
+ developer = os.environ.get(variable)
+ if developer:
+ break
+
+build_system = ARGUMENTS.get('BUILD_SYSTEM')
+if not build_system:
+ import socket
build_system = string.split(socket.gethostname(), '.')[0]
-if ARGUMENTS.has_key('version'):
- revision = ARGUMENTS['version']
-elif aesub:
- revision = os.popen(aesub + " \\$version", "r").read()[:-1]
-else:
- revision = default_version
-
-# This is old code that adds an initial "0" to revision numbers < 10.
-#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 = default_version
-
-build_id = string.replace(revision, version + '.', '')
-
-if ARGUMENTS.has_key('change'):
- change = ARGUMENTS['change']
-elif aesub:
- change = os.popen(aesub + " \\$change", "r").read()[:-1]
-else:
- change = default_version
+version = ARGUMENTS.get('VERSION', '')
+if not version:
+ version = default_version
+
+revision = ARGUMENTS.get('REVISION', '')
+if not revision and svn:
+ svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read()
+ m = re.search('Revision: (\d+)', svn_info)
+ if m:
+ revision = m.group(1)
+
+checkpoint = ARGUMENTS.get('CHECKPOINT', '')
+if checkpoint:
+ if checkpoint == 'd':
+ import time
+ checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
+ elif checkpoint == 'r':
+ checkpoint = 'r' + revision
+ version = version + '.' + checkpoint
+
+svn_status = None
+svn_status_lines = []
+
+if svn:
+ svn_status = os.popen("%s status --verbose 2> /dev/null" % svn, "r").read()
+ svn_status_lines = svn_status[:-1].split('\n')
+
+build_id = ARGUMENTS.get('BUILD_ID')
+if build_id is None:
+ if revision:
+ build_id = 'r' + revision
+ if filter(lambda l: l[0] in 'ACDMR', svn_status_lines):
+ build_id = build_id + '[MODIFIED]'
+ else:
+ build_id = ''
python_ver = sys.version[0:3]
platform = distutils.util.get_platform()
+# Re-exporting LD_LIBRARY_PATH is necessary if the Python version was
+# built with the --enable-shared option.
+
ENV = { 'PATH' : os.environ['PATH'] }
-for key in ['AEGIS_PROJECT', 'LOGNAME', 'PYTHONPATH']:
+for key in ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
if os.environ.has_key(key):
ENV[key] = os.environ[key]
if not os.path.isabs(build_dir):
build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
+command_line_variables = [
+ ("BUILDDIR=", "The directory in which to build the packages. " +
+ "The default is the './build' subdirectory."),
+
+ ("BUILD_ID=", "An identifier for the specific build." +
+ "The default is the Subversion revision number."),
+
+ ("BUILD_SYSTEM=", "The system on which the packages were built. " +
+ "The default is whatever hostname is returned " +
+ "by socket.gethostname()."),
+
+ ("CHECKPOINT=", "The specific checkpoint release being packaged, " +
+ "which will be appended to the VERSION string. " +
+ "A value of CHECKPOINT=d will generate a string " +
+ "of 'd' plus today's date in the format YYYMMDD. " +
+ "A value of CHECKPOINT=r will generate a " +
+ "string of 'r' plus the Subversion revision " +
+ "number. Any other CHECKPOINT= string will be " +
+ "used as is. There is no default value."),
+
+ ("DATE=", "The date string representing when the packaging " +
+ "build occurred. The default is the day and time " +
+ "the SConstruct file was invoked, in the format " +
+ "YYYY/MM/DD HH:MM:SS."),
+
+ ("DEVELOPER=", "The developer who created the packages. " +
+ "The default is the first set environment " +
+ "variable from the list $USERNAME, $LOGNAME, $USER."),
+
+ ("REVISION=", "The revision number of the source being built. " +
+ "The default is the Subversion revision returned " +
+ "'svn info', with an appended string of " +
+ "'[MODIFIED]' if there are any changes in the " +
+ "working copy."),
+
+ ("VERSION=", "The SCons version being packaged. The default " +
+ "is the hard-coded value '%s' " % default_version +
+ "from this SConstruct file."),
+]
+
Default('.', build_dir)
packaging_flavors = [
- 'deb',
- 'rpm',
- 'tar-gz',
- 'src-tar-gz',
- 'local-tar-gz',
- 'zip',
- 'src-zip',
- 'local-zip',
+ ('deb', "A .deb package. (This is currently not supported.)"),
+
+ ('rpm', "A RedHat Package Manager file."),
+
+ ('tar-gz', "The normal .tar.gz file for end-user installation."),
+
+ ('src-tar-gz', "A .tar.gz file containing all the source " +
+ "(including tests and documentation)."),
+
+ ('local-tar-gz', "A .tar.gz file for dropping into other software " +
+ "for local use."),
+
+ ('zip', "The normal .zip file for end-user installation."),
+
+ ('src-zip', "A .zip file containing all the source " +
+ "(including tests and documentation)."),
+
+ ('local-zip', "A .zip file for dropping into other software " +
+ "for local use."),
]
test_deb_dir = os.path.join(build_dir, "test-deb")
project_script_subinst_dir = 'bin'
+
+import textwrap
+
+indent_fmt = ' %-26s '
+
+Help("""\
+The following aliases build packages of various types, and unpack the
+contents into build/test-$PACKAGE subdirectories, which can be used by the
+runtest.py -p option to run tests against what's been actually packaged:
+
+""")
+
+aliases = packaging_flavors + [('doc', 'The SCons documentation.')]
+aliases.sort()
+
+for alias, help_text in aliases:
+ tw = textwrap.TextWrapper(
+ width = 78,
+ initial_indent = indent_fmt % alias,
+ subsequent_indent = indent_fmt % '' + ' ',
+ )
+ Help(tw.fill(help_text) + '\n')
+
+Help("""
+The following command-line variables can be set:
+
+""")
+
+for variable, help_text in command_line_variables:
+ tw = textwrap.TextWrapper(
+ width = 78,
+ initial_indent = indent_fmt % variable,
+ subsequent_indent = indent_fmt % '' + ' ',
+ )
+ Help(tw.fill(help_text) + '\n')
+
+
+
zcat = 'gzip -d -c'
#
COPYRIGHT = copyright,
DATE = date,
DEVELOPER = developer,
+ DISTDIR = os.path.join(build_dir, 'dist'),
MONTH_YEAR = month_year,
REVISION = revision,
VERSION = version,
BUILDERS = { 'SCons_revision' : revbuilder,
'SOElim' : soelimbuilder },
- PYTHON = sys.executable,
+ PYTHON = '"%s"' % sys.executable,
PYTHONFLAGS = '-tt',
)
'buildermap' : {},
+ 'extra_rpm_files' : [],
+
'explicit_deps' : {
'SCons/__init__.py' : Version_values,
},
}
+# Figure out the name of a .egg-info file that might be generated
+# as part of the RPM package. There are two complicating factors.
+#
+# First, the RPM spec file we generate will just execute "python", not
+# necessarily the one in sys.executable. If *that* version of python has
+# a distutils that knows about Python eggs, then setup.py will generate a
+# .egg-info file, so we have to execute any distutils logic in a subshell.
+#
+# Second, we can't just have the subshell check for the existence of the
+# distutils.command.install_egg_info module and generate the expected
+# file name by hand, the way we used to, because different systems can
+# have slightly different .egg-info naming conventions. (Specifically,
+# Ubuntu overrides the default behavior to remove the Python version
+# string from the .egg-info file name.) The right way to do this is to
+# actually call into the install_egg_info() class to have it generate
+# the expected name for us.
+#
+# This is all complicated enough that we do it by writing an in-line
+# script to a temporary file and then feeding it to a separate invocation
+# of "python" to tell us the actual name of the generated .egg-info file.
+
+print_egg_info_name = """
+try:
+ from distutils.dist import Distribution
+ from distutils.command.install_egg_info import install_egg_info
+except ImportError:
+ pass
+else:
+ dist = Distribution({'name' : "scons", 'version' : '%s'})
+ i = install_egg_info(dist)
+ i.finalize_options()
+ import os.path
+ print os.path.split(i.outputs[0])[1]
+""" % version
+
+try:
+ fd, tfname = tempfile.mkstemp()
+ tfp = os.fdopen(fd, "w")
+ tfp.write(print_egg_info_name)
+ tfp.close()
+ egg_info_file = os.popen("python %s" % tfname).read()[:-1]
+ if egg_info_file:
+ python_scons['extra_rpm_files'].append(egg_info_file)
+finally:
+ try:
+ os.unlink(tfname)
+ except EnvironmentError:
+ pass
+
#
# The original packaging scheme would have have required us to push
# the Python version number into the package name (python1.5-scons,
setup_py = os.path.join(build, 'setup.py')
env.Replace(PKG = pkg,
PKG_VERSION = pkg_version,
- SETUP_PY = setup_py)
+ SETUP_PY = '"%s"' % setup_py)
Local(setup_py)
#
distutils_targets = [ win32_exe ]
- install_targets = distutils_targets[:]
+ dist_distutils_targets = env.Install('$DISTDIR', distutils_targets)
+ Local(dist_distutils_targets)
+ AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0644))
- if gzip:
+ if not gzip:
+ print "gzip not found; skipping .tar.gz package for %s." % pkg
+ else:
distutils_formats.append('gztar')
src_deps.append(tar_gz)
distutils_targets.extend([ tar_gz, platform_tar_gz ])
- install_targets.extend([ tar_gz, platform_tar_gz ])
+
+ dist_tar_gz = env.Install('$DISTDIR', tar_gz)
+ dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz)
+ Local(dist_tar_gz, dist_platform_tar_gz)
+ AddPostAction(dist_tar_gz, Chmod(dist_tar_gz, 0644))
+ AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0644))
#
# Unpack the tar.gz archive created by the distutils into
unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version:
os.path.join(u, pv, x),
src_files)
- env.Command(unpack_tar_gz_files, tar_gz, [
+ env.Command(unpack_tar_gz_files, dist_tar_gz, [
Delete(os.path.join(unpack_tar_gz_dir, pkg_version)),
"$ZCAT $SOURCES > .temp",
"tar xf .temp -C $UNPACK_TAR_GZ_DIR",
bytes))
env.Command(digest, tar_gz, Digestify)
- if zipit:
+ if not zipit:
+ print "zip not found; skipping .zip package for %s." % pkg
+ else:
distutils_formats.append('zip')
src_deps.append(zip)
distutils_targets.extend([ zip, platform_zip ])
- install_targets.extend([ zip, platform_zip ])
+
+ dist_zip = env.Install('$DISTDIR', zip)
+ dist_platform_zip = env.Install('$DISTDIR', platform_zip)
+ Local(dist_zip, dist_platform_zip)
+ AddPostAction(dist_zip, Chmod(dist_zip, 0644))
+ AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0644))
#
# Unpack the zip archive created by the distutils into
os.path.join(u, pv, x),
src_files)
- env.Command(unpack_zip_files, zip, [
+ env.Command(unpack_zip_files, dist_zip, [
Delete(os.path.join(unpack_zip_dir, pkg_version)),
unzipit,
])
os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
])
- if rpmbuild:
+ if not rpmbuild:
+ msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\""
+ AlwaysBuild(Alias('rpm', [], msg))
+ else:
topdir = os.path.join(build, 'build',
'bdist.' + platform, 'rpm')
maintain multiple lists.
"""
c = open(str(source[0]), 'rb').read()
- c = string.replace(c, '__RPM_FILES__', env['RPM_FILES'])
+ c = string.replace(c, '__VERSION' + '__', env['VERSION'])
+ c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES'])
open(str(target[0]), 'wb').write(c)
rpm_files.sort()
cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot)
if not os.path.isdir(BUILDdir):
cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd
- env.Command(targets, specfile, cmd)
- env.Depends(targets, sourcefile)
+ t = env.Command(targets, specfile, cmd)
+ env.Depends(t, sourcefile)
- install_targets.extend(targets)
+ dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm)
+ dist_src_rpm = env.Install('$DISTDIR', src_rpm)
+ Local(dist_noarch_rpm, dist_src_rpm)
+ AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0644))
+ AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0644))
dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x),
dst_files)
env.Command(dfiles,
- noarch_rpm,
+ dist_noarch_rpm,
"$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)")
if dh_builddeb and fakeroot:
# Our Debian packaging builds directly into build/dist,
- # so we don't need to add the .debs to install_targets.
+ # so we don't need to Install() the .debs.
deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
for d in p['debian_deps']:
b = env.SCons_revision(os.path.join(build, d), d)
build_dir_local = os.path.join(build_dir, local)
build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
- local_tar_gz = os.path.join(build_dir, 'dist', "%s.tar.gz" % s_l_v)
- local_zip = os.path.join(build_dir, 'dist', "%s.zip" % s_l_v)
+ dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v)
+ dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v)
+ AddPostAction(dist_local_tar_gz, Chmod(dist_local_tar_gz, 0644))
+ AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0644))
commands = [
Delete(build_dir_local),
Local(l)
if gzip:
- env.Command(local_tar_gz,
+ env.Command(dist_local_tar_gz,
local_targets,
"cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
Mkdir(test_local_tar_gz_dir),
"cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]
- env.Command(unpack_targets, local_tar_gz, commands)
+ env.Command(unpack_targets, dist_local_tar_gz, commands)
if zipit:
- env.Command(local_zip, local_targets, zipit,
+ env.Command(dist_local_zip, local_targets, zipit,
CD = build_dir_local, PSV = '.')
unpack_targets = map(lambda x, d=test_local_zip_dir:
Mkdir(test_local_zip_dir),
unzipit]
- env.Command(unpack_targets, local_zip, unzipit,
+ env.Command(unpack_targets, dist_local_zip, unzipit,
UNPACK_ZIP_DIR = test_local_zip_dir)
- #
- # And, lastly, install the appropriate packages in the
- # appropriate subdirectory.
- #
- b_d_files = env.Install(os.path.join(build_dir, 'dist'), install_targets)
- Local(b_d_files)
-
#
#
#
SConscript('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.
+# If we're running in a Subversion working directory, pack up a complete
+# source archive from the project files and files in the change.
#
-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[-1])
-
- cmd = "aegis -list -terse pf 2>/dev/null"
- pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
- cmd = "aegis -list -terse -c %s cf 2>/dev/null" % change
- cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines())
- u = {}
- for f in pf + cf:
- u[f] = 1
- for f in df:
- try:
- del u[f]
- except KeyError:
- pass
- sfiles = filter(lambda x: x[-9:] != '.aeignore' and
- x[-9:] != '.sconsign' and
- x[-10:] != '.cvsignore',
- u.keys())
+if not svn_status:
+ "Not building in a Subversion tree; skipping building src package."
+else:
+ slines = filter(lambda l: l[0] in ' MA', svn_status_lines)
+ sentries = map(lambda l: l.split()[-1], slines)
+ sfiles = filter(os.path.isfile, sentries)
+
+ remove_patterns = [
+ '.svnt/*',
+ '*.aeignore',
+ '*.cvsignore',
+ 'www/*',
+ ]
+
+ for p in remove_patterns:
+ sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles)
if sfiles:
ps = "%s-src" % project
'scons',
'build')),
Delete("$TEST_SRC_TAR_GZ_DIR"),
- 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s"' % \
+ 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
(os.path.join(unpack_tar_gz_dir, psv),
os.path.join('src', 'script', 'scons.py'),
os.path.join('build', 'scons')),
'scons',
'build')),
Delete("$TEST_SRC_ZIP_DIR"),
- 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s"' % \
+ 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \
(os.path.join(unpack_zip_dir, psv),
os.path.join('src', 'script', 'scons.py'),
os.path.join('build', 'scons')),
],
ENV = ENV)
-for pf in packaging_flavors:
- Alias(pf, ['build/test-'+pf, 'build/QMTest', 'build/runtest.py'])
+for pf, help_text in packaging_flavors:
+ Alias(pf, [
+ os.path.join(build_dir, 'test-'+pf),
+ os.path.join(build_dir, 'QMTest'),
+ os.path.join(build_dir, 'runtest.py'),
+ ])