#
# SConstruct file to build scons packages during development.
#
# See the README.rst file for an overview of how SCons is built and tested.

from __future__ import print_function

copyright_years = '2001 - 2019'

# This gets inserted into the man pages to reflect the month of release.
month_year = 'December 2019'

#
# Copyright (c) 2001 - 2019 The SCons Foundation
#
# 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 distutils.command
import fnmatch
import os
import os.path
import re
import stat
import sys
import tempfile
import time
import socket
import textwrap

import bootstrap

project = 'scons'
default_version = '3.1.2'
copyright = "Copyright (c) %s The SCons Foundation" % copyright_years

SConsignFile()

#
# 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
#
gzip = whereis('gzip')
git = os.path.exists('.git') and whereis('git')
unzip = whereis('unzip')
zip = whereis('zip')

#
# Now grab the information that we "build" into the files.
#
date = ARGUMENTS.get('DATE')
if not date:
    date = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))

developer = ARGUMENTS.get('DEVELOPER')
if not developer:
    for variable in ['USERNAME', 'LOGNAME', 'USER']:
        developer = os.environ.get(variable)
        if developer:
            break
    if os.environ.get('SOURCE_DATE_EPOCH'):
        developer = '_reproducible'

build_system = ARGUMENTS.get('BUILD_SYSTEM')
if not build_system:
    if os.environ.get('SOURCE_DATE_EPOCH'):
        build_system = '_reproducible'
    else:
        build_system = socket.gethostname().split('.')[0]

version = ARGUMENTS.get('VERSION', '')
if not version:
    version = default_version

git_status_lines = []

if git:
    cmd = "%s ls-files 2> /dev/null" % git
    with os.popen(cmd, "r") as p:
        git_status_lines = p.readlines()

revision = ARGUMENTS.get('REVISION', '')
def generate_build_id(revision):
    return revision

if not revision and git:
    with os.popen("%s rev-parse HEAD 2> /dev/null" % git, "r") as p:
        git_hash = p.read().strip()
    def generate_build_id(revision):
        result = git_hash
        if [l for l in git_status_lines if 'modified' in l]:
            result = result + '[MODIFIED]'
        return result
    revision = git_hash

checkpoint = ARGUMENTS.get('CHECKPOINT', '')
if checkpoint:
    if checkpoint == 'd':
        checkpoint = time.strftime('%Y%m%d', time.localtime(time.time()))
    elif checkpoint == 'r':
        checkpoint = 'r' + revision
    version = version + '.beta.' + checkpoint

build_id = ARGUMENTS.get('BUILD_ID')
if build_id is None:
    if revision:
        build_id = generate_build_id(revision)
    else:
        build_id = ''

#
# Adding some paths to sys.path, this is mainly needed
# for the doc toolchain.
#
addpaths = [os.path.abspath(os.path.join(os.getcwd(), 'bin')),
            os.path.abspath(os.path.join(os.getcwd(), 'testing/framework'))]
for a in addpaths:
    if a not in sys.path:
        sys.path.append(a)


# 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 ['LOGNAME', 'PYTHONPATH', 'LD_LIBRARY_PATH']:
    if key in os.environ:
        ENV[key] = os.environ[key]

build_dir = ARGUMENTS.get('BUILDDIR', 'build')
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(). If SOURCE_DATE_EPOCH " +
                        "env var is set, '_reproducible' is the default."),

    ("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." +
                        "If the SOURCE_DATE_EPOCH env var is set, " +
                        "'_reproducible' is the default."),

    ("REVISION=",       "The revision number of the source being built.  " +
                        "The default is the git hash returned " +
                        "'git rev-parse HEAD', 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 = [
    ('tar-gz',          "The normal .tar.gz file for end-user installation."),
    ('local-tar-gz',    "A .tar.gz file for dropping into other software " +
                        "for local use."),
    ('zip',             "The normal .zip file for end-user installation."),
    ('local-zip',       "A .zip file for dropping into other software " +
                        "for local use."),
    ('src-tar-gz',      "A .tar.gz file containing all the source " +
                        "(including tests and documentation)."),
    ('src-zip',         "A .zip file containing all the source " +
                        "(including tests and documentation)."),
]

test_tar_gz_dir       = os.path.join(build_dir, "test-tar-gz")
test_src_tar_gz_dir   = os.path.join(build_dir, "test-src-tar-gz")
test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
test_zip_dir          = os.path.join(build_dir, "test-zip")
test_src_zip_dir      = os.path.join(build_dir, "test-src-zip")
test_local_zip_dir    = os.path.join(build_dir, "test-local-zip")

unpack_tar_gz_dir     = os.path.join(build_dir, "unpack-tar-gz")
unpack_zip_dir        = os.path.join(build_dir, "unpack-zip")

if is_windows():
    tar_hflag = ''
    python_project_subinst_dir = os.path.join("Lib", "site-packages", project)
    project_script_subinst_dir = 'Scripts'
else:
    tar_hflag = 'h'
    python_project_subinst_dir = os.path.join("lib", project)
    project_script_subinst_dir = 'bin'




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 = sorted(packaging_flavors + [('doc', 'The SCons documentation.')])

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')




revaction = SCons_revision
revbuilder = Builder(action = Action(SCons_revision,
                                     varlist=['COPYRIGHT', 'VERSION']))


# Just make copies, don't symlink them.
SetOption('duplicate', 'copy')

env = Environment(
                   ENV                 = ENV,

                   BUILD               = build_id,
                   BUILDDIR            = build_dir,
                   BUILDSYS            = build_system,
                   COPYRIGHT           = copyright,
                   DATE                = date,
                   DEB_DATE            = deb_date,
                   DEVELOPER           = developer,
                   DISTDIR             = os.path.join(build_dir, 'dist'),
                   MONTH_YEAR          = month_year,
                   REVISION            = revision,
                   VERSION             = version,

                   TAR_HFLAG           = tar_hflag,

                   ZIP                 = zip,
                   ZIPFLAGS            = '-r',
                   UNZIP               = unzip,
                   UNZIPFLAGS          = '-o -d $UNPACK_ZIP_DIR',

                   ZCAT                = zcat,

                   TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir,
                   TEST_SRC_ZIP_DIR    = test_src_zip_dir,
                   TEST_TAR_GZ_DIR     = test_tar_gz_dir,
                   TEST_ZIP_DIR        = test_zip_dir,

                   UNPACK_TAR_GZ_DIR   = unpack_tar_gz_dir,
                   UNPACK_ZIP_DIR      = unpack_zip_dir,

                   BUILDERS            = { 'SCons_revision' : revbuilder,
                                           'SOElim' : soelimbuilder },

                   PYTHON              = '"%s"' % sys.executable,
                   PYTHONFLAGS         = '-tt',
                 )

Version_values = [Value(version), Value(build_id)]

#
# 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.
#

from distutils.sysconfig import get_python_lib


python_scons = {
        'pkg'           : 'python-' + project,
        'src_subdir'    : 'engine',
        'inst_subdir'   : get_python_lib(),

        'debian_deps'   : [
                            'debian/changelog',
                            'debian/compat',
                            'debian/control',
                            'debian/copyright',
                            'debian/dirs',
                            'debian/docs',
                            'debian/postinst',
                            'debian/prerm',
                            'debian/rules',
                          ],

        'files'         : [ 'LICENSE.txt',
                            'README.txt',
                            'setup.cfg',
                            'setup.py',
                          ],

        'filemap'       : {
                            'LICENSE.txt' : '../LICENSE.txt'
                          },

        'buildermap'    : {},

        'explicit_deps' : {
                            'SCons/__init__.py' : Version_values,
                          },
}


scons_script = {
        'pkg'           : project + '-script',
        'src_subdir'    : 'script',
        'inst_subdir'   : 'bin',

        'debian_deps'   : [
                            'debian/changelog',
                            'debian/compat',
                            'debian/control',
                            'debian/copyright',
                            'debian/dirs',
                            'debian/docs',
                            'debian/postinst',
                            'debian/prerm',
                            'debian/rules',
                          ],

        'files'         : [
                            'LICENSE.txt',
                            'README.txt',
                            'setup.cfg',
                            'setup.py',
                          ],

        'filemap'       : {
                            'LICENSE.txt'       : '../LICENSE.txt',
                            'scons'             : 'scons.py',
                            'sconsign'          : 'sconsign.py',
                            'scons-time'        : 'scons-time.py',
                            'scons-configure-cache'        : 'scons-configure-cache.py',
                           },

        'buildermap'    : {},

        'explicit_deps' : {
                            'scons'       : Version_values,
                            'sconsign'    : Version_values,
                          },
}

scons = {
        'pkg'           : project,

        'debian_deps'   : [
                            'debian/changelog',
                            'debian/compat',
                            'debian/control',
                            'debian/copyright',
                            'debian/dirs',
                            'debian/docs',
                            'debian/postinst',
                            'debian/prerm',
                            'debian/rules',
                          ],

        'files'         : [
                            'CHANGES.txt',
                            'LICENSE.txt',
                            'README.txt',
                            'RELEASE.txt',
                            'scons.1',
                            'sconsign.1',
                            'scons-time.1',
                            'script/scons.bat',
                            'setup.cfg',
                            'setup.py',
                          ],

        'filemap'       : {
                            'scons.1' : '$BUILDDIR/doc/man/scons.1',
                            'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
                            'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
                          },

        'buildermap'    : {
                            'scons.1' : env.SOElim,
                            'sconsign.1' : env.SOElim,
                            'scons-time.1' : env.SOElim,
                          },

        'subpkgs'       : [ python_scons, scons_script ],

        'subinst_dirs'  : {
                             'python-' + project : python_project_subinst_dir,
                             project + '-script' : project_script_subinst_dir,
                           },
}

scripts = ['scons', 'sconsign', 'scons-time', 'scons-configure-cache']

src_deps = []
src_files = []

for p in [ scons ]:
    #
    # Initialize variables with the right directories for this package.
    #
    pkg = p['pkg']
    pkg_version = "%s-%s" % (pkg, version)

    src = 'src'
    if 'src_subdir' in p:
        src = os.path.join(src, p['src_subdir'])

    build = os.path.join(build_dir, pkg)

    tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
    platform_tar_gz = os.path.join(build,
                                   'dist',
                                   "%s.%s.tar.gz" % (pkg_version, platform))
    zip = os.path.join(build, 'dist', "%s.zip" % pkg_version)
    platform_zip = os.path.join(build,
                                'dist',
                                "%s.%s.zip" % (pkg_version, platform))


    #
    # Update the environment with the relevant information
    # for this package.
    #
    # 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.
    #
    setup_py = os.path.join(build, 'setup.py')
    env.Replace(PKG = pkg,
                PKG_VERSION = pkg_version,
                SETUP_PY = '"%s"' % setup_py)
    Local(setup_py)

    #
    # 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.
    #
    manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr()
    src_files = bootstrap.parseManifestLines(src, manifest_in)
    raw_files = src_files[:]
    dst_files = src_files[:]

    MANIFEST_in_list = []


    if 'subpkgs' in p:
        #
        # 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']]

            MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr()
            MANIFEST_in_list.append(MANIFEST_in)
            files = bootstrap.parseManifestLines(os.path.join(src, ssubdir), MANIFEST_in)

            raw_files.extend(files)
            src_files.extend([os.path.join(ssubdir, x) for x in files])

            files = [os.path.join(isubdir, x) for x in files]
            dst_files.extend(files)
            for k, f in sp['filemap'].items():
                if f:
                    k = os.path.join(ssubdir, k)
                    p['filemap'][k] = os.path.join(ssubdir, f)
            for f, deps in sp['explicit_deps'].items():
                f = os.path.join(build, ssubdir, f)
                env.Depends(f, deps)

    #
    # 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 SConstruct, 3.1.2, etc.
    #
    for b in src_files:
        s = p['filemap'].get(b, b)
        if not s[0] == '$' and not os.path.isabs(s):
            s = os.path.join(src, s)

        builder = p['buildermap'].get(b, env.SCons_revision)
        x = builder(os.path.join(build, b), s)

        Local(x)

    #
    # NOW, finally, we can create the MANIFEST, which we do
    # by having Python 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")
    MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))

    def write_src_files(target, source, **kw):
        global src_files
        src_files.sort()
        with open(str(target[0]), 'w') as f:
            for file in src_files:
                f.write(file + "\n")
        return 0
    env.Command(os.path.join(build, 'MANIFEST'),
                MANIFEST_in_list,
                write_src_files)

    #
    # Now go through and arrange to create whatever packages we can.
    #
    build_src_files = [os.path.join(build, x) for x in src_files]
    Local(*build_src_files)

    distutils_formats = []
    distutils_targets = []
    dist_distutils_targets = []

    for target in distutils_targets:
        dist_target = env.Install('$DISTDIR', target)
        AddPostAction(dist_target, Chmod(dist_target, 0o644))
        dist_distutils_targets += dist_target

    if not gzip:
        print("gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg))
    else:

        distutils_formats.append('gztar')

        src_deps.append(tar_gz)

        distutils_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, 0o644))
        AddPostAction(dist_platform_tar_gz, Chmod(dist_platform_tar_gz, 0o644))

        #
        # Unpack the tar.gz archive created by the distutils into
        # build/unpack-tar-gz/scons-{version}.
        #
        # We'd like to replace the last three lines with the following:
        #
        #       tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
        #
        # but that gives heartburn to Cygwin's tar, so work around it
        # with separate zcat-tar-rm commands.
        #
        unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, pkg_version, x)
                               for x in src_files]
        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",
                    Delete(".temp"),
        ])

        #
        # Run setup.py in the unpacked subdirectory to "install" everything
        # into our build/test subdirectory.  The runtest.py script will set
        # PYTHONPATH so that the tests only look under build/test-{package},
        # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py,
        # etc.).  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.
        #
        dfiles = [os.path.join(test_tar_gz_dir, x) for x in dst_files]
        env.Command(dfiles, unpack_tar_gz_files, [
            Delete(os.path.join(unpack_tar_gz_dir, pkg_version, 'build')),
            Delete("$TEST_TAR_GZ_DIR"),
            '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_TAR_GZ_DIR" --standalone-lib' % \
                os.path.join(unpack_tar_gz_dir, pkg_version, 'setup.py'),
        ])

        #
        # Generate portage files for submission to Gentoo Linux.
        #
        gentoo = os.path.join(build, 'gentoo')
        ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
        digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
        env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)

        def Digestify(target, source, env):
            import hashlib
            src = source[0].rfile()
            with open(str(src),'rb') as f:
                contents = f.read()
            m = hashlib.md5()
            m.update(contents)
            sig = m.hexdigest()
            bytes = os.stat(str(src))[6]
            with open(str(target[0]), 'w') as f:
                f.write("MD5 %s %s %d\n" % (sig, src.name, bytes))
        env.Command(digest, tar_gz, Digestify)

    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 ])

        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, 0o644))
        AddPostAction(dist_platform_zip, Chmod(dist_platform_zip, 0o644))

        #
        # Unpack the zip archive created by the distutils into
        # build/unpack-zip/scons-{version}.
        #
        unpack_zip_files = [os.path.join(unpack_zip_dir, pkg_version, x)
                                         for x in src_files]

        env.Command(unpack_zip_files, dist_zip, [
            Delete(os.path.join(unpack_zip_dir, pkg_version)),
            unzipit,
        ])

        #
        # Run setup.py in the unpacked subdirectory to "install" everything
        # into our build/test subdirectory.  The runtest.py script will set
        # PYTHONPATH so that the tests only look under build/test-{package},
        # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py,
        # etc.).  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.
        #
        dfiles = [os.path.join(test_zip_dir, x) for x in dst_files]
        env.Command(dfiles, unpack_zip_files, [
            Delete(os.path.join(unpack_zip_dir, pkg_version, 'build')),
            Delete("$TEST_ZIP_DIR"),
            '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_ZIP_DIR" --standalone-lib' % \
                os.path.join(unpack_zip_dir, pkg_version, 'setup.py'),
        ])



    #
    # Use the Python distutils to generate the appropriate packages.
    #
    commands = [
        Delete(os.path.join(build, 'build', 'lib')),
        Delete(os.path.join(build, 'build', 'scripts')),
    ]

    if distutils_formats:
        commands.append(Delete(os.path.join(build,
                                            'build',
                                            'bdist.' + platform,
                                            'dumb')))
        for format in distutils_formats:
            commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_dumb -f %s" % format)

        commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" %  \
                            ','.join(distutils_formats))

    env.Command(distutils_targets, build_src_files, commands)

    #
    # Now create local packages for people who want to let people
    # build their SCons-buildable packages without having to
    # install SCons.
    #
    s_l_v = '%s-local-%s' % (pkg, version)

    local = pkg + '-local'
    build_dir_local = os.path.join(build_dir, local)
    build_dir_local_slv = os.path.join(build_dir, local, 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, 0o644))
    AddPostAction(dist_local_zip, Chmod(dist_local_zip, 0o644))

    commands = [
        Delete(build_dir_local),
        '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
                                                (build_dir_local, build_dir_local_slv),
    ]

    for script in scripts:
        # add .py extension for scons-local scripts on non-windows platforms
        if is_windows():
            break
        local_script = os.path.join(build_dir_local, script)
        commands.append(Move(local_script + '.py', local_script))

    rf = [x for x in raw_files if not x in scripts]
    rf = [os.path.join(s_l_v, x) for x in rf]
    for script in scripts:
        rf.append("%s.py" % script)
    local_targets = [os.path.join(build_dir_local, x) for x in rf]

    env.Command(local_targets, build_src_files, commands)

    scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
    l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
    local_targets.append(l)
    Local(l)

    scons_README = os.path.join(build_dir_local, 'scons-README')
    l = env.SCons_revision(scons_README, 'README-local')
    local_targets.append(l)
    Local(l)

    if gzip:
        if is_windows():
            # avoid problem with tar interpreting c:/ as a remote machine
            tar_cargs = '-cz --force-local -f'
        else:
            tar_cargs = '-czf'
        env.Command(dist_local_tar_gz,
                    local_targets,
                    "cd %s && tar %s $( ${TARGET.abspath} $) *" % (build_dir_local, tar_cargs))

        unpack_targets = [os.path.join(test_local_tar_gz_dir, x) for x in rf]
        commands = [Delete(test_local_tar_gz_dir),
                    Mkdir(test_local_tar_gz_dir),
                    "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir]

        env.Command(unpack_targets, dist_local_tar_gz, commands)

    if zipit:
        env.Command(dist_local_zip, local_targets, zipit,
                    CD = build_dir_local, PSV = '.')

        unpack_targets = [os.path.join(test_local_zip_dir, x) for x in rf]
        commands = [Delete(test_local_zip_dir),
                    Mkdir(test_local_zip_dir),
                    unzipit]

        env.Command(unpack_targets, dist_local_zip, unzipit,
                    UNPACK_ZIP_DIR = test_local_zip_dir)

#
#
#
Export('build_dir', 'env')

SConscript('testing/framework/SConscript')

#
#
#
sp = env.Install(build_dir, 'runtest.py')
Local(sp)
files = [
    'runtest.py',
]


#
# Documentation.
#
Export('build_dir', 'env', 'whereis', 'revaction')

SConscript('doc/SConscript')

#
# If we're running in a Git working directory, pack up a complete
# source archive from the project files and files in the change.
#


sfiles = [l.split()[-1] for l in git_status_lines]
if git_status_lines:
    # slines = [l for l in git_status_lines if 'modified:' in l]
    # sfiles = [l.split()[-1] for l in slines]
    pass
else:
   print("Not building in a Git tree; skipping building src package.")

if sfiles:
    remove_patterns = [
        '*.gitignore',
        '*.hgignore',
        'www/*',
    ]

    for p in remove_patterns:
        sfiles = [s for s in sfiles if not fnmatch.fnmatch(s, p)]

    if sfiles:
        ps = "%s-src" % project
        psv = "%s-%s" % (ps, version)
        b_ps = os.path.join(build_dir, ps)
        b_psv = os.path.join(build_dir, psv)
        b_psv_stamp = b_psv + '-stamp'

        src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
        src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)

        Local(src_tar_gz, src_zip)

        for file in sfiles:
            if file.endswith('jpg') or file.endswith('png'):
                # don't revision binary files.
                env.Install(os.path.dirname(os.path.join(b_ps,file)), file)
            else:
                env.SCons_revision(os.path.join(b_ps, file), file)

        b_ps_files = [os.path.join(b_ps, x) for x in sfiles]
        cmds = [
            Delete(b_psv),
            Copy(b_psv, b_ps),
            Touch("$TARGET"),
        ]

        env.Command(b_psv_stamp, src_deps + b_ps_files, cmds)

        Local(*b_ps_files)

        if gzip:

            env.Command(src_tar_gz, b_psv_stamp,
                        "tar cz${TAR_HFLAG} -f $TARGET -C build %s" % psv)

            #
            # Unpack the archive into build/unpack/scons-{version}.
            #
            unpack_tar_gz_files = [os.path.join(unpack_tar_gz_dir, psv, x)
                                   for x in sfiles]

            #
            # We'd like to replace the last three lines with the following:
            #
            #   tar zxf $SOURCES -C $UNPACK_TAR_GZ_DIR
            #
            # but that gives heartburn to Cygwin's tar, so work around it
            # with separate zcat-tar-rm commands.
            env.Command(unpack_tar_gz_files, src_tar_gz, [
                Delete(os.path.join(unpack_tar_gz_dir, psv)),
                "$ZCAT $SOURCES > .temp",
                "tar xf .temp -C $UNPACK_TAR_GZ_DIR",
                Delete(".temp"),
            ])

            #
            # Run setup.py in the unpacked subdirectory to "install" everything
            # into our build/test subdirectory.  The runtest.py script will set
            # PYTHONPATH so that the tests only look under build/test-{package},
            # and under testing/framework (for the testing modules TestCmd.py,
            # TestSCons.py, etc.).  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.
            #
            dfiles = [os.path.join(test_src_tar_gz_dir, x) for x in dst_files]
            scons_lib_dir = os.path.join(unpack_tar_gz_dir, psv, 'src', 'engine')
            ENV = env.Dictionary('ENV').copy()
            ENV['SCONS_LIB_DIR'] = scons_lib_dir
            ENV['USERNAME'] = developer
            env.Command(dfiles, unpack_tar_gz_files,
                [
                Delete(os.path.join(unpack_tar_gz_dir,
                                    psv,
                                    'build',
                                    'scons',
                                    'build')),
                Delete("$TEST_SRC_TAR_GZ_DIR"),
                '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')),
                '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_TAR_GZ_DIR" --standalone-lib' % \
                    os.path.join(unpack_tar_gz_dir,
                                 psv,
                                 'build',
                                 'scons',
                                 'setup.py'),
                ],
                ENV = ENV)

        if zipit:

            env.Command(src_zip, b_psv_stamp, zipit, CD = 'build', PSV = psv)

            #
            # Unpack the archive into build/unpack/scons-{version}.
            #
            unpack_zip_files = [os.path.join(unpack_zip_dir, psv, x)
                                for x in sfiles]

            env.Command(unpack_zip_files, src_zip, [
                Delete(os.path.join(unpack_zip_dir, psv)),
                unzipit
            ])

            #
            # Run setup.py in the unpacked subdirectory to "install" everything
            # into our build/test subdirectory.  The runtest.py script will set
            # PYTHONPATH so that the tests only look under build/test-{package},
            # and under testing/framework (for the testing modules TestCmd.py,
            # TestSCons.py, etc.).  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.
            #
            dfiles = [os.path.join(test_src_zip_dir, x) for x in dst_files]
            scons_lib_dir = os.path.join(unpack_zip_dir, psv, 'src', 'engine')
            ENV = env.Dictionary('ENV').copy()
            ENV['SCONS_LIB_DIR'] = scons_lib_dir
            ENV['USERNAME'] = developer
            env.Command(dfiles, unpack_zip_files,
                [
                Delete(os.path.join(unpack_zip_dir,
                                    psv,
                                    'build',
                                    'scons',
                                    'build')),
                Delete("$TEST_SRC_ZIP_DIR"),
                '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')),
                '$PYTHON $PYTHONFLAGS "%s" install "--prefix=$TEST_SRC_ZIP_DIR" --standalone-lib' % \
                    os.path.join(unpack_zip_dir,
                                 psv,
                                 'build',
                                 'scons',
                                 'setup.py'),
                ],
                ENV = ENV)

for pf, help_text in packaging_flavors:
    Alias(pf, [
        os.path.join(build_dir, 'test-'+pf),
        os.path.join(build_dir, 'testing/framework'),
        os.path.join(build_dir, 'runtest.py'),
    ])


