issue-sl-installer OS X installer: fix installer builds on 10.6 (Snow Leopard) Patch 0: this patch merges and resolves the minor differences between the python2 and python3 versions of the OS X installer build script, build-installer.py. Having a common build script base simplifies subsequent patches and future maintenance. Specific differences for py3 builds and installs: 1. Installer packages "PythonProfileChanges" and "PythonSystemFixes" default to "not selected for install" 2. Include "--with-computed-gotos" on configure. 3. Do not change Framework/Current link during install. APPLIES py3k, 3.1, trunk, 2.6 NOTE issue-sl-installer-py3k-31.txt (this file) py3k, 3.1 issue-sl-installer-trunk.txt trunk issue-sl-installer-26.txt 2.6 diff -r 514fd0ab66fe Mac/BuildScript/build-installer.py --- Mac/BuildScript/build-installer.py Sun Jan 03 22:19:56 2010 -0800 +++ Mac/BuildScript/build-installer.py Sun Jan 03 22:31:08 2010 -0800 @@ -40,16 +40,19 @@ if ln.startswith(variable): value = ln[len(variable):].strip() return value[1:-1] + raise RuntimeError, "Cannot find variable %s" % variable[:-1] def getVersion(): return grepValue(os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION') +def getVersionTuple(): + return tuple([int(n) for n in getVersion().split('.')]) + def 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??" # The directory we'll use to create the build (will be erased and recreated) @@ -61,12 +64,33 @@ DEPSRC = os.path.expanduser('~/Universal/other-sources') # Location of the preferred SDK + +### There are some issues with the SDK selection below here, +### The resulting binary doesn't work on all platforms that +### it should. Always default to the 10.4u SDK until that +### isue is resolved. +### +##if int(os.uname()[2].split('.')[0]) == 8: +## # Explicitly use the 10.4u (universal) SDK when +## # building on 10.4, the system headers are not +## # useable for a universal build +## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk" +##else: +## SDKPATH = "/" + SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk" -#SDKPATH = "/" universal_opts_map = { '32-bit': ('i386', 'ppc',), '64-bit': ('x86_64', 'ppc64',), - 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) } + 'intel': ('i386', 'x86_64'), + '3-way': ('ppc', 'i386', 'x86_64'), + 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) } +default_target_map = { + '64-bit': '10.5', + '3-way': '10.5', + 'intel': '10.5', + 'all': '10.5', +} UNIVERSALOPTS = tuple(universal_opts_map.keys()) @@ -84,6 +108,17 @@ # $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level 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', +} + +CC = target_cc_map[DEPTARGET] + +PYTHON_3 = getVersionTuple() >= (3, 0) + USAGE = textwrap.dedent("""\ Usage: build_python [options] @@ -104,177 +139,203 @@ # [The recipes are defined here for convenience but instantiated later after # command line options have been processed.] def library_recipes(): - return [ + result = [] + + if DEPTARGET < '10.5': + result.extend([ + dict( + name="Bzip2 1.0.5", + url="http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz", + checksum='3c15a0c8d1d3ee1c46a1634d00617b1a', + configure=None, + install='make install CC=%s PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%( + CC, + shellQuote(os.path.join(WORKDIR, 'libraries')), + ' -arch '.join(ARCHLIST), + SDKPATH, + ), + ), + dict( + name="ZLib 1.2.3", + 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, + shellQuote(os.path.join(WORKDIR, 'libraries')), + ' -arch '.join(ARCHLIST), + SDKPATH, + ), + ), + dict( + # Note that GNU readline is GPL'd software + name="GNU Readline 5.1.4", + url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" , + checksum='7ee5a692db88b30ca48927a13fd60e46', + patchlevel='0', + 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-5.1-patches/readline51-001', + 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002', + 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003', + 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004', + ] + ), + dict( + name="SQLite 3.6.11", + url="http://www.sqlite.org/sqlite-3.6.11.tar.gz", + checksum='7ebb099696ab76cc6ff65dd496d17858', + configure_pre=[ + '--enable-threadsafe', + '--enable-tempstore', + '--enable-shared=no', + '--enable-static=yes', + '--disable-tcl', + ] + ), + dict( + name="NCurses 5.5", + url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz", + checksum='e73c1ac10b4bfc46db43b2ddfd6244ef', + configure_pre=[ + "--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(), + ), + ), + ]) + + result.extend([ dict( - name="Bzip2 1.0.4", - url="http://www.bzip.org/1.0.4/bzip2-1.0.4.tar.gz", - checksum='fc310b254f6ba5fbb5da018f04533688', - configure=None, - install='make install PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%( - shellQuote(os.path.join(WORKDIR, 'libraries')), - ' -arch '.join(ARCHLIST), - SDKPATH, - ), - ), - dict( - name="ZLib 1.2.3", - url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz", - checksum='debc62758716a169df9f62e6ab2bc634', - configure=None, - install='make install prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%( - shellQuote(os.path.join(WORKDIR, 'libraries')), - ' -arch '.join(ARCHLIST), - SDKPATH, - ), - ), - dict( - # Note that GNU readline is GPL'd software - name="GNU Readline 5.1.4", - url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" , - checksum='7ee5a692db88b30ca48927a13fd60e46', - patchlevel='0', - 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-5.1-patches/readline51-001', - 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002', - 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003', - 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004', + name="Sleepycat DB 4.7.25", + url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz", + checksum='ec2b87e833779681a0c3a814aa71359e', + buildDir="build_unix", + configure="../dist/configure", + configure_pre=[ + '--includedir=/usr/local/include/db4', ] ), + ]) - dict( - name="SQLite 3.6.11", - url="http://www.sqlite.org/sqlite-3.6.11.tar.gz", - checksum='7ebb099696ab76cc6ff65dd496d17858', - configure_pre=[ - '--enable-threadsafe', - '--enable-tempstore', - '--enable-shared=no', - '--enable-static=yes', - '--disable-tcl', - ] - ), + return result - dict( - name="NCurses 5.5", - url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz", - checksum='e73c1ac10b4bfc46db43b2ddfd6244ef', - configure_pre=[ - "--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(), - ), - ), -] # Instructions for building packages inside the .mpkg. -PKG_RECIPES = [ - dict( - name="PythonFramework", - long_name="Python Framework", - source="/Library/Frameworks/Python.framework", - readme="""\ - This package installs Python.framework, that is the python - interpreter and the standard library. This also includes Python - wrappers for lots of Mac OS X API's. - """, - postflight="scripts/postflight.framework", - selected='selected', - ), - dict( - name="PythonApplications", - long_name="GUI Applications", - source="/Applications/Python %(VER)s", - readme="""\ - This package installs IDLE (an interactive Python IDE), - Python Launcher and Build Applet (create application bundles - from python scripts). +def pkg_recipes(): + unselected_for_python3 = ('selected', 'unselected')[PYTHON_3] + result = [ + dict( + name="PythonFramework", + long_name="Python Framework", + source="/Library/Frameworks/Python.framework", + readme="""\ + This package installs Python.framework, that is the python + interpreter and the standard library. This also includes Python + wrappers for lots of Mac OS X API's. + """, + postflight="scripts/postflight.framework", + selected='selected', + ), + dict( + name="PythonApplications", + long_name="GUI Applications", + source="/Applications/Python %(VER)s", + readme="""\ + This package installs IDLE (an interactive Python IDE), + Python Launcher and Build Applet (create application bundles + from python scripts). - It also installs a number of examples and demos. - """, - required=False, - selected='selected', - ), - dict( - name="PythonUnixTools", - long_name="UNIX command-line tools", - source="/usr/local/bin", - readme="""\ - This package installs the unix tools in /usr/local/bin for - compatibility with older releases of Python. This package - is not necessary to use Python. - """, - required=False, - selected='selected', - ), - dict( - name="PythonDocumentation", - long_name="Python Documentation", - topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation", - 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 - """, - postflight="scripts/postflight.documentation", - required=False, - selected='selected', - ), - dict( - name="PythonProfileChanges", - long_name="Shell profile updater", - readme="""\ - This packages updates your shell profile to make sure that - the Python tools are found by your shell in preference of - the system provided Python tools. + It also installs a number of examples and demos. + """, + required=False, + selected='selected', + ), + dict( + name="PythonUnixTools", + long_name="UNIX command-line tools", + source="/usr/local/bin", + readme="""\ + This package installs the unix tools in /usr/local/bin for + compatibility with older releases of Python. This package + is not necessary to use Python. + """, + required=False, + selected='selected', + ), + dict( + name="PythonDocumentation", + long_name="Python Documentation", + topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation", + 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 + """, + postflight="scripts/postflight.documentation", + required=False, + selected='selected', + ), + dict( + name="PythonProfileChanges", + long_name="Shell profile updater", + readme="""\ + This packages updates your shell profile to make sure that + the Python tools are found by your shell in preference of + the system provided Python tools. - If you don't install this package you'll have to add - "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin" - to your PATH by hand. - """, - postflight="scripts/postflight.patch-profile", - topdir="/Library/Frameworks/Python.framework", - source="/empty-dir", - required=False, - selected='unselected', - ), - dict( - name="PythonSystemFixes", - long_name="Fix system Python", - readme="""\ - This package updates the system python installation on - Mac OS X 10.3 to ensure that you can build new python extensions - using that copy of python after installing this version. - """, - postflight="../Tools/fixapplepython23.py", - topdir="/Library/Frameworks/Python.framework", - source="/empty-dir", - required=False, - selected='unselected', - ) -] + If you don't install this package you'll have to add + "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin" + to your PATH by hand. + """, + postflight="scripts/postflight.patch-profile", + topdir="/Library/Frameworks/Python.framework", + source="/empty-dir", + required=False, + selected=unselected_for_python3, + ), + ] + + if DEPTARGET < '10.4': + result.append( + dict( + name="PythonSystemFixes", + long_name="Fix system Python", + readme="""\ + This package updates the system python installation on + Mac OS X 10.3 to ensure that you can build new python extensions + using that copy of python after installing this version. + """, + postflight="../Tools/fixapplepython23.py", + topdir="/Library/Frameworks/Python.framework", + source="/empty-dir", + required=False, + selected=unselected_for_python3, + ) + ) + return result def fatal(msg): """ @@ -322,10 +383,10 @@ """ if platform.system() != 'Darwin': - fatal("This script should be run on a Mac OS X 10.4 system") + fatal("This script should be run on a Mac OS X 10.4 (or later) system") - if platform.release() <= '8.': - fatal("This script should be run on a Mac OS X 10.4 system") + if int(platform.release().split('.')[0]) < 8: + fatal("This script should be run on a Mac OS X 10.4 (or later) system") if not os.path.exists(SDKPATH): fatal("Please install the latest version of Xcode and the %s SDK"%( @@ -338,7 +399,7 @@ Parse arguments and update global settings. """ global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET - global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST + global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC if args is None: args = sys.argv[1:] @@ -355,6 +416,7 @@ print "Additional arguments" sys.exit(1) + deptarget = None for k, v in options: if k in ('-h', '-?', '--help'): print USAGE @@ -374,11 +436,16 @@ elif k in ('--dep-target', ): DEPTARGET=v + deptarget=v elif k in ('--universal-archs', ): if v in UNIVERSALOPTS: UNIVERSALARCHS = v ARCHLIST = universal_opts_map[UNIVERSALARCHS] + if deptarget is None: + # Select alternate default deployment + # target + DEPTARGET = default_target_map.get(v, '10.3') else: raise NotImplementedError, v @@ -390,6 +457,8 @@ SDKPATH=os.path.abspath(SDKPATH) DEPSRC=os.path.abspath(DEPSRC) + CC=target_cc_map[DEPTARGET] + print "Settings:" print " * Source directory:", SRCDIR print " * Build directory: ", WORKDIR @@ -397,6 +466,7 @@ print " * Third-party source:", DEPSRC print " * Deployment target:", DEPTARGET print " * Universal architectures:", ARCHLIST + print " * C compiler:", CC print "" @@ -614,8 +684,8 @@ runCommand('make update') runCommand('make html') os.chdir(curDir) - if os.path.exists(docdir): - os.rmdir(docdir) + if not os.path.exists(docdir): + os.mkdir(docdir) os.rename(os.path.join(buildDir, 'build', 'html'), docdir) @@ -650,20 +720,22 @@ 'libraries', 'usr', 'local', 'lib') print "Running configure..." runCommand("%s -C --enable-framework --enable-universalsdk=%s " - "--with-universal-archs=%s --with-computed-gotos " + "--with-universal-archs=%s " + "%s " "LDFLAGS='-g -L%s/libraries/usr/local/lib' " "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%( shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH), UNIVERSALARCHS, + (' ', '--with-computed-gotos ')[PYTHON_3], shellQuote(WORKDIR)[1:-1], shellQuote(WORKDIR)[1:-1])) print "Running make" runCommand("make") - print "Running make frameworkinstall" + print "Running make install" runCommand("make install DESTDIR=%s"%( - shellQuote(rootDir))) + shellQuote(rootDir))) print "Running make frameworkinstallextras" runCommand("make frameworkinstallextras DESTDIR=%s"%( @@ -685,8 +757,6 @@ frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework') gid = grp.getgrnam('admin').gr_gid - - for dirpath, dirnames, filenames in os.walk(frmDir): for dn in dirnames: os.chmod(os.path.join(dirpath, dn), 0775) @@ -733,12 +803,11 @@ os.chdir(curdir) - # Remove the 'Current' link, that way we don't accidently mess with an already installed - # version of python - os.unlink(os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework', 'Versions', 'Current')) - - - + if PYTHON_3: + # Remove the 'Current' link, that way we don't accidently mess + # with an already installed version of python 2 + os.unlink(os.path.join(rootDir, 'Library', 'Frameworks', + 'Python.framework', 'Versions', 'Current')) def patchFile(inPath, outPath): data = fileContents(inPath) @@ -872,9 +941,9 @@ IFPkgFlagPackageList=[ dict( IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()), - IFPkgFlagPackageSelection=item['selected'], + IFPkgFlagPackageSelection=item.get('selected', 'selected'), ) - for item in PKG_RECIPES + for item in pkg_recipes() ], IFPkgFormatVersion=0.10000000149011612, IFPkgFlagBackgroundScaling="proportional", @@ -901,7 +970,7 @@ pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents') pkgcontents = os.path.join(pkgroot, 'Packages') os.makedirs(pkgcontents) - for recipe in PKG_RECIPES: + for recipe in pkg_recipes(): packageFromRecipe(pkgcontents, recipe) rsrcDir = os.path.join(pkgroot, 'Resources') @@ -949,9 +1018,9 @@ shutil.rmtree(outdir) imagepath = os.path.join(outdir, - 'python-%s-macosx'%(getFullVersion(),)) + 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET)) if INCLUDE_TIMESTAMP: - imagepath = imagepath + '%04d-%02d-%02d'%(time.localtime()[:3]) + imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3]) imagepath = imagepath + '.dmg' os.mkdir(outdir) @@ -1009,6 +1078,7 @@ checkEnvironment() os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET + os.environ['CC'] = CC if os.path.exists(WORKDIR): shutil.rmtree(WORKDIR) @@ -1055,11 +1125,8 @@ print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos fp.close() - - # And copy it to a DMG buildDMG() - if __name__ == "__main__": main()