blob: ddb067d94fc3544ff4c8b6504617608fa4ab99f6 [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 Deilye6ef7022013-10-25 00:46:59 -07001061 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1062 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1063
Ned Deilye1c9794952013-01-29 00:07:46 -08001064 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1065 'Python.framework', 'Versions',
1066 version, 'lib', 'python%s'%(version,))
Ned Deilye6ef7022013-10-25 00:46:59 -07001067
1068 # fix Makefile
1069 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1070 fp = open(path, 'r')
1071 data = fp.read()
1072 fp.close()
1073
1074 for p in (include_path, lib_path):
1075 data = data.replace(" " + p, '')
1076 data = data.replace(p + " ", '')
1077
1078 fp = open(path, 'w')
1079 fp.write(data)
1080 fp.close()
1081
1082 # fix _sysconfigdata if it exists
1083 #
1084 # TODO: make this more robust! test_sysconfig_module of
1085 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1086 # the output from get_config_var in both sysconfig and
1087 # distutils.sysconfig is exactly the same for both CFLAGS and
1088 # LDFLAGS. The fixing up is now complicated by the pretty
1089 # printing in _sysconfigdata.py. Also, we are using the
1090 # pprint from the Python running the installer build which
1091 # may not cosmetically format the same as the pprint in the Python
1092 # being built (and which is used to originally generate
1093 # _sysconfigdata.py).
1094
1095 import pprint
1096 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1097 if os.path.exists(path):
Ned Deilye1c9794952013-01-29 00:07:46 -08001098 fp = open(path, 'r')
1099 data = fp.read()
1100 fp.close()
Ned Deilye6ef7022013-10-25 00:46:59 -07001101 # create build_time_vars dict
1102 exec(data)
1103 vars = {}
1104 for k, v in build_time_vars.items():
1105 if type(v) == type(''):
1106 for p in (include_path, lib_path):
1107 v = v.replace(' ' + p, '')
1108 v = v.replace(p + ' ', '')
1109 vars[k] = v
Ned Deilye1c9794952013-01-29 00:07:46 -08001110
Ned Deilye1c9794952013-01-29 00:07:46 -08001111 fp = open(path, 'w')
Ned Deilye6ef7022013-10-25 00:46:59 -07001112 # duplicated from sysconfig._generate_posix_vars()
1113 fp.write('# system configuration generated and used by'
1114 ' the sysconfig module\n')
1115 fp.write('build_time_vars = ')
1116 pprint.pprint(vars, stream=fp)
Ned Deilye1c9794952013-01-29 00:07:46 -08001117 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001118
1119 # Add symlinks in /usr/local/bin, using relative links
1120 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1121 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1122 'Python.framework', 'Versions', version, 'bin')
1123 if os.path.exists(usr_local_bin):
1124 shutil.rmtree(usr_local_bin)
1125 os.makedirs(usr_local_bin)
1126 for fn in os.listdir(
1127 os.path.join(frmDir, 'Versions', version, 'bin')):
1128 os.symlink(os.path.join(to_framework, fn),
1129 os.path.join(usr_local_bin, fn))
1130
1131 os.chdir(curdir)
1132
Ned Deily53c460d2011-01-30 01:43:40 +00001133 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001134 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001135 # with an already installed version of python 2
1136 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1137 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001138
1139def patchFile(inPath, outPath):
1140 data = fileContents(inPath)
1141 data = data.replace('$FULL_VERSION', getFullVersion())
1142 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001143 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001144 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001145 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +00001146
1147 # This one is not handy as a template variable
1148 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001149 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001150 fp.write(data)
1151 fp.close()
1152
1153def patchScript(inPath, outPath):
1154 data = fileContents(inPath)
1155 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001156 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001157 fp.write(data)
1158 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001159 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001160
1161
1162
1163def packageFromRecipe(targetDir, recipe):
1164 curdir = os.getcwd()
1165 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001166 # The major version (such as 2.5) is included in the package name
1167 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001168 # common.
1169 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001170 srcdir = recipe.get('source')
1171 pkgroot = recipe.get('topdir', srcdir)
1172 postflight = recipe.get('postflight')
1173 readme = textwrap.dedent(recipe['readme'])
1174 isRequired = recipe.get('required', True)
1175
Ned Deilye1c9794952013-01-29 00:07:46 -08001176 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001177
1178 # Substitute some variables
1179 textvars = dict(
1180 VER=getVersion(),
1181 FULLVER=getFullVersion(),
1182 )
1183 readme = readme % textvars
1184
1185 if pkgroot is not None:
1186 pkgroot = pkgroot % textvars
1187 else:
1188 pkgroot = '/'
1189
1190 if srcdir is not None:
1191 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1192 srcdir = srcdir % textvars
1193
1194 if postflight is not None:
1195 postflight = os.path.abspath(postflight)
1196
1197 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1198 os.makedirs(packageContents)
1199
1200 if srcdir is not None:
1201 os.chdir(srcdir)
1202 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1203 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1204 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1205
1206 fn = os.path.join(packageContents, 'PkgInfo')
1207 fp = open(fn, 'w')
1208 fp.write('pmkrpkg1')
1209 fp.close()
1210
1211 rsrcDir = os.path.join(packageContents, "Resources")
1212 os.mkdir(rsrcDir)
1213 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1214 fp.write(readme)
1215 fp.close()
1216
1217 if postflight is not None:
1218 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1219
1220 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001221 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001222 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001223 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1224 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1225 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001226 CFBundleShortVersionString=vers,
1227 IFMajorVersion=major,
1228 IFMinorVersion=minor,
1229 IFPkgFormatVersion=0.10000000149011612,
1230 IFPkgFlagAllowBackRev=False,
1231 IFPkgFlagAuthorizationAction="RootAuthorization",
1232 IFPkgFlagDefaultLocation=pkgroot,
1233 IFPkgFlagFollowLinks=True,
1234 IFPkgFlagInstallFat=True,
1235 IFPkgFlagIsRequired=isRequired,
1236 IFPkgFlagOverwritePermissions=False,
1237 IFPkgFlagRelocatable=False,
1238 IFPkgFlagRestartAction="NoRestart",
1239 IFPkgFlagRootVolumeOnly=True,
1240 IFPkgFlagUpdateInstalledLangauges=False,
1241 )
1242 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1243
1244 pl = Plist(
1245 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001246 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001247 IFPkgDescriptionVersion=vers,
1248 )
1249 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1250
1251 finally:
1252 os.chdir(curdir)
1253
1254
1255def makeMpkgPlist(path):
1256
1257 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001258 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001259
1260 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001261 CFBundleGetInfoString="Python %s"%(vers,),
1262 CFBundleIdentifier='org.python.Python',
1263 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001264 CFBundleShortVersionString=vers,
1265 IFMajorVersion=major,
1266 IFMinorVersion=minor,
1267 IFPkgFlagComponentDirectory="Contents/Packages",
1268 IFPkgFlagPackageList=[
1269 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001270 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001271 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001272 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001273 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001274 ],
1275 IFPkgFormatVersion=0.10000000149011612,
1276 IFPkgFlagBackgroundScaling="proportional",
1277 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001278 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001279 )
1280
1281 writePlist(pl, path)
1282
1283
1284def buildInstaller():
1285
1286 # Zap all compiled files
1287 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1288 for fn in filenames:
1289 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1290 os.unlink(os.path.join(dirpath, fn))
1291
1292 outdir = os.path.join(WORKDIR, 'installer')
1293 if os.path.exists(outdir):
1294 shutil.rmtree(outdir)
1295 os.mkdir(outdir)
1296
Ronald Oussoren508282e2009-03-30 19:34:51 +00001297 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001298 pkgcontents = os.path.join(pkgroot, 'Packages')
1299 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001300 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001301 packageFromRecipe(pkgcontents, recipe)
1302
1303 rsrcDir = os.path.join(pkgroot, 'Resources')
1304
1305 fn = os.path.join(pkgroot, 'PkgInfo')
1306 fp = open(fn, 'w')
1307 fp.write('pmkrpkg1')
1308 fp.close()
1309
1310 os.mkdir(rsrcDir)
1311
1312 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1313 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001314 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001315 IFPkgDescriptionVersion=getVersion(),
1316 )
1317
1318 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1319 for fn in os.listdir('resources'):
1320 if fn == '.svn': continue
1321 if fn.endswith('.jpg'):
1322 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1323 else:
1324 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1325
Ronald Oussorenc5555542006-06-11 20:24:45 +00001326 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001327
1328
1329def installSize(clear=False, _saved=[]):
1330 if clear:
1331 del _saved[:]
1332 if not _saved:
1333 data = captureCommand("du -ks %s"%(
1334 shellQuote(os.path.join(WORKDIR, '_root'))))
1335 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1336 return _saved[0]
1337
1338
1339def buildDMG():
1340 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001341 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001342 """
1343 outdir = os.path.join(WORKDIR, 'diskimage')
1344 if os.path.exists(outdir):
1345 shutil.rmtree(outdir)
1346
1347 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001348 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001349 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001350 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001351 imagepath = imagepath + '.dmg'
1352
1353 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001354 volname='Python %s'%(getFullVersion())
1355 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1356 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001357 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001358 shellQuote(imagepath + ".tmp.dmg" )))
1359
1360
1361 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1362 os.mkdir(os.path.join(WORKDIR, "mnt"))
1363 runCommand("hdiutil attach %s -mountroot %s"%(
1364 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1365
1366 # Custom icon for the DMG, shown when the DMG is mounted.
1367 shutil.copy("../Icons/Disk Image.icns",
1368 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001369 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001370 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1371
1372 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1373
1374 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1375 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1376 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1377 setIcon(imagepath, "../Icons/Disk Image.icns")
1378
1379 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001380
1381 return imagepath
1382
1383
1384def setIcon(filePath, icnsPath):
1385 """
1386 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001387 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001388
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001389 dirPath = os.path.normpath(os.path.dirname(__file__))
1390 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001391 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1392 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1393 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001394 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1395 if not os.path.exists(appPath):
1396 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001397 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1398 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001399
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001400 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1401 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001402
1403def main():
1404 # First parse options and check if we can perform our work
1405 parseOptions()
1406 checkEnvironment()
1407
Ronald Oussoren508282e2009-03-30 19:34:51 +00001408 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001409 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001410 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001411
1412 if os.path.exists(WORKDIR):
1413 shutil.rmtree(WORKDIR)
1414 os.mkdir(WORKDIR)
1415
Ronald Oussoren287128a2010-04-18 14:01:05 +00001416 os.environ['LC_ALL'] = 'C'
1417
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001418 # Then build third-party libraries such as sleepycat DB4.
1419 buildLibraries()
1420
1421 # Now build python itself
1422 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001423
1424 # And then build the documentation
1425 # Remove the Deployment Target from the shell
1426 # environment, it's no longer needed and
1427 # an unexpected build target can cause problems
1428 # when Sphinx and its dependencies need to
1429 # be (re-)installed.
1430 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001431 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001432
1433
1434 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001435 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001436 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001437 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001438
Benjamin Petersonc3104762008-10-03 11:52:06 +00001439 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001440 getVersion(),))
Ned Deilye1c9794952013-01-29 00:07:46 -08001441 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001442 setIcon(folder, "../Icons/Python Folder.icns")
1443
1444 # Create the installer
1445 buildInstaller()
1446
1447 # And copy the readme into the directory containing the installer
1448 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1449
1450 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001451 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001452
1453 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001454 fp.write("# BUILD INFO\n")
1455 fp.write("# Date: %s\n" % time.ctime())
1456 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001457 fp.close()
1458
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001459 # And copy it to a DMG
1460 buildDMG()
1461
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001462if __name__ == "__main__":
1463 main()