blob: 3e728e095524dea84e2942a297153f7fb526ab0e [file] [log] [blame]
Ned Deilye1c9794952013-01-29 00:07:46 -08001#!/usr/bin/env python
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00002"""
Ned Deily53c460d2011-01-30 01:43:40 +00003This script is used to build "official" universal installers on Mac OS X.
Ned Deilye1c9794952013-01-29 00:07:46 -08004It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
Ned Deily53c460d2011-01-30 01:43:40 +0000532-bit builds. 64-bit or four-way universal builds require at least
6OS X 10.5 and the 10.5 SDK.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00007
Ned Deily53c460d2011-01-30 01:43:40 +00008Please ensure that this script keeps working with Python 2.5, to avoid
9bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Sphinx,
10which is used to build the documentation, currently requires at least
11Python 2.4.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000012
Ned Deilye1c9794952013-01-29 00:07:46 -080013In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script
14requires an installed version of hg and a third-party version of
15Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5
16(for 10.6 or later) installed in /Library/Frameworks. When installed,
17the Python built by this script will attempt to dynamically link first to
18Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
19back to the ones in /System/Library/Framework. For the build, we recommend
20installing the most recent ActiveTcl 8.4 or 8.5 version.
21
2232-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5
23and the installation of additional components, such as a newer Python
24(2.5 is needed for Python parser updates), hg, and svn (for the documentation
25build).
26
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000027Usage: see USAGE variable in the script.
28"""
Ned Deilye1c9794952013-01-29 00:07:46 -080029import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
30try:
31 import urllib2 as urllib_request
32except ImportError:
33 import urllib.request as urllib_request
34
35STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
36 | stat.S_IRGRP | stat.S_IXGRP
37 | stat.S_IROTH | stat.S_IXOTH )
38
39STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
40 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
41 | stat.S_IROTH | stat.S_IXOTH )
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000042
Ronald Oussoren158ad592006-11-07 16:00:34 +000043INCLUDE_TIMESTAMP = 1
44VERBOSE = 1
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000045
46from plistlib import Plist
47
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000048try:
49 from plistlib import writePlist
50except ImportError:
51 # We're run using python2.3
52 def writePlist(plist, path):
53 plist.write(path)
54
55def shellQuote(value):
56 """
Ronald Oussorenaa560962006-11-07 15:53:38 +000057 Return the string value in a form that can safely be inserted into
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000058 a shell command.
59 """
60 return "'%s'"%(value.replace("'", "'\"'\"'"))
61
62def grepValue(fn, variable):
63 variable = variable + '='
64 for ln in open(fn, 'r'):
65 if ln.startswith(variable):
66 value = ln[len(variable):].strip()
67 return value[1:-1]
Ned Deilye1c9794952013-01-29 00:07:46 -080068 raise RuntimeError("Cannot find variable %s" % variable[:-1])
69
70_cache_getVersion = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000071
72def getVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080073 global _cache_getVersion
74 if _cache_getVersion is None:
75 _cache_getVersion = grepValue(
76 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
77 return _cache_getVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000078
Ned Deily53c460d2011-01-30 01:43:40 +000079def getVersionTuple():
80 return tuple([int(n) for n in getVersion().split('.')])
81
Ned Deilye1c9794952013-01-29 00:07:46 -080082def getVersionMajorMinor():
83 return tuple([int(n) for n in getVersion().split('.', 2)])
84
85_cache_getFullVersion = None
86
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000087def getFullVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080088 global _cache_getFullVersion
89 if _cache_getFullVersion is not None:
90 return _cache_getFullVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000091 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
92 for ln in open(fn):
93 if 'PY_VERSION' in ln:
Ned Deilye1c9794952013-01-29 00:07:46 -080094 _cache_getFullVersion = ln.split()[-1][1:-1]
95 return _cache_getFullVersion
96 raise RuntimeError("Cannot find full version??")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000097
Ronald Oussorenaa560962006-11-07 15:53:38 +000098# The directory we'll use to create the build (will be erased and recreated)
Ronald Oussoren158ad592006-11-07 16:00:34 +000099WORKDIR = "/tmp/_py"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000100
Ronald Oussorenaa560962006-11-07 15:53:38 +0000101# The directory we'll use to store third-party sources. Set this to something
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000102# else if you don't want to re-fetch required libraries every time.
Ronald Oussoren158ad592006-11-07 16:00:34 +0000103DEPSRC = os.path.join(WORKDIR, 'third-party')
104DEPSRC = os.path.expanduser('~/Universal/other-sources')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000105
106# Location of the preferred SDK
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000107
108### There are some issues with the SDK selection below here,
109### The resulting binary doesn't work on all platforms that
110### it should. Always default to the 10.4u SDK until that
Ezio Melotti6d0f0f22013-08-26 01:31:30 +0300111### issue is resolved.
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000112###
113##if int(os.uname()[2].split('.')[0]) == 8:
114## # Explicitly use the 10.4u (universal) SDK when
115## # building on 10.4, the system headers are not
116## # useable for a universal build
117## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
118##else:
119## SDKPATH = "/"
120
121SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000122
Ronald Oussoren508282e2009-03-30 19:34:51 +0000123universal_opts_map = { '32-bit': ('i386', 'ppc',),
124 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000125 'intel': ('i386', 'x86_64'),
126 '3-way': ('ppc', 'i386', 'x86_64'),
127 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
128default_target_map = {
129 '64-bit': '10.5',
130 '3-way': '10.5',
131 'intel': '10.5',
132 'all': '10.5',
133}
Ronald Oussoren508282e2009-03-30 19:34:51 +0000134
135UNIVERSALOPTS = tuple(universal_opts_map.keys())
136
137UNIVERSALARCHS = '32-bit'
138
139ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000140
Ezio Melotti24b07bc2011-03-15 18:55:01 +0200141# Source directory (assume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000142SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000143 os.path.dirname(
144 os.path.dirname(
145 os.path.abspath(__file__
146 ))))
147
Ronald Oussoren508282e2009-03-30 19:34:51 +0000148# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
149DEPTARGET = '10.3'
150
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000151target_cc_map = {
Ned Deilye1c9794952013-01-29 00:07:46 -0800152 '10.3': ('gcc-4.0', 'g++-4.0'),
153 '10.4': ('gcc-4.0', 'g++-4.0'),
154 '10.5': ('gcc-4.2', 'g++-4.2'),
155 '10.6': ('gcc-4.2', 'g++-4.2'),
156 '10.7': ('clang', 'clang++'),
157 '10.8': ('clang', 'clang++'),
Ned Deily11f880a2013-10-18 20:40:23 -0700158 '10.9': ('clang', 'clang++'),
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000159}
160
Ned Deilye1c9794952013-01-29 00:07:46 -0800161CC, CXX = target_cc_map[DEPTARGET]
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000162
Ned Deily53c460d2011-01-30 01:43:40 +0000163PYTHON_3 = getVersionTuple() >= (3, 0)
164
Ronald Oussoren158ad592006-11-07 16:00:34 +0000165USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000166 Usage: build_python [options]
167
168 Options:
169 -? or -h: Show this message
170 -b DIR
171 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
172 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
173 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
174 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000175 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
176 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000177""")% globals()
178
Ned Deilye1c9794952013-01-29 00:07:46 -0800179# Dict of object file names with shared library names to check after building.
180# This is to ensure that we ended up dynamically linking with the shared
181# library paths and versions we expected. For example:
182# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
183# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
184# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
185EXPECTED_SHARED_LIBS = {}
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000186
187# Instructions for building libraries that are necessary for building a
188# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000189# [The recipes are defined here for convenience but instantiated later after
190# command line options have been processed.]
191def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000192 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000193
Ned Deilye1c9794952013-01-29 00:07:46 -0800194 LT_10_5 = bool(DEPTARGET < '10.5')
195
Ned Deily0203a802013-10-25 00:40:07 -0700196 if DEPTARGET > '10.5':
197 result.extend([
198 dict(
199 name="Tcl 8.5.15",
200 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tcl8.5.15-src.tar.gz",
201 checksum='f3df162f92c69b254079c4d0af7a690f',
202 buildDir="unix",
203 configure_pre=[
204 '--enable-shared',
205 '--enable-threads',
206 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
207 ],
208 useLDFlags=False,
209 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
210 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
211 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
212 },
213 ),
214 dict(
215 name="Tk 8.5.15",
216 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tk8.5.15-src.tar.gz",
217 checksum='55b8e33f903210a4e1c8bce0f820657f',
218 buildDir="unix",
219 configure_pre=[
220 '--enable-aqua',
221 '--enable-shared',
222 '--enable-threads',
223 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
224 ],
225 useLDFlags=False,
226 install='make TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
227 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
228 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
229 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.5'%(getVersion())),
230 },
231 ),
232 ])
233
Ned Deilye1c9794952013-01-29 00:07:46 -0800234 if getVersionTuple() >= (3, 3):
235 result.extend([
236 dict(
237 name="XZ 5.0.3",
238 url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
239 checksum='fefe52f9ecd521de2a8ce38c21a27574',
240 configure_pre=[
241 '--disable-dependency-tracking',
242 ]
243 ),
244 ])
245
246 result.extend([
247 dict(
248 name="NCurses 5.9",
249 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
250 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
251 configure_pre=[
252 "--enable-widec",
253 "--without-cxx",
254 "--without-cxx-binding",
255 "--without-ada",
256 "--without-curses-h",
257 "--enable-shared",
258 "--with-shared",
259 "--without-debug",
260 "--without-normal",
261 "--without-tests",
262 "--without-manpages",
263 "--datadir=/usr/share",
264 "--sysconfdir=/etc",
265 "--sharedstatedir=/usr/com",
266 "--with-terminfo-dirs=/usr/share/terminfo",
267 "--with-default-terminfo-dir=/usr/share/terminfo",
268 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
269 ],
270 patchscripts=[
271 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
272 "f54bf02a349f96a7c4f0d00922f3a0d4"),
273 ],
274 useLDFlags=False,
275 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
276 shellQuote(os.path.join(WORKDIR, 'libraries')),
277 shellQuote(os.path.join(WORKDIR, 'libraries')),
278 getVersion(),
279 ),
280 ),
281 dict(
282 name="SQLite 3.7.13",
283 url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
284 checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
285 extra_cflags=('-Os '
286 '-DSQLITE_ENABLE_FTS4 '
287 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
288 '-DSQLITE_ENABLE_RTREE '
289 '-DSQLITE_TCL=0 '
290 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
291 configure_pre=[
292 '--enable-threadsafe',
293 '--enable-shared=no',
294 '--enable-static=yes',
295 '--disable-readline',
296 '--disable-dependency-tracking',
297 ]
298 ),
299 ])
300
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000301 if DEPTARGET < '10.5':
302 result.extend([
303 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000304 name="Bzip2 1.0.6",
305 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
306 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000307 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800308 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
309 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000310 shellQuote(os.path.join(WORKDIR, 'libraries')),
311 ' -arch '.join(ARCHLIST),
312 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000313 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000314 ),
315 dict(
316 name="ZLib 1.2.3",
317 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
318 checksum='debc62758716a169df9f62e6ab2bc634',
319 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800320 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
321 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000322 shellQuote(os.path.join(WORKDIR, 'libraries')),
323 ' -arch '.join(ARCHLIST),
324 SDKPATH,
325 ),
326 ),
327 dict(
328 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000329 name="GNU Readline 6.1.2",
330 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
331 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000332 patchlevel='0',
333 patches=[
334 # The readline maintainers don't do actual micro releases, but
335 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800336 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
337 'c642f2e84d820884b0bf9fd176bc6c3f'),
338 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
339 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000340 ]
341 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000342 ])
343
Ned Deily53c460d2011-01-30 01:43:40 +0000344 if not PYTHON_3:
345 result.extend([
346 dict(
347 name="Sleepycat DB 4.7.25",
348 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
349 checksum='ec2b87e833779681a0c3a814aa71359e',
350 buildDir="build_unix",
351 configure="../dist/configure",
352 configure_pre=[
353 '--includedir=/usr/local/include/db4',
354 ]
355 ),
356 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000357
358 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000359
360
361# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000362def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000363 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000364 result = [
365 dict(
366 name="PythonFramework",
367 long_name="Python Framework",
368 source="/Library/Frameworks/Python.framework",
369 readme="""\
370 This package installs Python.framework, that is the python
371 interpreter and the standard library. This also includes Python
372 wrappers for lots of Mac OS X API's.
373 """,
374 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000375 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000376 ),
377 dict(
378 name="PythonApplications",
379 long_name="GUI Applications",
380 source="/Applications/Python %(VER)s",
381 readme="""\
382 This package installs IDLE (an interactive Python IDE),
383 Python Launcher and Build Applet (create application bundles
384 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000385
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000386 It also installs a number of examples and demos.
387 """,
388 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000389 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000390 ),
391 dict(
392 name="PythonUnixTools",
393 long_name="UNIX command-line tools",
394 source="/usr/local/bin",
395 readme="""\
396 This package installs the unix tools in /usr/local/bin for
397 compatibility with older releases of Python. This package
398 is not necessary to use Python.
399 """,
400 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000401 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000402 ),
403 dict(
404 name="PythonDocumentation",
405 long_name="Python Documentation",
406 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
407 source="/pydocs",
408 readme="""\
409 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800410 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000411 """,
412 postflight="scripts/postflight.documentation",
413 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000414 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000415 ),
416 dict(
417 name="PythonProfileChanges",
418 long_name="Shell profile updater",
419 readme="""\
420 This packages updates your shell profile to make sure that
421 the Python tools are found by your shell in preference of
422 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000423
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000424 If you don't install this package you'll have to add
425 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
426 to your PATH by hand.
427 """,
428 postflight="scripts/postflight.patch-profile",
429 topdir="/Library/Frameworks/Python.framework",
430 source="/empty-dir",
431 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000432 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000433 ),
434 ]
435
Ned Deilye1c9794952013-01-29 00:07:46 -0800436 if DEPTARGET < '10.4' and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000437 result.append(
438 dict(
439 name="PythonSystemFixes",
440 long_name="Fix system Python",
441 readme="""\
442 This package updates the system python installation on
443 Mac OS X 10.3 to ensure that you can build new python extensions
444 using that copy of python after installing this version.
445 """,
446 postflight="../Tools/fixapplepython23.py",
447 topdir="/Library/Frameworks/Python.framework",
448 source="/empty-dir",
449 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000450 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000451 )
452 )
453 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000454
455def fatal(msg):
456 """
457 A fatal error, bail out.
458 """
459 sys.stderr.write('FATAL: ')
460 sys.stderr.write(msg)
461 sys.stderr.write('\n')
462 sys.exit(1)
463
464def fileContents(fn):
465 """
466 Return the contents of the named file
467 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800468 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000469
470def runCommand(commandline):
471 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200472 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000473 unless the command fails.
474 """
475 fd = os.popen(commandline, 'r')
476 data = fd.read()
477 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000478 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000479 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800480 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000481
482 if VERBOSE:
483 sys.stdout.write(data); sys.stdout.flush()
484
485def captureCommand(commandline):
486 fd = os.popen(commandline, 'r')
487 data = fd.read()
488 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000489 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000490 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800491 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000492
493 return data
494
Ronald Oussoren287128a2010-04-18 14:01:05 +0000495def getTclTkVersion(configfile, versionline):
496 """
497 search Tcl or Tk configuration file for version line
498 """
499 try:
500 f = open(configfile, "r")
501 except:
502 fatal("Framework configuration file not found: %s" % configfile)
503
504 for l in f:
505 if l.startswith(versionline):
506 f.close()
507 return l
508
509 fatal("Version variable %s not found in framework configuration file: %s"
510 % (versionline, configfile))
511
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000512def checkEnvironment():
513 """
514 Check that we're running on a supported system.
515 """
516
Ned Deily53c460d2011-01-30 01:43:40 +0000517 if sys.version_info[0:2] < (2, 4):
518 fatal("This script must be run with Python 2.4 or later")
519
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000520 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000521 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000522
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000523 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000524 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000525
526 if not os.path.exists(SDKPATH):
527 fatal("Please install the latest version of Xcode and the %s SDK"%(
528 os.path.basename(SDKPATH[:-4])))
529
Ronald Oussoren287128a2010-04-18 14:01:05 +0000530 # Because we only support dynamic load of only one major/minor version of
531 # Tcl/Tk, ensure:
532 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800533 # higher than the Apple-supplied system version in
534 # SDKROOT/System/Library/Frameworks
535 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
536 # in) SDKROOT/Library/Frameworks with the same version as the system
537 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000538
Ned Deilye1c9794952013-01-29 00:07:46 -0800539 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000540 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800541 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000542 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800543 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000544 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800545 frameworks[framework] = os.readlink(sysfw)
546 if not os.path.exists(libfw):
547 fatal("Please install a link to a current %s %s as %s so "
548 "the user can override the system framework."
549 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000550 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000551 fatal("Version of %s must match %s" % (libfw, sysfw) )
552 if os.path.exists(usrfw):
553 fatal("Please rename %s to avoid possible dynamic load issues."
554 % usrfw)
555
Ned Deilye1c9794952013-01-29 00:07:46 -0800556 if frameworks['Tcl'] != frameworks['Tk']:
557 fatal("The Tcl and Tk frameworks are not the same version.")
558
559 # add files to check after build
560 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
561 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
562 % frameworks['Tcl'],
563 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
564 % frameworks['Tk'],
565 ]
566
Ned Deily0203a802013-10-25 00:40:07 -0700567 # For 10.6+ builds, we build two versions of _tkinter:
568 # - the traditional version (renamed to _tkinter.so.framework) linked
569 # with /Library/Frameworks/{Tcl,Tk}.framework
570 # - the default version linked with our private copies of Tcl and Tk
571 if DEPTARGET > '10.5':
572 EXPECTED_SHARED_LIBS['_tkinter.so.framework'] = \
573 EXPECTED_SHARED_LIBS['_tkinter.so']
574 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
575 "/Library/Frameworks/Python.framework/Versions/%s/lib/libtcl%s.dylib"
576 % (getVersion(), frameworks['Tcl']),
577 "/Library/Frameworks/Python.framework/Versions/%s/lib/libtk%s.dylib"
578 % (getVersion(), frameworks['Tk']),
579 ]
580
Ronald Oussoren287128a2010-04-18 14:01:05 +0000581 # Remove inherited environment variables which might influence build
582 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
583 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
584 for ev in list(os.environ):
585 for prefix in environ_var_prefixes:
586 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800587 print("INFO: deleting environment variable %s=%s" % (
588 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000589 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000590
Ned Deilye1c9794952013-01-29 00:07:46 -0800591 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
592 if 'SDK_TOOLS_BIN' in os.environ:
593 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
594 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
595 # add its fixed location here if it exists
596 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
597 if os.path.isdir(OLD_DEVELOPER_TOOLS):
598 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
599 os.environ['PATH'] = base_path
600 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussorenac4b7ad2010-04-20 05:50:44 +0000601
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000602
Ronald Oussoren158ad592006-11-07 16:00:34 +0000603def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000604 """
605 Parse arguments and update global settings.
606 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000607 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800608 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000609
610 if args is None:
611 args = sys.argv[1:]
612
613 try:
614 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000615 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
616 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800617 except getopt.GetoptError:
618 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000619 sys.exit(1)
620
621 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800622 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000623 sys.exit(1)
624
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000625 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000626 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000627 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800628 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000629 sys.exit(0)
630
631 elif k in ('-d', '--build-dir'):
632 WORKDIR=v
633
634 elif k in ('--third-party',):
635 DEPSRC=v
636
637 elif k in ('--sdk-path',):
638 SDKPATH=v
639
640 elif k in ('--src-dir',):
641 SRCDIR=v
642
Ronald Oussoren508282e2009-03-30 19:34:51 +0000643 elif k in ('--dep-target', ):
644 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000645 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000646
647 elif k in ('--universal-archs', ):
648 if v in UNIVERSALOPTS:
649 UNIVERSALARCHS = v
650 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000651 if deptarget is None:
652 # Select alternate default deployment
653 # target
654 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000655 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800656 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000657
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000658 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800659 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000660
661 SRCDIR=os.path.abspath(SRCDIR)
662 WORKDIR=os.path.abspath(WORKDIR)
663 SDKPATH=os.path.abspath(SDKPATH)
664 DEPSRC=os.path.abspath(DEPSRC)
665
Ned Deilye1c9794952013-01-29 00:07:46 -0800666 CC, CXX=target_cc_map[DEPTARGET]
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000667
Ned Deilye1c9794952013-01-29 00:07:46 -0800668 print("Settings:")
669 print(" * Source directory:", SRCDIR)
670 print(" * Build directory: ", WORKDIR)
671 print(" * SDK location: ", SDKPATH)
672 print(" * Third-party source:", DEPSRC)
673 print(" * Deployment target:", DEPTARGET)
674 print(" * Universal architectures:", ARCHLIST)
675 print(" * C compiler:", CC)
676 print(" * C++ compiler:", CXX)
677 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000678
679
680
681
682def extractArchive(builddir, archiveName):
683 """
684 Extract a source archive into 'builddir'. Returns the path of the
685 extracted archive.
686
687 XXX: This function assumes that archives contain a toplevel directory
688 that is has the same name as the basename of the archive. This is
Ned Deily0203a802013-10-25 00:40:07 -0700689 safe enough for almost anything we use. Unfortunately, it does not
690 work for current Tcl and Tk source releases where the basename of
691 the archive ends with "-src" but the uncompressed directory does not.
692 For now, just special case Tcl and Tk tar.gz downloads.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000693 """
694 curdir = os.getcwd()
695 try:
696 os.chdir(builddir)
697 if archiveName.endswith('.tar.gz'):
698 retval = os.path.basename(archiveName[:-7])
Ned Deily0203a802013-10-25 00:40:07 -0700699 if ((retval.startswith('tcl') or retval.startswith('tk'))
700 and retval.endswith('-src')):
701 retval = retval[:-4]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000702 if os.path.exists(retval):
703 shutil.rmtree(retval)
704 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
705
706 elif archiveName.endswith('.tar.bz2'):
707 retval = os.path.basename(archiveName[:-8])
708 if os.path.exists(retval):
709 shutil.rmtree(retval)
710 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
711
712 elif archiveName.endswith('.tar'):
713 retval = os.path.basename(archiveName[:-4])
714 if os.path.exists(retval):
715 shutil.rmtree(retval)
716 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
717
718 elif archiveName.endswith('.zip'):
719 retval = os.path.basename(archiveName[:-4])
720 if os.path.exists(retval):
721 shutil.rmtree(retval)
722 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
723
724 data = fp.read()
725 xit = fp.close()
726 if xit is not None:
727 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800728 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000729
730 return os.path.join(builddir, retval)
731
732 finally:
733 os.chdir(curdir)
734
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000735def downloadURL(url, fname):
736 """
737 Download the contents of the url into the file.
738 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800739 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000740 fpOut = open(fname, 'wb')
741 block = fpIn.read(10240)
742 try:
743 while block:
744 fpOut.write(block)
745 block = fpIn.read(10240)
746 fpIn.close()
747 fpOut.close()
748 except:
749 try:
750 os.unlink(fname)
751 except:
752 pass
753
Ned Deilye1c9794952013-01-29 00:07:46 -0800754def verifyThirdPartyFile(url, checksum, fname):
755 """
756 Download file from url to filename fname if it does not already exist.
757 Abort if file contents does not match supplied md5 checksum.
758 """
759 name = os.path.basename(fname)
760 if os.path.exists(fname):
761 print("Using local copy of %s"%(name,))
762 else:
763 print("Did not find local copy of %s"%(name,))
764 print("Downloading %s"%(name,))
765 downloadURL(url, fname)
766 print("Archive for %s stored as %s"%(name, fname))
767 if os.system(
768 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
769 % (shellQuote(fname), checksum) ):
770 fatal('MD5 checksum mismatch for file %s' % fname)
771
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000772def buildRecipe(recipe, basedir, archList):
773 """
774 Build software using a recipe. This function does the
775 'configure;make;make install' dance for C software, with a possibility
776 to customize this process, basically a poor-mans DarwinPorts.
777 """
778 curdir = os.getcwd()
779
780 name = recipe['name']
781 url = recipe['url']
782 configure = recipe.get('configure', './configure')
783 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
784 shellQuote(basedir)))
785
786 archiveName = os.path.split(url)[-1]
787 sourceArchive = os.path.join(DEPSRC, archiveName)
788
789 if not os.path.exists(DEPSRC):
790 os.mkdir(DEPSRC)
791
Ned Deilye1c9794952013-01-29 00:07:46 -0800792 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
793 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000794 buildDir=os.path.join(WORKDIR, '_bld')
795 if not os.path.exists(buildDir):
796 os.mkdir(buildDir)
797
798 workDir = extractArchive(buildDir, sourceArchive)
799 os.chdir(workDir)
800 if 'buildDir' in recipe:
801 os.chdir(recipe['buildDir'])
802
Ned Deilye1c9794952013-01-29 00:07:46 -0800803 for patch in recipe.get('patches', ()):
804 if isinstance(patch, tuple):
805 url, checksum = patch
806 fn = os.path.join(DEPSRC, os.path.basename(url))
807 verifyThirdPartyFile(url, checksum, fn)
808 else:
809 # patch is a file in the source directory
810 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000811 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
812 shellQuote(fn),))
813
Ned Deilye1c9794952013-01-29 00:07:46 -0800814 for patchscript in recipe.get('patchscripts', ()):
815 if isinstance(patchscript, tuple):
816 url, checksum = patchscript
817 fn = os.path.join(DEPSRC, os.path.basename(url))
818 verifyThirdPartyFile(url, checksum, fn)
819 else:
820 # patch is a file in the source directory
821 fn = os.path.join(curdir, patchscript)
822 if fn.endswith('.bz2'):
823 runCommand('bunzip2 -fk %s' % shellQuote(fn))
824 fn = fn[:-4]
825 runCommand('sh %s' % shellQuote(fn))
826 os.unlink(fn)
827
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000828 if configure is not None:
829 configure_args = [
830 "--prefix=/usr/local",
831 "--enable-static",
832 "--disable-shared",
833 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
834 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000835
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000836 if 'configure_pre' in recipe:
837 args = list(recipe['configure_pre'])
838 if '--disable-static' in args:
839 configure_args.remove('--enable-static')
840 if '--enable-shared' in args:
841 configure_args.remove('--disable-shared')
842 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000843
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000844 if recipe.get('useLDFlags', 1):
845 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800846 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
847 "-I%s/usr/local/include"%(
848 recipe.get('extra_cflags', ''),
849 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000850 ' -arch '.join(archList),
851 shellQuote(SDKPATH)[1:-1],
852 shellQuote(basedir)[1:-1],),
Ned Deilye1c9794952013-01-29 00:07:46 -0800853 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
854 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000855 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000856 shellQuote(basedir)[1:-1],
857 ' -arch '.join(archList)),
858 ])
859 else:
860 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800861 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
862 "-I%s/usr/local/include"%(
863 recipe.get('extra_cflags', ''),
864 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000865 ' -arch '.join(archList),
866 shellQuote(SDKPATH)[1:-1],
867 shellQuote(basedir)[1:-1],),
868 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000869
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000870 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -0800871 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000872
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000873 configure_args.insert(0, configure)
874 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000875
Ned Deilye1c9794952013-01-29 00:07:46 -0800876 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000877 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000878
Ned Deilye1c9794952013-01-29 00:07:46 -0800879 print("Running install for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000880 runCommand('{ ' + install + ' ;} 2>&1')
881
Ned Deilye1c9794952013-01-29 00:07:46 -0800882 print("Done %s"%(name,))
883 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000884
885 os.chdir(curdir)
886
887def buildLibraries():
888 """
889 Build our dependencies into $WORKDIR/libraries/usr/local
890 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800891 print("")
892 print("Building required libraries")
893 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000894 universal = os.path.join(WORKDIR, 'libraries')
895 os.mkdir(universal)
896 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
897 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
898
Ronald Oussoren508282e2009-03-30 19:34:51 +0000899 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000900 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000901
902
903
904def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000905 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000906 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -0800907 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000908 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000909 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000910 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000911 curDir = os.getcwd()
912 os.chdir(buildDir)
913 runCommand('make update')
Martin v. Löwis896c4772010-04-22 11:34:36 +0000914 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000915 os.chdir(curDir)
916 if not os.path.exists(docdir):
917 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000918 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000919
920
921def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -0800922 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000923
924 buildDir = os.path.join(WORKDIR, '_bld', 'python')
925 rootDir = os.path.join(WORKDIR, '_root')
926
927 if os.path.exists(buildDir):
928 shutil.rmtree(buildDir)
929 if os.path.exists(rootDir):
930 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +0000931 os.makedirs(buildDir)
932 os.makedirs(rootDir)
933 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000934 curdir = os.getcwd()
935 os.chdir(buildDir)
936
937 # Not sure if this is still needed, the original build script
938 # claims that parts of the install assume python.exe exists.
939 os.symlink('python', os.path.join(buildDir, 'python.exe'))
940
941 # Extract the version from the configure file, needed to calculate
942 # several paths.
943 version = getVersion()
944
Ronald Oussoren008af852009-03-30 20:02:08 +0000945 # Since the extra libs are not in their installed framework location
946 # during the build, augment the library path so that the interpreter
947 # will find them during its extension import sanity checks.
948 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
949 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -0800950 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +0000951 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
952 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +0000953 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +0000954 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deilyf84b5312013-10-25 00:44:46 -0700955 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000956 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
957 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +0000958 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +0000959 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000960 shellQuote(WORKDIR)[1:-1]))
961
Ned Deilye1c9794952013-01-29 00:07:46 -0800962 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000963 runCommand("make")
964
Ned Deily0203a802013-10-25 00:40:07 -0700965 # For deployment targets of 10.6 and higher, we build our own version
966 # of Tcl and Cocoa Aqua Tk libs because the Apple-supplied Tk 8.5 is
967 # out-of-date and has critical bugs. Save the _tkinter.so that was
968 # linked with /Library/Frameworks/{Tck,Tk}.framework and build
969 # another _tkinter.so linked with our private Tcl and Tk libs.
970 if DEPTARGET > '10.5':
971 runCommand("find build -name '_tkinter.so' "
972 " -execdir mv '{}' '{}'.framework \;")
973 print("Running make to rebuild _tkinter")
974 runCommand("make TCLTK_INCLUDES='-I%s/libraries/usr/local/include' "
975 "TCLTK_LIBS='-L%s/libraries/usr/local/lib -ltcl8.5 -ltk8.5'"%(
976 shellQuote(WORKDIR)[1:-1],
977 shellQuote(WORKDIR)[1:-1]))
978 # make a backup copy, just in case
979 runCommand("find build -name '_tkinter.so' "
980 " -execdir cp -p '{}' '{}'.private \;")
981
Ned Deilye1c9794952013-01-29 00:07:46 -0800982 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +0000983 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000984 shellQuote(rootDir)))
985
Ned Deilye1c9794952013-01-29 00:07:46 -0800986 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000987 runCommand("make frameworkinstallextras DESTDIR=%s"%(
988 shellQuote(rootDir)))
989
Ronald Oussoren008af852009-03-30 20:02:08 +0000990 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -0800991 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000992 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
993 runCommand("mv %s/* %s"%(
994 shellQuote(os.path.join(
995 WORKDIR, 'libraries', 'Library', 'Frameworks',
996 'Python.framework', 'Versions', getVersion(),
997 'lib')),
998 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
999 'Python.framework', 'Versions', getVersion(),
1000 'lib'))))
1001
Ned Deilye1c9794952013-01-29 00:07:46 -08001002 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001003 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001004 gid = grp.getgrnam('admin').gr_gid
1005
Ned Deilye1c9794952013-01-29 00:07:46 -08001006 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001007 for dirpath, dirnames, filenames in os.walk(frmDir):
1008 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -08001009 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001010 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +00001011
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001012 for fn in filenames:
1013 if os.path.islink(fn):
1014 continue
1015
1016 # "chmod g+w $fn"
1017 p = os.path.join(dirpath, fn)
1018 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001019 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1020 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001021
Ned Deilye1c9794952013-01-29 00:07:46 -08001022 if fn in EXPECTED_SHARED_LIBS:
1023 # check to see that this file was linked with the
1024 # expected library path and version
1025 data = captureCommand("otool -L %s" % shellQuote(p))
1026 for sl in EXPECTED_SHARED_LIBS[fn]:
1027 if ("\t%s " % sl) not in data:
1028 print("Expected shared lib %s was not linked with %s"
1029 % (sl, p))
1030 shared_lib_error = True
1031
1032 if shared_lib_error:
1033 fatal("Unexpected shared library errors.")
1034
Ned Deily53c460d2011-01-30 01:43:40 +00001035 if PYTHON_3:
1036 LDVERSION=None
1037 VERSION=None
1038 ABIFLAGS=None
1039
1040 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
1041 for ln in fp:
1042 if ln.startswith('VERSION='):
1043 VERSION=ln.split()[1]
1044 if ln.startswith('ABIFLAGS='):
1045 ABIFLAGS=ln.split()[1]
1046 if ln.startswith('LDVERSION='):
1047 LDVERSION=ln.split()[1]
1048 fp.close()
1049
1050 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1051 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1052 config_suffix = '-' + LDVERSION
1053 else:
1054 config_suffix = '' # Python 2.x
1055
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001056 # We added some directories to the search path during the configure
1057 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -08001058 # the end-users system. Also remove the directories from _sysconfigdata.py
1059 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001060
Ned Deilye1c9794952013-01-29 00:07:46 -08001061 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1062 'Python.framework', 'Versions',
1063 version, 'lib', 'python%s'%(version,))
1064 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
1065 os.path.join(path_to_lib, '_sysconfigdata.py')]
1066 for path in paths:
1067 if not os.path.exists(path):
1068 continue
1069 fp = open(path, 'r')
1070 data = fp.read()
1071 fp.close()
1072
1073 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
1074 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
1075 fp = open(path, 'w')
1076 fp.write(data)
1077 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001078
1079 # Add symlinks in /usr/local/bin, using relative links
1080 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1081 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1082 'Python.framework', 'Versions', version, 'bin')
1083 if os.path.exists(usr_local_bin):
1084 shutil.rmtree(usr_local_bin)
1085 os.makedirs(usr_local_bin)
1086 for fn in os.listdir(
1087 os.path.join(frmDir, 'Versions', version, 'bin')):
1088 os.symlink(os.path.join(to_framework, fn),
1089 os.path.join(usr_local_bin, fn))
1090
1091 os.chdir(curdir)
1092
Ned Deily53c460d2011-01-30 01:43:40 +00001093 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001094 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001095 # with an already installed version of python 2
1096 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1097 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001098
1099def patchFile(inPath, outPath):
1100 data = fileContents(inPath)
1101 data = data.replace('$FULL_VERSION', getFullVersion())
1102 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001103 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001104 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001105 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +00001106
1107 # This one is not handy as a template variable
1108 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001109 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001110 fp.write(data)
1111 fp.close()
1112
1113def patchScript(inPath, outPath):
1114 data = fileContents(inPath)
1115 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001116 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001117 fp.write(data)
1118 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001119 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001120
1121
1122
1123def packageFromRecipe(targetDir, recipe):
1124 curdir = os.getcwd()
1125 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001126 # The major version (such as 2.5) is included in the package name
1127 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001128 # common.
1129 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001130 srcdir = recipe.get('source')
1131 pkgroot = recipe.get('topdir', srcdir)
1132 postflight = recipe.get('postflight')
1133 readme = textwrap.dedent(recipe['readme'])
1134 isRequired = recipe.get('required', True)
1135
Ned Deilye1c9794952013-01-29 00:07:46 -08001136 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001137
1138 # Substitute some variables
1139 textvars = dict(
1140 VER=getVersion(),
1141 FULLVER=getFullVersion(),
1142 )
1143 readme = readme % textvars
1144
1145 if pkgroot is not None:
1146 pkgroot = pkgroot % textvars
1147 else:
1148 pkgroot = '/'
1149
1150 if srcdir is not None:
1151 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1152 srcdir = srcdir % textvars
1153
1154 if postflight is not None:
1155 postflight = os.path.abspath(postflight)
1156
1157 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1158 os.makedirs(packageContents)
1159
1160 if srcdir is not None:
1161 os.chdir(srcdir)
1162 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1163 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1164 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1165
1166 fn = os.path.join(packageContents, 'PkgInfo')
1167 fp = open(fn, 'w')
1168 fp.write('pmkrpkg1')
1169 fp.close()
1170
1171 rsrcDir = os.path.join(packageContents, "Resources")
1172 os.mkdir(rsrcDir)
1173 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1174 fp.write(readme)
1175 fp.close()
1176
1177 if postflight is not None:
1178 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1179
1180 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001181 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001182 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001183 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1184 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1185 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001186 CFBundleShortVersionString=vers,
1187 IFMajorVersion=major,
1188 IFMinorVersion=minor,
1189 IFPkgFormatVersion=0.10000000149011612,
1190 IFPkgFlagAllowBackRev=False,
1191 IFPkgFlagAuthorizationAction="RootAuthorization",
1192 IFPkgFlagDefaultLocation=pkgroot,
1193 IFPkgFlagFollowLinks=True,
1194 IFPkgFlagInstallFat=True,
1195 IFPkgFlagIsRequired=isRequired,
1196 IFPkgFlagOverwritePermissions=False,
1197 IFPkgFlagRelocatable=False,
1198 IFPkgFlagRestartAction="NoRestart",
1199 IFPkgFlagRootVolumeOnly=True,
1200 IFPkgFlagUpdateInstalledLangauges=False,
1201 )
1202 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1203
1204 pl = Plist(
1205 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001206 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001207 IFPkgDescriptionVersion=vers,
1208 )
1209 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1210
1211 finally:
1212 os.chdir(curdir)
1213
1214
1215def makeMpkgPlist(path):
1216
1217 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001218 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001219
1220 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001221 CFBundleGetInfoString="Python %s"%(vers,),
1222 CFBundleIdentifier='org.python.Python',
1223 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001224 CFBundleShortVersionString=vers,
1225 IFMajorVersion=major,
1226 IFMinorVersion=minor,
1227 IFPkgFlagComponentDirectory="Contents/Packages",
1228 IFPkgFlagPackageList=[
1229 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001230 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001231 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001232 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001233 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001234 ],
1235 IFPkgFormatVersion=0.10000000149011612,
1236 IFPkgFlagBackgroundScaling="proportional",
1237 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001238 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001239 )
1240
1241 writePlist(pl, path)
1242
1243
1244def buildInstaller():
1245
1246 # Zap all compiled files
1247 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1248 for fn in filenames:
1249 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1250 os.unlink(os.path.join(dirpath, fn))
1251
1252 outdir = os.path.join(WORKDIR, 'installer')
1253 if os.path.exists(outdir):
1254 shutil.rmtree(outdir)
1255 os.mkdir(outdir)
1256
Ronald Oussoren508282e2009-03-30 19:34:51 +00001257 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001258 pkgcontents = os.path.join(pkgroot, 'Packages')
1259 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001260 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001261 packageFromRecipe(pkgcontents, recipe)
1262
1263 rsrcDir = os.path.join(pkgroot, 'Resources')
1264
1265 fn = os.path.join(pkgroot, 'PkgInfo')
1266 fp = open(fn, 'w')
1267 fp.write('pmkrpkg1')
1268 fp.close()
1269
1270 os.mkdir(rsrcDir)
1271
1272 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1273 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001274 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001275 IFPkgDescriptionVersion=getVersion(),
1276 )
1277
1278 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1279 for fn in os.listdir('resources'):
1280 if fn == '.svn': continue
1281 if fn.endswith('.jpg'):
1282 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1283 else:
1284 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1285
Ronald Oussorenc5555542006-06-11 20:24:45 +00001286 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001287
1288
1289def installSize(clear=False, _saved=[]):
1290 if clear:
1291 del _saved[:]
1292 if not _saved:
1293 data = captureCommand("du -ks %s"%(
1294 shellQuote(os.path.join(WORKDIR, '_root'))))
1295 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1296 return _saved[0]
1297
1298
1299def buildDMG():
1300 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001301 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001302 """
1303 outdir = os.path.join(WORKDIR, 'diskimage')
1304 if os.path.exists(outdir):
1305 shutil.rmtree(outdir)
1306
1307 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001308 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001309 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001310 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001311 imagepath = imagepath + '.dmg'
1312
1313 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001314 volname='Python %s'%(getFullVersion())
1315 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1316 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001317 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001318 shellQuote(imagepath + ".tmp.dmg" )))
1319
1320
1321 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1322 os.mkdir(os.path.join(WORKDIR, "mnt"))
1323 runCommand("hdiutil attach %s -mountroot %s"%(
1324 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1325
1326 # Custom icon for the DMG, shown when the DMG is mounted.
1327 shutil.copy("../Icons/Disk Image.icns",
1328 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001329 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001330 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1331
1332 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1333
1334 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1335 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1336 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1337 setIcon(imagepath, "../Icons/Disk Image.icns")
1338
1339 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001340
1341 return imagepath
1342
1343
1344def setIcon(filePath, icnsPath):
1345 """
1346 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001347 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001348
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001349 dirPath = os.path.normpath(os.path.dirname(__file__))
1350 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001351 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1352 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1353 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001354 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1355 if not os.path.exists(appPath):
1356 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001357 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1358 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001359
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001360 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1361 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001362
1363def main():
1364 # First parse options and check if we can perform our work
1365 parseOptions()
1366 checkEnvironment()
1367
Ronald Oussoren508282e2009-03-30 19:34:51 +00001368 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001369 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001370 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001371
1372 if os.path.exists(WORKDIR):
1373 shutil.rmtree(WORKDIR)
1374 os.mkdir(WORKDIR)
1375
Ronald Oussoren287128a2010-04-18 14:01:05 +00001376 os.environ['LC_ALL'] = 'C'
1377
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001378 # Then build third-party libraries such as sleepycat DB4.
1379 buildLibraries()
1380
1381 # Now build python itself
1382 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001383
1384 # And then build the documentation
1385 # Remove the Deployment Target from the shell
1386 # environment, it's no longer needed and
1387 # an unexpected build target can cause problems
1388 # when Sphinx and its dependencies need to
1389 # be (re-)installed.
1390 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001391 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001392
1393
1394 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001395 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001396 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001397 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001398
Benjamin Petersonc3104762008-10-03 11:52:06 +00001399 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001400 getVersion(),))
Ned Deilye1c9794952013-01-29 00:07:46 -08001401 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001402 setIcon(folder, "../Icons/Python Folder.icns")
1403
1404 # Create the installer
1405 buildInstaller()
1406
1407 # And copy the readme into the directory containing the installer
1408 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1409
1410 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001411 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001412
1413 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001414 fp.write("# BUILD INFO\n")
1415 fp.write("# Date: %s\n" % time.ctime())
1416 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001417 fp.close()
1418
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001419 # And copy it to a DMG
1420 buildDMG()
1421
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001422if __name__ == "__main__":
1423 main()