Issue #14018: Backport OS X installer updates from 3.3.
diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py
index ba92012..004765a 100755
--- a/Mac/BuildScript/build-installer.py
+++ b/Mac/BuildScript/build-installer.py
@@ -1,7 +1,7 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 """
 This script is used to build "official" universal installers on Mac OS X.
-It requires at least Mac OS X 10.4, Xcode 2.2 and the 10.4u SDK for
+It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
 32-bit builds.  64-bit or four-way universal builds require at least
 OS X 10.5 and the 10.5 SDK.
 
@@ -10,18 +10,41 @@
 which is used to build the documentation, currently requires at least
 Python 2.4.
 
+In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script
+requires an installed version of hg and a third-party version of
+Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5
+(for 10.6 or later) installed in /Library/Frameworks.  When installed,
+the Python built by this script will attempt to dynamically link first to
+Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
+back to the ones in /System/Library/Framework.  For the build, we recommend
+installing the most recent ActiveTcl 8.4 or 8.5 version.
+
+32-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5
+and the installation of additional components, such as a newer Python
+(2.5 is needed for Python parser updates), hg, and svn (for the documentation
+build).
+
 Usage: see USAGE variable in the script.
 """
-import platform, os, sys, getopt, textwrap, shutil, urllib2, stat, time, pwd
-import grp
+import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
+try:
+    import urllib2 as urllib_request
+except ImportError:
+    import urllib.request as urllib_request
+
+STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
+             | stat.S_IRGRP |                stat.S_IXGRP
+             | stat.S_IROTH |                stat.S_IXOTH )
+
+STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
+             | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
+             | stat.S_IROTH |                stat.S_IXOTH )
 
 INCLUDE_TIMESTAMP = 1
 VERBOSE = 1
 
 from plistlib import Plist
 
-import MacOS
-
 try:
     from plistlib import writePlist
 except ImportError:
@@ -42,20 +65,35 @@
         if ln.startswith(variable):
             value = ln[len(variable):].strip()
             return value[1:-1]
-    raise RuntimeError, "Cannot find variable %s" % variable[:-1]
+    raise RuntimeError("Cannot find variable %s" % variable[:-1])
+
+_cache_getVersion = None
 
 def getVersion():
-    return grepValue(os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
+    global _cache_getVersion
+    if _cache_getVersion is None:
+        _cache_getVersion = grepValue(
+            os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
+    return _cache_getVersion
 
 def getVersionTuple():
     return tuple([int(n) for n in getVersion().split('.')])
 
+def getVersionMajorMinor():
+    return tuple([int(n) for n in getVersion().split('.', 2)])
+
+_cache_getFullVersion = None
+
 def getFullVersion():
+    global _cache_getFullVersion
+    if _cache_getFullVersion is not None:
+        return _cache_getFullVersion
     fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
     for ln in open(fn):
         if 'PY_VERSION' in ln:
-            return ln.split()[-1][1:-1]
-    raise RuntimeError, "Cannot find full version??"
+            _cache_getFullVersion = ln.split()[-1][1:-1]
+            return _cache_getFullVersion
+    raise RuntimeError("Cannot find full version??")
 
 # The directory we'll use to create the build (will be erased and recreated)
 WORKDIR = "/tmp/_py"
@@ -111,13 +149,15 @@
 DEPTARGET = '10.3'
 
 target_cc_map = {
-        '10.3': 'gcc-4.0',
-        '10.4': 'gcc-4.0',
-        '10.5': 'gcc-4.0',
-        '10.6': 'gcc-4.2',
+        '10.3': ('gcc-4.0', 'g++-4.0'),
+        '10.4': ('gcc-4.0', 'g++-4.0'),
+        '10.5': ('gcc-4.2', 'g++-4.2'),
+        '10.6': ('gcc-4.2', 'g++-4.2'),
+        '10.7': ('clang', 'clang++'),
+        '10.8': ('clang', 'clang++'),
 }
 
-CC = target_cc_map[DEPTARGET]
+CC, CXX = target_cc_map[DEPTARGET]
 
 PYTHON_3 = getVersionTuple() >= (3, 0)
 
@@ -135,6 +175,13 @@
     --universal-archs=x  universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
 """)% globals()
 
+# Dict of object file names with shared library names to check after building.
+# This is to ensure that we ended up dynamically linking with the shared
+# library paths and versions we expected.  For example:
+#   EXPECTED_SHARED_LIBS['_tkinter.so'] = [
+#                       '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
+#                       '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
+EXPECTED_SHARED_LIBS = {}
 
 # Instructions for building libraries that are necessary for building a
 # batteries included python.
@@ -143,6 +190,75 @@
 def library_recipes():
     result = []
 
+    LT_10_5 = bool(DEPTARGET < '10.5')
+
+    if getVersionTuple() >= (3, 3):
+        result.extend([
+          dict(
+              name="XZ 5.0.3",
+              url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
+              checksum='fefe52f9ecd521de2a8ce38c21a27574',
+              configure_pre=[
+                    '--disable-dependency-tracking',
+              ]
+              ),
+        ])
+
+    result.extend([
+          dict(
+              name="NCurses 5.9",
+              url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
+              checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
+              configure_pre=[
+                  "--enable-widec",
+                  "--without-cxx",
+                  "--without-cxx-binding",
+                  "--without-ada",
+                  "--without-curses-h",
+                  "--enable-shared",
+                  "--with-shared",
+                  "--without-debug",
+                  "--without-normal",
+                  "--without-tests",
+                  "--without-manpages",
+                  "--datadir=/usr/share",
+                  "--sysconfdir=/etc",
+                  "--sharedstatedir=/usr/com",
+                  "--with-terminfo-dirs=/usr/share/terminfo",
+                  "--with-default-terminfo-dir=/usr/share/terminfo",
+                  "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
+              ],
+              patchscripts=[
+                  ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
+                   "f54bf02a349f96a7c4f0d00922f3a0d4"),
+                   ],
+              useLDFlags=False,
+              install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
+                  shellQuote(os.path.join(WORKDIR, 'libraries')),
+                  shellQuote(os.path.join(WORKDIR, 'libraries')),
+                  getVersion(),
+                  ),
+          ),
+          dict(
+              name="SQLite 3.7.13",
+              url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
+              checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
+              extra_cflags=('-Os '
+                            '-DSQLITE_ENABLE_FTS4 '
+                            '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
+                            '-DSQLITE_ENABLE_RTREE '
+                            '-DSQLITE_TCL=0 '
+                 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
+              configure_pre=[
+                  '--enable-threadsafe',
+                  '--enable-shared=no',
+                  '--enable-static=yes',
+                  '--disable-readline',
+                  '--disable-dependency-tracking',
+              ]
+          ),
+        ])
+
     if DEPTARGET < '10.5':
         result.extend([
           dict(
@@ -150,8 +266,8 @@
               url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
               checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
               configure=None,
-              install='make install CC=%s PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
-                  CC,
+              install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
+                  CC, CXX,
                   shellQuote(os.path.join(WORKDIR, 'libraries')),
                   ' -arch '.join(ARCHLIST),
                   SDKPATH,
@@ -162,8 +278,8 @@
               url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
               checksum='debc62758716a169df9f62e6ab2bc634',
               configure=None,
-              install='make install CC=%s prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
-                  CC,
+              install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
+                  CC, CXX,
                   shellQuote(os.path.join(WORKDIR, 'libraries')),
                   ' -arch '.join(ARCHLIST),
                   SDKPATH,
@@ -178,58 +294,12 @@
               patches=[
                   # The readline maintainers don't do actual micro releases, but
                   # just ship a set of patches.
-                  'http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
-                  'http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
+                  ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
+                   'c642f2e84d820884b0bf9fd176bc6c3f'),
+                  ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
+                   '1a76781a1ea734e831588285db7ec9b1'),
               ]
           ),
-          dict(
-              name="SQLite 3.7.4",
-              url="http://www.sqlite.org/sqlite-autoconf-3070400.tar.gz",
-              checksum='8f0c690bfb33c3cbbc2471c3d9ba0158',
-              configure_env=('CFLAGS="-Os'
-                                  ' -DSQLITE_ENABLE_FTS3'
-                                  ' -DSQLITE_ENABLE_FTS3_PARENTHESIS'
-                                  ' -DSQLITE_ENABLE_RTREE'
-                                  ' -DSQLITE_TCL=0'
-                                  '"'),
-              configure_pre=[
-                  '--enable-threadsafe',
-                  '--enable-shared=no',
-                  '--enable-static=yes',
-                  '--disable-readline',
-                  '--disable-dependency-tracking',
-              ]
-          ),
-          dict(
-              name="NCurses 5.5",
-              url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
-              checksum='e73c1ac10b4bfc46db43b2ddfd6244ef',
-              configure_pre=[
-                  "--enable-widec",
-                  "--without-cxx",
-                  "--without-ada",
-                  "--without-progs",
-                  "--without-curses-h",
-                  "--enable-shared",
-                  "--with-shared",
-                  "--datadir=/usr/share",
-                  "--sysconfdir=/etc",
-                  "--sharedstatedir=/usr/com",
-                  "--with-terminfo-dirs=/usr/share/terminfo",
-                  "--with-default-terminfo-dir=/usr/share/terminfo",
-                  "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
-                  "--enable-termcap",
-              ],
-              patches=[
-                  "ncurses-5.5.patch",
-              ],
-              useLDFlags=False,
-              install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
-                  shellQuote(os.path.join(WORKDIR, 'libraries')),
-                  shellQuote(os.path.join(WORKDIR, 'libraries')),
-                  getVersion(),
-                  ),
-          ),
         ])
 
     if not PYTHON_3:
@@ -298,9 +368,7 @@
             source="/pydocs",
             readme="""\
                 This package installs the python documentation at a location
-                that is useable for pydoc and IDLE. If you have installed Xcode
-                it will also install a link to the documentation in
-                /Developer/Documentation/Python
+                that is useable for pydoc and IDLE.
                 """,
             postflight="scripts/postflight.documentation",
             required=False,
@@ -326,7 +394,7 @@
         ),
     ]
 
-    if DEPTARGET < '10.4':
+    if DEPTARGET < '10.4' and not PYTHON_3:
         result.append(
             dict(
                 name="PythonSystemFixes",
@@ -358,7 +426,7 @@
     """
     Return the contents of the named file
     """
-    return open(fn, 'rb').read()
+    return open(fn, 'r').read()
 
 def runCommand(commandline):
     """
@@ -370,7 +438,7 @@
     xit = fd.close()
     if xit is not None:
         sys.stdout.write(data)
-        raise RuntimeError, "command failed: %s"%(commandline,)
+        raise RuntimeError("command failed: %s"%(commandline,))
 
     if VERBOSE:
         sys.stdout.write(data); sys.stdout.flush()
@@ -381,7 +449,7 @@
     xit = fd.close()
     if xit is not None:
         sys.stdout.write(data)
-        raise RuntimeError, "command failed: %s"%(commandline,)
+        raise RuntimeError("command failed: %s"%(commandline,))
 
     return data
 
@@ -423,39 +491,60 @@
     # Because we only support dynamic load of only one major/minor version of
     # Tcl/Tk, ensure:
     # 1. there are no user-installed frameworks of Tcl/Tk with version
-    #       higher than the Apple-supplied system version
-    # 2. there is a user-installed framework in /Library/Frameworks with the
-    #       same version as the system version.  This allows users to choose
-    #       to install a newer patch level.
+    #       higher than the Apple-supplied system version in
+    #       SDKROOT/System/Library/Frameworks
+    # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
+    #       in) SDKROOT/Library/Frameworks with the same version as the system
+    #       version. This allows users to choose to install a newer patch level.
 
+    frameworks = {}
     for framework in ['Tcl', 'Tk']:
-        #fw = dict(lower=framework.lower(),
-        #            upper=framework.upper(),
-        #            cap=framework.capitalize())
-        #fwpth = "Library/Frameworks/%(cap)s.framework/%(lower)sConfig.sh" % fw
-        fwpth = 'Library/Frameworks/Tcl.framework/Versions/Current'
+        fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
         sysfw = os.path.join(SDKPATH, 'System', fwpth)
-        libfw = os.path.join('/', fwpth)
+        libfw = os.path.join(SDKPATH, fwpth)
         usrfw = os.path.join(os.getenv('HOME'), fwpth)
-        #version = "%(upper)s_VERSION" % fw
+        frameworks[framework] = os.readlink(sysfw)
+        if not os.path.exists(libfw):
+            fatal("Please install a link to a current %s %s as %s so "
+                    "the user can override the system framework."
+                    % (framework, frameworks[framework], libfw))
         if os.readlink(libfw) != os.readlink(sysfw):
             fatal("Version of %s must match %s" % (libfw, sysfw) )
         if os.path.exists(usrfw):
             fatal("Please rename %s to avoid possible dynamic load issues."
                     % usrfw)
 
+    if frameworks['Tcl'] != frameworks['Tk']:
+        fatal("The Tcl and Tk frameworks are not the same version.")
+
+    # add files to check after build
+    EXPECTED_SHARED_LIBS['_tkinter.so'] = [
+            "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
+                % frameworks['Tcl'],
+            "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
+                % frameworks['Tk'],
+            ]
+
     # Remove inherited environment variables which might influence build
     environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
                             'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
     for ev in list(os.environ):
         for prefix in environ_var_prefixes:
             if ev.startswith(prefix) :
-                print "INFO: deleting environment variable %s=%s" % (
-                                                    ev, os.environ[ev])
+                print("INFO: deleting environment variable %s=%s" % (
+                                                    ev, os.environ[ev]))
                 del os.environ[ev]
 
-    os.environ['PATH'] = '/bin:/sbin:/usr/bin:/usr/sbin'
-    print "Setting default PATH: %s"%(os.environ['PATH'])
+    base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
+    if 'SDK_TOOLS_BIN' in os.environ:
+        base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
+    # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
+    # add its fixed location here if it exists
+    OLD_DEVELOPER_TOOLS = '/Developer/Tools'
+    if os.path.isdir(OLD_DEVELOPER_TOOLS):
+        base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
+    os.environ['PATH'] = base_path
+    print("Setting default PATH: %s"%(os.environ['PATH']))
 
 
 def parseOptions(args=None):
@@ -463,7 +552,7 @@
     Parse arguments and update global settings.
     """
     global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
-    global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC
+    global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
 
     if args is None:
         args = sys.argv[1:]
@@ -472,18 +561,18 @@
         options, args = getopt.getopt(args, '?hb',
                 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
                   'dep-target=', 'universal-archs=', 'help' ])
-    except getopt.error, msg:
-        print msg
+    except getopt.GetoptError:
+        print(sys.exc_info()[1])
         sys.exit(1)
 
     if args:
-        print "Additional arguments"
+        print("Additional arguments")
         sys.exit(1)
 
     deptarget = None
     for k, v in options:
         if k in ('-h', '-?', '--help'):
-            print USAGE
+            print(USAGE)
             sys.exit(0)
 
         elif k in ('-d', '--build-dir'):
@@ -511,27 +600,28 @@
                     # target
                     DEPTARGET = default_target_map.get(v, '10.3')
             else:
-                raise NotImplementedError, v
+                raise NotImplementedError(v)
 
         else:
-            raise NotImplementedError, k
+            raise NotImplementedError(k)
 
     SRCDIR=os.path.abspath(SRCDIR)
     WORKDIR=os.path.abspath(WORKDIR)
     SDKPATH=os.path.abspath(SDKPATH)
     DEPSRC=os.path.abspath(DEPSRC)
 
-    CC=target_cc_map[DEPTARGET]
+    CC, CXX=target_cc_map[DEPTARGET]
 
-    print "Settings:"
-    print " * Source directory:", SRCDIR
-    print " * Build directory: ", WORKDIR
-    print " * SDK location:    ", SDKPATH
-    print " * Third-party source:", DEPSRC
-    print " * Deployment target:", DEPTARGET
-    print " * Universal architectures:", ARCHLIST
-    print " * C compiler:", CC
-    print ""
+    print("Settings:")
+    print(" * Source directory:", SRCDIR)
+    print(" * Build directory: ", WORKDIR)
+    print(" * SDK location:    ", SDKPATH)
+    print(" * Third-party source:", DEPSRC)
+    print(" * Deployment target:", DEPTARGET)
+    print(" * Universal architectures:", ARCHLIST)
+    print(" * C compiler:", CC)
+    print(" * C++ compiler:", CXX)
+    print("")
 
 
 
@@ -576,31 +666,18 @@
         xit = fp.close()
         if xit is not None:
             sys.stdout.write(data)
-            raise RuntimeError, "Cannot extract %s"%(archiveName,)
+            raise RuntimeError("Cannot extract %s"%(archiveName,))
 
         return os.path.join(builddir, retval)
 
     finally:
         os.chdir(curdir)
 
-KNOWNSIZES = {
-    "http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz": 7952742,
-    "http://downloads.sleepycat.com/db-4.4.20.tar.gz": 2030276,
-}
-
 def downloadURL(url, fname):
     """
     Download the contents of the url into the file.
     """
-    try:
-        size = os.path.getsize(fname)
-    except OSError:
-        pass
-    else:
-        if KNOWNSIZES.get(url) == size:
-            print "Using existing file for", url
-            return
-    fpIn = urllib2.urlopen(url)
+    fpIn = urllib_request.urlopen(url)
     fpOut = open(fname, 'wb')
     block = fpIn.read(10240)
     try:
@@ -615,6 +692,24 @@
         except:
             pass
 
+def verifyThirdPartyFile(url, checksum, fname):
+    """
+    Download file from url to filename fname if it does not already exist.
+    Abort if file contents does not match supplied md5 checksum.
+    """
+    name = os.path.basename(fname)
+    if os.path.exists(fname):
+        print("Using local copy of %s"%(name,))
+    else:
+        print("Did not find local copy of %s"%(name,))
+        print("Downloading %s"%(name,))
+        downloadURL(url, fname)
+        print("Archive for %s stored as %s"%(name, fname))
+    if os.system(
+            'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
+                % (shellQuote(fname), checksum) ):
+        fatal('MD5 checksum mismatch for file %s' % fname)
+
 def buildRecipe(recipe, basedir, archList):
     """
     Build software using a recipe. This function does the
@@ -635,17 +730,8 @@
     if not os.path.exists(DEPSRC):
         os.mkdir(DEPSRC)
 
-
-    if os.path.exists(sourceArchive):
-        print "Using local copy of %s"%(name,)
-
-    else:
-        print "Did not find local copy of %s"%(name,)
-        print "Downloading %s"%(name,)
-        downloadURL(url, sourceArchive)
-        print "Archive for %s stored as %s"%(name, sourceArchive)
-
-    print "Extracting archive for %s"%(name,)
+    verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
+    print("Extracting archive for %s"%(name,))
     buildDir=os.path.join(WORKDIR, '_bld')
     if not os.path.exists(buildDir):
         os.mkdir(buildDir)
@@ -655,18 +741,31 @@
     if 'buildDir' in recipe:
         os.chdir(recipe['buildDir'])
 
-
-    for fn in recipe.get('patches', ()):
-        if fn.startswith('http://'):
-            # Download the patch before applying it.
-            path = os.path.join(DEPSRC, os.path.basename(fn))
-            downloadURL(fn, path)
-            fn = path
-
-        fn = os.path.join(curdir, fn)
+    for patch in recipe.get('patches', ()):
+        if isinstance(patch, tuple):
+            url, checksum = patch
+            fn = os.path.join(DEPSRC, os.path.basename(url))
+            verifyThirdPartyFile(url, checksum, fn)
+        else:
+            # patch is a file in the source directory
+            fn = os.path.join(curdir, patch)
         runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
             shellQuote(fn),))
 
+    for patchscript in recipe.get('patchscripts', ()):
+        if isinstance(patchscript, tuple):
+            url, checksum = patchscript
+            fn = os.path.join(DEPSRC, os.path.basename(url))
+            verifyThirdPartyFile(url, checksum, fn)
+        else:
+            # patch is a file in the source directory
+            fn = os.path.join(curdir, patchscript)
+        if fn.endswith('.bz2'):
+            runCommand('bunzip2 -fk %s' % shellQuote(fn))
+            fn = fn[:-4]
+        runCommand('sh %s' % shellQuote(fn))
+        os.unlink(fn)
+
     if configure is not None:
         configure_args = [
             "--prefix=/usr/local",
@@ -685,40 +784,44 @@
 
         if recipe.get('useLDFlags', 1):
             configure_args.extend([
-                "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
+                "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
+                            "-I%s/usr/local/include"%(
+                        recipe.get('extra_cflags', ''),
+                        DEPTARGET,
                         ' -arch '.join(archList),
                         shellQuote(SDKPATH)[1:-1],
                         shellQuote(basedir)[1:-1],),
-                "LDFLAGS=-syslibroot,%s -L%s/usr/local/lib -arch %s"%(
+                "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
+                    DEPTARGET,
                     shellQuote(SDKPATH)[1:-1],
                     shellQuote(basedir)[1:-1],
                     ' -arch '.join(archList)),
             ])
         else:
             configure_args.extend([
-                "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
+                "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
+                            "-I%s/usr/local/include"%(
+                        recipe.get('extra_cflags', ''),
+                        DEPTARGET,
                         ' -arch '.join(archList),
                         shellQuote(SDKPATH)[1:-1],
                         shellQuote(basedir)[1:-1],),
             ])
 
         if 'configure_post' in recipe:
-            configure_args = configure_args = list(recipe['configure_post'])
+            configure_args = configure_args + list(recipe['configure_post'])
 
         configure_args.insert(0, configure)
         configure_args = [ shellQuote(a) for a in configure_args ]
 
-        if 'configure_env' in recipe:
-            configure_args.insert(0, recipe['configure_env'])
-
-        print "Running configure for %s"%(name,)
+        print("Running configure for %s"%(name,))
         runCommand(' '.join(configure_args) + ' 2>&1')
 
-    print "Running install for %s"%(name,)
+    print("Running install for %s"%(name,))
     runCommand('{ ' + install + ' ;} 2>&1')
 
-    print "Done %s"%(name,)
-    print ""
+    print("Done %s"%(name,))
+    print("")
 
     os.chdir(curdir)
 
@@ -726,9 +829,9 @@
     """
     Build our dependencies into $WORKDIR/libraries/usr/local
     """
-    print ""
-    print "Building required libraries"
-    print ""
+    print("")
+    print("Building required libraries")
+    print("")
     universal = os.path.join(WORKDIR, 'libraries')
     os.mkdir(universal)
     os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
@@ -742,7 +845,7 @@
 def buildPythonDocs():
     # This stores the documentation as Resources/English.lproj/Documentation
     # inside the framwork. pydoc and IDLE will pick it up there.
-    print "Install python documentation"
+    print("Install python documentation")
     rootDir = os.path.join(WORKDIR, '_root')
     buildDir = os.path.join('../../Doc')
     docdir = os.path.join(rootDir, 'pydocs')
@@ -757,7 +860,7 @@
 
 
 def buildPython():
-    print "Building a universal python for %s architectures" % UNIVERSALARCHS
+    print("Building a universal python for %s architectures" % UNIVERSALARCHS)
 
     buildDir = os.path.join(WORKDIR, '_bld', 'python')
     rootDir = os.path.join(WORKDIR, '_root')
@@ -785,7 +888,7 @@
     # will find them during its extension import sanity checks.
     os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
                                         'libraries', 'usr', 'local', 'lib')
-    print "Running configure..."
+    print("Running configure...")
     runCommand("%s -C --enable-framework --enable-universalsdk=%s "
                "--with-universal-archs=%s "
                "%s "
@@ -797,19 +900,19 @@
         shellQuote(WORKDIR)[1:-1],
         shellQuote(WORKDIR)[1:-1]))
 
-    print "Running make"
+    print("Running make")
     runCommand("make")
 
-    print "Running make install"
+    print("Running make install")
     runCommand("make install DESTDIR=%s"%(
         shellQuote(rootDir)))
 
-    print "Running make frameworkinstallextras"
+    print("Running make frameworkinstallextras")
     runCommand("make frameworkinstallextras DESTDIR=%s"%(
         shellQuote(rootDir)))
 
     del os.environ['DYLD_LIBRARY_PATH']
-    print "Copying required shared libraries"
+    print("Copying required shared libraries")
     if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
         runCommand("mv %s/* %s"%(
             shellQuote(os.path.join(
@@ -820,16 +923,16 @@
                 'Python.framework', 'Versions', getVersion(),
                 'lib'))))
 
-    print "Fix file modes"
+    print("Fix file modes")
     frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
     gid = grp.getgrnam('admin').gr_gid
 
+    shared_lib_error = False
     for dirpath, dirnames, filenames in os.walk(frmDir):
         for dn in dirnames:
-            os.chmod(os.path.join(dirpath, dn), 0775)
+            os.chmod(os.path.join(dirpath, dn), STAT_0o775)
             os.chown(os.path.join(dirpath, dn), -1, gid)
 
-
         for fn in filenames:
             if os.path.islink(fn):
                 continue
@@ -840,6 +943,19 @@
             os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
             os.chown(p, -1, gid)
 
+            if fn in EXPECTED_SHARED_LIBS:
+                # check to see that this file was linked with the
+                # expected library path and version
+                data = captureCommand("otool -L %s" % shellQuote(p))
+                for sl in EXPECTED_SHARED_LIBS[fn]:
+                    if ("\t%s " % sl) not in data:
+                        print("Expected shared lib %s was not linked with %s"
+                                % (sl, p))
+                        shared_lib_error = True
+
+    if shared_lib_error:
+        fatal("Unexpected shared library errors.")
+
     if PYTHON_3:
         LDVERSION=None
         VERSION=None
@@ -863,19 +979,26 @@
 
     # We added some directories to the search path during the configure
     # phase. Remove those because those directories won't be there on
-    # the end-users system.
-    path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
-                'Versions', version, 'lib', 'python%s'%(version,),
-                'config' + config_suffix, 'Makefile')
-    fp = open(path, 'r')
-    data = fp.read()
-    fp.close()
+    # the end-users system. Also remove the directories from _sysconfigdata.py
+    # (added in 3.3) if it exists.
 
-    data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
-    data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
-    fp = open(path, 'w')
-    fp.write(data)
-    fp.close()
+    path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
+                                'Python.framework', 'Versions',
+                                version, 'lib', 'python%s'%(version,))
+    paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
+             os.path.join(path_to_lib, '_sysconfigdata.py')]
+    for path in paths:
+        if not os.path.exists(path):
+            continue
+        fp = open(path, 'r')
+        data = fp.read()
+        fp.close()
+
+        data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
+        data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
+        fp = open(path, 'w')
+        fp.write(data)
+        fp.close()
 
     # Add symlinks in /usr/local/bin, using relative links
     usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
@@ -907,17 +1030,17 @@
 
     # This one is not handy as a template variable
     data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
-    fp = open(outPath, 'wb')
+    fp = open(outPath, 'w')
     fp.write(data)
     fp.close()
 
 def patchScript(inPath, outPath):
     data = fileContents(inPath)
     data = data.replace('@PYVER@', getVersion())
-    fp = open(outPath, 'wb')
+    fp = open(outPath, 'w')
     fp.write(data)
     fp.close()
-    os.chmod(outPath, 0755)
+    os.chmod(outPath, STAT_0o755)
 
 
 
@@ -934,7 +1057,7 @@
         readme = textwrap.dedent(recipe['readme'])
         isRequired = recipe.get('required', True)
 
-        print "- building package %s"%(pkgname,)
+        print("- building package %s"%(pkgname,))
 
         # Substitute some variables
         textvars = dict(
@@ -979,7 +1102,7 @@
             patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
 
         vers = getFullVersion()
-        major, minor = map(int, getVersion().split('.', 2))
+        major, minor = getVersionMajorMinor()
         pl = Plist(
                 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
                 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
@@ -1016,7 +1139,7 @@
 def makeMpkgPlist(path):
 
     vers = getFullVersion()
-    major, minor = map(int, getVersion().split('.', 2))
+    major, minor = getVersionMajorMinor()
 
     pl = Plist(
             CFBundleGetInfoString="Python %s"%(vers,),
@@ -1127,7 +1250,7 @@
     # Custom icon for the DMG, shown when the DMG is mounted.
     shutil.copy("../Icons/Disk Image.icns",
             os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
-    runCommand("/Developer/Tools/SetFile -a C %s/"%(
+    runCommand("SetFile -a C %s/"%(
             shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
 
     runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
@@ -1168,6 +1291,7 @@
 
     os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
     os.environ['CC'] = CC
+    os.environ['CXX'] = CXX
 
     if os.path.exists(WORKDIR):
         shutil.rmtree(WORKDIR)
@@ -1198,7 +1322,7 @@
 
     folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
         getVersion(),))
-    os.chmod(folder, 0755)
+    os.chmod(folder, STAT_0o755)
     setIcon(folder, "../Icons/Python Folder.icns")
 
     # Create the installer
@@ -1211,9 +1335,9 @@
     shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
 
     fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
-    print >> fp, "# BUILD INFO"
-    print >> fp, "# Date:", time.ctime()
-    print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
+    fp.write("# BUILD INFO\n")
+    fp.write("# Date: %s\n" % time.ctime())
+    fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
     fp.close()
 
     # And copy it to a DMG