blob: ef822d585495ad74ec6fd6e8ec5df58f57e1538e [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++'),
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000158}
159
Ned Deilye1c9794952013-01-29 00:07:46 -0800160CC, CXX = target_cc_map[DEPTARGET]
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000161
Ned Deily53c460d2011-01-30 01:43:40 +0000162PYTHON_3 = getVersionTuple() >= (3, 0)
163
Ronald Oussoren158ad592006-11-07 16:00:34 +0000164USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000165 Usage: build_python [options]
166
167 Options:
168 -? or -h: Show this message
169 -b DIR
170 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
171 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
172 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
173 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000174 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
175 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000176""")% globals()
177
Ned Deilye1c9794952013-01-29 00:07:46 -0800178# Dict of object file names with shared library names to check after building.
179# This is to ensure that we ended up dynamically linking with the shared
180# library paths and versions we expected. For example:
181# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
182# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
183# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
184EXPECTED_SHARED_LIBS = {}
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000185
186# Instructions for building libraries that are necessary for building a
187# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000188# [The recipes are defined here for convenience but instantiated later after
189# command line options have been processed.]
190def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000191 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000192
Ned Deilye1c9794952013-01-29 00:07:46 -0800193 LT_10_5 = bool(DEPTARGET < '10.5')
194
195 if getVersionTuple() >= (3, 3):
196 result.extend([
197 dict(
198 name="XZ 5.0.3",
199 url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
200 checksum='fefe52f9ecd521de2a8ce38c21a27574',
201 configure_pre=[
202 '--disable-dependency-tracking',
203 ]
204 ),
205 ])
206
207 result.extend([
208 dict(
209 name="NCurses 5.9",
210 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
211 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
212 configure_pre=[
213 "--enable-widec",
214 "--without-cxx",
215 "--without-cxx-binding",
216 "--without-ada",
217 "--without-curses-h",
218 "--enable-shared",
219 "--with-shared",
220 "--without-debug",
221 "--without-normal",
222 "--without-tests",
223 "--without-manpages",
224 "--datadir=/usr/share",
225 "--sysconfdir=/etc",
226 "--sharedstatedir=/usr/com",
227 "--with-terminfo-dirs=/usr/share/terminfo",
228 "--with-default-terminfo-dir=/usr/share/terminfo",
229 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
230 ],
231 patchscripts=[
232 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
233 "f54bf02a349f96a7c4f0d00922f3a0d4"),
234 ],
235 useLDFlags=False,
236 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
237 shellQuote(os.path.join(WORKDIR, 'libraries')),
238 shellQuote(os.path.join(WORKDIR, 'libraries')),
239 getVersion(),
240 ),
241 ),
242 dict(
243 name="SQLite 3.7.13",
244 url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
245 checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
246 extra_cflags=('-Os '
247 '-DSQLITE_ENABLE_FTS4 '
248 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
249 '-DSQLITE_ENABLE_RTREE '
250 '-DSQLITE_TCL=0 '
251 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
252 configure_pre=[
253 '--enable-threadsafe',
254 '--enable-shared=no',
255 '--enable-static=yes',
256 '--disable-readline',
257 '--disable-dependency-tracking',
258 ]
259 ),
260 ])
261
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000262 if DEPTARGET < '10.5':
263 result.extend([
264 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000265 name="Bzip2 1.0.6",
266 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
267 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000268 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800269 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
270 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000271 shellQuote(os.path.join(WORKDIR, 'libraries')),
272 ' -arch '.join(ARCHLIST),
273 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000274 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000275 ),
276 dict(
277 name="ZLib 1.2.3",
278 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
279 checksum='debc62758716a169df9f62e6ab2bc634',
280 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800281 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
282 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000283 shellQuote(os.path.join(WORKDIR, 'libraries')),
284 ' -arch '.join(ARCHLIST),
285 SDKPATH,
286 ),
287 ),
288 dict(
289 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000290 name="GNU Readline 6.1.2",
291 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
292 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000293 patchlevel='0',
294 patches=[
295 # The readline maintainers don't do actual micro releases, but
296 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800297 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
298 'c642f2e84d820884b0bf9fd176bc6c3f'),
299 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
300 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000301 ]
302 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000303 ])
304
Ned Deily53c460d2011-01-30 01:43:40 +0000305 if not PYTHON_3:
306 result.extend([
307 dict(
308 name="Sleepycat DB 4.7.25",
309 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
310 checksum='ec2b87e833779681a0c3a814aa71359e',
311 buildDir="build_unix",
312 configure="../dist/configure",
313 configure_pre=[
314 '--includedir=/usr/local/include/db4',
315 ]
316 ),
317 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000318
319 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000320
321
322# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000323def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000324 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000325 result = [
326 dict(
327 name="PythonFramework",
328 long_name="Python Framework",
329 source="/Library/Frameworks/Python.framework",
330 readme="""\
331 This package installs Python.framework, that is the python
332 interpreter and the standard library. This also includes Python
333 wrappers for lots of Mac OS X API's.
334 """,
335 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000336 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000337 ),
338 dict(
339 name="PythonApplications",
340 long_name="GUI Applications",
341 source="/Applications/Python %(VER)s",
342 readme="""\
343 This package installs IDLE (an interactive Python IDE),
344 Python Launcher and Build Applet (create application bundles
345 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000346
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000347 It also installs a number of examples and demos.
348 """,
349 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000350 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000351 ),
352 dict(
353 name="PythonUnixTools",
354 long_name="UNIX command-line tools",
355 source="/usr/local/bin",
356 readme="""\
357 This package installs the unix tools in /usr/local/bin for
358 compatibility with older releases of Python. This package
359 is not necessary to use Python.
360 """,
361 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000362 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000363 ),
364 dict(
365 name="PythonDocumentation",
366 long_name="Python Documentation",
367 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
368 source="/pydocs",
369 readme="""\
370 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800371 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000372 """,
373 postflight="scripts/postflight.documentation",
374 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000375 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000376 ),
377 dict(
378 name="PythonProfileChanges",
379 long_name="Shell profile updater",
380 readme="""\
381 This packages updates your shell profile to make sure that
382 the Python tools are found by your shell in preference of
383 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000384
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000385 If you don't install this package you'll have to add
386 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
387 to your PATH by hand.
388 """,
389 postflight="scripts/postflight.patch-profile",
390 topdir="/Library/Frameworks/Python.framework",
391 source="/empty-dir",
392 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000393 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000394 ),
395 ]
396
Ned Deilye1c9794952013-01-29 00:07:46 -0800397 if DEPTARGET < '10.4' and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000398 result.append(
399 dict(
400 name="PythonSystemFixes",
401 long_name="Fix system Python",
402 readme="""\
403 This package updates the system python installation on
404 Mac OS X 10.3 to ensure that you can build new python extensions
405 using that copy of python after installing this version.
406 """,
407 postflight="../Tools/fixapplepython23.py",
408 topdir="/Library/Frameworks/Python.framework",
409 source="/empty-dir",
410 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000411 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000412 )
413 )
414 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000415
416def fatal(msg):
417 """
418 A fatal error, bail out.
419 """
420 sys.stderr.write('FATAL: ')
421 sys.stderr.write(msg)
422 sys.stderr.write('\n')
423 sys.exit(1)
424
425def fileContents(fn):
426 """
427 Return the contents of the named file
428 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800429 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000430
431def runCommand(commandline):
432 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200433 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000434 unless the command fails.
435 """
436 fd = os.popen(commandline, 'r')
437 data = fd.read()
438 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000439 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000440 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800441 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000442
443 if VERBOSE:
444 sys.stdout.write(data); sys.stdout.flush()
445
446def captureCommand(commandline):
447 fd = os.popen(commandline, 'r')
448 data = fd.read()
449 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000450 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000451 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800452 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000453
454 return data
455
Ronald Oussoren287128a2010-04-18 14:01:05 +0000456def getTclTkVersion(configfile, versionline):
457 """
458 search Tcl or Tk configuration file for version line
459 """
460 try:
461 f = open(configfile, "r")
462 except:
463 fatal("Framework configuration file not found: %s" % configfile)
464
465 for l in f:
466 if l.startswith(versionline):
467 f.close()
468 return l
469
470 fatal("Version variable %s not found in framework configuration file: %s"
471 % (versionline, configfile))
472
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000473def checkEnvironment():
474 """
475 Check that we're running on a supported system.
476 """
477
Ned Deily53c460d2011-01-30 01:43:40 +0000478 if sys.version_info[0:2] < (2, 4):
479 fatal("This script must be run with Python 2.4 or later")
480
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000481 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000482 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000483
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000484 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000485 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000486
487 if not os.path.exists(SDKPATH):
488 fatal("Please install the latest version of Xcode and the %s SDK"%(
489 os.path.basename(SDKPATH[:-4])))
490
Ronald Oussoren287128a2010-04-18 14:01:05 +0000491 # Because we only support dynamic load of only one major/minor version of
492 # Tcl/Tk, ensure:
493 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800494 # higher than the Apple-supplied system version in
495 # SDKROOT/System/Library/Frameworks
496 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
497 # in) SDKROOT/Library/Frameworks with the same version as the system
498 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000499
Ned Deilye1c9794952013-01-29 00:07:46 -0800500 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000501 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800502 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000503 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800504 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000505 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800506 frameworks[framework] = os.readlink(sysfw)
507 if not os.path.exists(libfw):
508 fatal("Please install a link to a current %s %s as %s so "
509 "the user can override the system framework."
510 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000511 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000512 fatal("Version of %s must match %s" % (libfw, sysfw) )
513 if os.path.exists(usrfw):
514 fatal("Please rename %s to avoid possible dynamic load issues."
515 % usrfw)
516
Ned Deilye1c9794952013-01-29 00:07:46 -0800517 if frameworks['Tcl'] != frameworks['Tk']:
518 fatal("The Tcl and Tk frameworks are not the same version.")
519
520 # add files to check after build
521 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
522 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
523 % frameworks['Tcl'],
524 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
525 % frameworks['Tk'],
526 ]
527
Ronald Oussoren287128a2010-04-18 14:01:05 +0000528 # Remove inherited environment variables which might influence build
529 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
530 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
531 for ev in list(os.environ):
532 for prefix in environ_var_prefixes:
533 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800534 print("INFO: deleting environment variable %s=%s" % (
535 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000536 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000537
Ned Deilye1c9794952013-01-29 00:07:46 -0800538 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
539 if 'SDK_TOOLS_BIN' in os.environ:
540 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
541 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
542 # add its fixed location here if it exists
543 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
544 if os.path.isdir(OLD_DEVELOPER_TOOLS):
545 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
546 os.environ['PATH'] = base_path
547 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussorenac4b7ad2010-04-20 05:50:44 +0000548
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000549
Ronald Oussoren158ad592006-11-07 16:00:34 +0000550def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000551 """
552 Parse arguments and update global settings.
553 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000554 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800555 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000556
557 if args is None:
558 args = sys.argv[1:]
559
560 try:
561 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000562 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
563 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800564 except getopt.GetoptError:
565 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000566 sys.exit(1)
567
568 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800569 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000570 sys.exit(1)
571
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000572 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000573 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000574 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800575 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000576 sys.exit(0)
577
578 elif k in ('-d', '--build-dir'):
579 WORKDIR=v
580
581 elif k in ('--third-party',):
582 DEPSRC=v
583
584 elif k in ('--sdk-path',):
585 SDKPATH=v
586
587 elif k in ('--src-dir',):
588 SRCDIR=v
589
Ronald Oussoren508282e2009-03-30 19:34:51 +0000590 elif k in ('--dep-target', ):
591 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000592 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000593
594 elif k in ('--universal-archs', ):
595 if v in UNIVERSALOPTS:
596 UNIVERSALARCHS = v
597 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000598 if deptarget is None:
599 # Select alternate default deployment
600 # target
601 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000602 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800603 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000604
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000605 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800606 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000607
608 SRCDIR=os.path.abspath(SRCDIR)
609 WORKDIR=os.path.abspath(WORKDIR)
610 SDKPATH=os.path.abspath(SDKPATH)
611 DEPSRC=os.path.abspath(DEPSRC)
612
Ned Deilye1c9794952013-01-29 00:07:46 -0800613 CC, CXX=target_cc_map[DEPTARGET]
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000614
Ned Deilye1c9794952013-01-29 00:07:46 -0800615 print("Settings:")
616 print(" * Source directory:", SRCDIR)
617 print(" * Build directory: ", WORKDIR)
618 print(" * SDK location: ", SDKPATH)
619 print(" * Third-party source:", DEPSRC)
620 print(" * Deployment target:", DEPTARGET)
621 print(" * Universal architectures:", ARCHLIST)
622 print(" * C compiler:", CC)
623 print(" * C++ compiler:", CXX)
624 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000625
626
627
628
629def extractArchive(builddir, archiveName):
630 """
631 Extract a source archive into 'builddir'. Returns the path of the
632 extracted archive.
633
634 XXX: This function assumes that archives contain a toplevel directory
635 that is has the same name as the basename of the archive. This is
636 save enough for anything we use.
637 """
638 curdir = os.getcwd()
639 try:
640 os.chdir(builddir)
641 if archiveName.endswith('.tar.gz'):
642 retval = os.path.basename(archiveName[:-7])
643 if os.path.exists(retval):
644 shutil.rmtree(retval)
645 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
646
647 elif archiveName.endswith('.tar.bz2'):
648 retval = os.path.basename(archiveName[:-8])
649 if os.path.exists(retval):
650 shutil.rmtree(retval)
651 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
652
653 elif archiveName.endswith('.tar'):
654 retval = os.path.basename(archiveName[:-4])
655 if os.path.exists(retval):
656 shutil.rmtree(retval)
657 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
658
659 elif archiveName.endswith('.zip'):
660 retval = os.path.basename(archiveName[:-4])
661 if os.path.exists(retval):
662 shutil.rmtree(retval)
663 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
664
665 data = fp.read()
666 xit = fp.close()
667 if xit is not None:
668 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800669 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000670
671 return os.path.join(builddir, retval)
672
673 finally:
674 os.chdir(curdir)
675
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000676def downloadURL(url, fname):
677 """
678 Download the contents of the url into the file.
679 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800680 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000681 fpOut = open(fname, 'wb')
682 block = fpIn.read(10240)
683 try:
684 while block:
685 fpOut.write(block)
686 block = fpIn.read(10240)
687 fpIn.close()
688 fpOut.close()
689 except:
690 try:
691 os.unlink(fname)
692 except:
693 pass
694
Ned Deilye1c9794952013-01-29 00:07:46 -0800695def verifyThirdPartyFile(url, checksum, fname):
696 """
697 Download file from url to filename fname if it does not already exist.
698 Abort if file contents does not match supplied md5 checksum.
699 """
700 name = os.path.basename(fname)
701 if os.path.exists(fname):
702 print("Using local copy of %s"%(name,))
703 else:
704 print("Did not find local copy of %s"%(name,))
705 print("Downloading %s"%(name,))
706 downloadURL(url, fname)
707 print("Archive for %s stored as %s"%(name, fname))
708 if os.system(
709 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
710 % (shellQuote(fname), checksum) ):
711 fatal('MD5 checksum mismatch for file %s' % fname)
712
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000713def buildRecipe(recipe, basedir, archList):
714 """
715 Build software using a recipe. This function does the
716 'configure;make;make install' dance for C software, with a possibility
717 to customize this process, basically a poor-mans DarwinPorts.
718 """
719 curdir = os.getcwd()
720
721 name = recipe['name']
722 url = recipe['url']
723 configure = recipe.get('configure', './configure')
724 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
725 shellQuote(basedir)))
726
727 archiveName = os.path.split(url)[-1]
728 sourceArchive = os.path.join(DEPSRC, archiveName)
729
730 if not os.path.exists(DEPSRC):
731 os.mkdir(DEPSRC)
732
Ned Deilye1c9794952013-01-29 00:07:46 -0800733 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
734 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000735 buildDir=os.path.join(WORKDIR, '_bld')
736 if not os.path.exists(buildDir):
737 os.mkdir(buildDir)
738
739 workDir = extractArchive(buildDir, sourceArchive)
740 os.chdir(workDir)
741 if 'buildDir' in recipe:
742 os.chdir(recipe['buildDir'])
743
Ned Deilye1c9794952013-01-29 00:07:46 -0800744 for patch in recipe.get('patches', ()):
745 if isinstance(patch, tuple):
746 url, checksum = patch
747 fn = os.path.join(DEPSRC, os.path.basename(url))
748 verifyThirdPartyFile(url, checksum, fn)
749 else:
750 # patch is a file in the source directory
751 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000752 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
753 shellQuote(fn),))
754
Ned Deilye1c9794952013-01-29 00:07:46 -0800755 for patchscript in recipe.get('patchscripts', ()):
756 if isinstance(patchscript, tuple):
757 url, checksum = patchscript
758 fn = os.path.join(DEPSRC, os.path.basename(url))
759 verifyThirdPartyFile(url, checksum, fn)
760 else:
761 # patch is a file in the source directory
762 fn = os.path.join(curdir, patchscript)
763 if fn.endswith('.bz2'):
764 runCommand('bunzip2 -fk %s' % shellQuote(fn))
765 fn = fn[:-4]
766 runCommand('sh %s' % shellQuote(fn))
767 os.unlink(fn)
768
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000769 if configure is not None:
770 configure_args = [
771 "--prefix=/usr/local",
772 "--enable-static",
773 "--disable-shared",
774 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
775 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000776
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000777 if 'configure_pre' in recipe:
778 args = list(recipe['configure_pre'])
779 if '--disable-static' in args:
780 configure_args.remove('--enable-static')
781 if '--enable-shared' in args:
782 configure_args.remove('--disable-shared')
783 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000784
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000785 if recipe.get('useLDFlags', 1):
786 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800787 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
788 "-I%s/usr/local/include"%(
789 recipe.get('extra_cflags', ''),
790 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000791 ' -arch '.join(archList),
792 shellQuote(SDKPATH)[1:-1],
793 shellQuote(basedir)[1:-1],),
Ned Deilye1c9794952013-01-29 00:07:46 -0800794 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
795 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000796 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000797 shellQuote(basedir)[1:-1],
798 ' -arch '.join(archList)),
799 ])
800 else:
801 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800802 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
803 "-I%s/usr/local/include"%(
804 recipe.get('extra_cflags', ''),
805 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000806 ' -arch '.join(archList),
807 shellQuote(SDKPATH)[1:-1],
808 shellQuote(basedir)[1:-1],),
809 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000810
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000811 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -0800812 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000813
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000814 configure_args.insert(0, configure)
815 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000816
Ned Deilye1c9794952013-01-29 00:07:46 -0800817 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000818 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000819
Ned Deilye1c9794952013-01-29 00:07:46 -0800820 print("Running install for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000821 runCommand('{ ' + install + ' ;} 2>&1')
822
Ned Deilye1c9794952013-01-29 00:07:46 -0800823 print("Done %s"%(name,))
824 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000825
826 os.chdir(curdir)
827
828def buildLibraries():
829 """
830 Build our dependencies into $WORKDIR/libraries/usr/local
831 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800832 print("")
833 print("Building required libraries")
834 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000835 universal = os.path.join(WORKDIR, 'libraries')
836 os.mkdir(universal)
837 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
838 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
839
Ronald Oussoren508282e2009-03-30 19:34:51 +0000840 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000841 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000842
843
844
845def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000846 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000847 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -0800848 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000849 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000850 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000851 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000852 curDir = os.getcwd()
853 os.chdir(buildDir)
854 runCommand('make update')
Martin v. Löwis896c4772010-04-22 11:34:36 +0000855 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000856 os.chdir(curDir)
857 if not os.path.exists(docdir):
858 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000859 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000860
861
862def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -0800863 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000864
865 buildDir = os.path.join(WORKDIR, '_bld', 'python')
866 rootDir = os.path.join(WORKDIR, '_root')
867
868 if os.path.exists(buildDir):
869 shutil.rmtree(buildDir)
870 if os.path.exists(rootDir):
871 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +0000872 os.makedirs(buildDir)
873 os.makedirs(rootDir)
874 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000875 curdir = os.getcwd()
876 os.chdir(buildDir)
877
878 # Not sure if this is still needed, the original build script
879 # claims that parts of the install assume python.exe exists.
880 os.symlink('python', os.path.join(buildDir, 'python.exe'))
881
882 # Extract the version from the configure file, needed to calculate
883 # several paths.
884 version = getVersion()
885
Ronald Oussoren008af852009-03-30 20:02:08 +0000886 # Since the extra libs are not in their installed framework location
887 # during the build, augment the library path so that the interpreter
888 # will find them during its extension import sanity checks.
889 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
890 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -0800891 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +0000892 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
893 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +0000894 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +0000895 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
896 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
897 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
898 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +0000899 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +0000900 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000901 shellQuote(WORKDIR)[1:-1]))
902
Ned Deilye1c9794952013-01-29 00:07:46 -0800903 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000904 runCommand("make")
905
Ned Deilye1c9794952013-01-29 00:07:46 -0800906 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +0000907 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000908 shellQuote(rootDir)))
909
Ned Deilye1c9794952013-01-29 00:07:46 -0800910 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000911 runCommand("make frameworkinstallextras DESTDIR=%s"%(
912 shellQuote(rootDir)))
913
Ronald Oussoren008af852009-03-30 20:02:08 +0000914 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -0800915 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000916 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
917 runCommand("mv %s/* %s"%(
918 shellQuote(os.path.join(
919 WORKDIR, 'libraries', 'Library', 'Frameworks',
920 'Python.framework', 'Versions', getVersion(),
921 'lib')),
922 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
923 'Python.framework', 'Versions', getVersion(),
924 'lib'))))
925
Ned Deilye1c9794952013-01-29 00:07:46 -0800926 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000927 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000928 gid = grp.getgrnam('admin').gr_gid
929
Ned Deilye1c9794952013-01-29 00:07:46 -0800930 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000931 for dirpath, dirnames, filenames in os.walk(frmDir):
932 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -0800933 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000934 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +0000935
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000936 for fn in filenames:
937 if os.path.islink(fn):
938 continue
939
940 # "chmod g+w $fn"
941 p = os.path.join(dirpath, fn)
942 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000943 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
944 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000945
Ned Deilye1c9794952013-01-29 00:07:46 -0800946 if fn in EXPECTED_SHARED_LIBS:
947 # check to see that this file was linked with the
948 # expected library path and version
949 data = captureCommand("otool -L %s" % shellQuote(p))
950 for sl in EXPECTED_SHARED_LIBS[fn]:
951 if ("\t%s " % sl) not in data:
952 print("Expected shared lib %s was not linked with %s"
953 % (sl, p))
954 shared_lib_error = True
955
956 if shared_lib_error:
957 fatal("Unexpected shared library errors.")
958
Ned Deily53c460d2011-01-30 01:43:40 +0000959 if PYTHON_3:
960 LDVERSION=None
961 VERSION=None
962 ABIFLAGS=None
963
964 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
965 for ln in fp:
966 if ln.startswith('VERSION='):
967 VERSION=ln.split()[1]
968 if ln.startswith('ABIFLAGS='):
969 ABIFLAGS=ln.split()[1]
970 if ln.startswith('LDVERSION='):
971 LDVERSION=ln.split()[1]
972 fp.close()
973
974 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
975 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
976 config_suffix = '-' + LDVERSION
977 else:
978 config_suffix = '' # Python 2.x
979
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000980 # We added some directories to the search path during the configure
981 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -0800982 # the end-users system. Also remove the directories from _sysconfigdata.py
983 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000984
Ned Deilye1c9794952013-01-29 00:07:46 -0800985 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
986 'Python.framework', 'Versions',
987 version, 'lib', 'python%s'%(version,))
988 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
989 os.path.join(path_to_lib, '_sysconfigdata.py')]
990 for path in paths:
991 if not os.path.exists(path):
992 continue
993 fp = open(path, 'r')
994 data = fp.read()
995 fp.close()
996
997 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
998 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
999 fp = open(path, 'w')
1000 fp.write(data)
1001 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001002
1003 # Add symlinks in /usr/local/bin, using relative links
1004 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1005 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1006 'Python.framework', 'Versions', version, 'bin')
1007 if os.path.exists(usr_local_bin):
1008 shutil.rmtree(usr_local_bin)
1009 os.makedirs(usr_local_bin)
1010 for fn in os.listdir(
1011 os.path.join(frmDir, 'Versions', version, 'bin')):
1012 os.symlink(os.path.join(to_framework, fn),
1013 os.path.join(usr_local_bin, fn))
1014
1015 os.chdir(curdir)
1016
Ned Deily53c460d2011-01-30 01:43:40 +00001017 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001018 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001019 # with an already installed version of python 2
1020 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1021 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001022
1023def patchFile(inPath, outPath):
1024 data = fileContents(inPath)
1025 data = data.replace('$FULL_VERSION', getFullVersion())
1026 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001027 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001028 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001029 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +00001030
1031 # This one is not handy as a template variable
1032 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001033 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001034 fp.write(data)
1035 fp.close()
1036
1037def patchScript(inPath, outPath):
1038 data = fileContents(inPath)
1039 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001040 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001041 fp.write(data)
1042 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001043 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001044
1045
1046
1047def packageFromRecipe(targetDir, recipe):
1048 curdir = os.getcwd()
1049 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001050 # The major version (such as 2.5) is included in the package name
1051 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001052 # common.
1053 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001054 srcdir = recipe.get('source')
1055 pkgroot = recipe.get('topdir', srcdir)
1056 postflight = recipe.get('postflight')
1057 readme = textwrap.dedent(recipe['readme'])
1058 isRequired = recipe.get('required', True)
1059
Ned Deilye1c9794952013-01-29 00:07:46 -08001060 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001061
1062 # Substitute some variables
1063 textvars = dict(
1064 VER=getVersion(),
1065 FULLVER=getFullVersion(),
1066 )
1067 readme = readme % textvars
1068
1069 if pkgroot is not None:
1070 pkgroot = pkgroot % textvars
1071 else:
1072 pkgroot = '/'
1073
1074 if srcdir is not None:
1075 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1076 srcdir = srcdir % textvars
1077
1078 if postflight is not None:
1079 postflight = os.path.abspath(postflight)
1080
1081 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1082 os.makedirs(packageContents)
1083
1084 if srcdir is not None:
1085 os.chdir(srcdir)
1086 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1087 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1088 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1089
1090 fn = os.path.join(packageContents, 'PkgInfo')
1091 fp = open(fn, 'w')
1092 fp.write('pmkrpkg1')
1093 fp.close()
1094
1095 rsrcDir = os.path.join(packageContents, "Resources")
1096 os.mkdir(rsrcDir)
1097 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1098 fp.write(readme)
1099 fp.close()
1100
1101 if postflight is not None:
1102 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1103
1104 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001105 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001106 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001107 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1108 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1109 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001110 CFBundleShortVersionString=vers,
1111 IFMajorVersion=major,
1112 IFMinorVersion=minor,
1113 IFPkgFormatVersion=0.10000000149011612,
1114 IFPkgFlagAllowBackRev=False,
1115 IFPkgFlagAuthorizationAction="RootAuthorization",
1116 IFPkgFlagDefaultLocation=pkgroot,
1117 IFPkgFlagFollowLinks=True,
1118 IFPkgFlagInstallFat=True,
1119 IFPkgFlagIsRequired=isRequired,
1120 IFPkgFlagOverwritePermissions=False,
1121 IFPkgFlagRelocatable=False,
1122 IFPkgFlagRestartAction="NoRestart",
1123 IFPkgFlagRootVolumeOnly=True,
1124 IFPkgFlagUpdateInstalledLangauges=False,
1125 )
1126 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1127
1128 pl = Plist(
1129 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001130 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001131 IFPkgDescriptionVersion=vers,
1132 )
1133 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1134
1135 finally:
1136 os.chdir(curdir)
1137
1138
1139def makeMpkgPlist(path):
1140
1141 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001142 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001143
1144 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001145 CFBundleGetInfoString="Python %s"%(vers,),
1146 CFBundleIdentifier='org.python.Python',
1147 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001148 CFBundleShortVersionString=vers,
1149 IFMajorVersion=major,
1150 IFMinorVersion=minor,
1151 IFPkgFlagComponentDirectory="Contents/Packages",
1152 IFPkgFlagPackageList=[
1153 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001154 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001155 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001156 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001157 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001158 ],
1159 IFPkgFormatVersion=0.10000000149011612,
1160 IFPkgFlagBackgroundScaling="proportional",
1161 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001162 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001163 )
1164
1165 writePlist(pl, path)
1166
1167
1168def buildInstaller():
1169
1170 # Zap all compiled files
1171 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1172 for fn in filenames:
1173 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1174 os.unlink(os.path.join(dirpath, fn))
1175
1176 outdir = os.path.join(WORKDIR, 'installer')
1177 if os.path.exists(outdir):
1178 shutil.rmtree(outdir)
1179 os.mkdir(outdir)
1180
Ronald Oussoren508282e2009-03-30 19:34:51 +00001181 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001182 pkgcontents = os.path.join(pkgroot, 'Packages')
1183 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001184 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001185 packageFromRecipe(pkgcontents, recipe)
1186
1187 rsrcDir = os.path.join(pkgroot, 'Resources')
1188
1189 fn = os.path.join(pkgroot, 'PkgInfo')
1190 fp = open(fn, 'w')
1191 fp.write('pmkrpkg1')
1192 fp.close()
1193
1194 os.mkdir(rsrcDir)
1195
1196 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1197 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001198 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001199 IFPkgDescriptionVersion=getVersion(),
1200 )
1201
1202 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1203 for fn in os.listdir('resources'):
1204 if fn == '.svn': continue
1205 if fn.endswith('.jpg'):
1206 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1207 else:
1208 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1209
Ronald Oussorenc5555542006-06-11 20:24:45 +00001210 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001211
1212
1213def installSize(clear=False, _saved=[]):
1214 if clear:
1215 del _saved[:]
1216 if not _saved:
1217 data = captureCommand("du -ks %s"%(
1218 shellQuote(os.path.join(WORKDIR, '_root'))))
1219 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1220 return _saved[0]
1221
1222
1223def buildDMG():
1224 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001225 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001226 """
1227 outdir = os.path.join(WORKDIR, 'diskimage')
1228 if os.path.exists(outdir):
1229 shutil.rmtree(outdir)
1230
1231 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001232 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001233 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001234 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001235 imagepath = imagepath + '.dmg'
1236
1237 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001238 volname='Python %s'%(getFullVersion())
1239 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1240 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001241 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001242 shellQuote(imagepath + ".tmp.dmg" )))
1243
1244
1245 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1246 os.mkdir(os.path.join(WORKDIR, "mnt"))
1247 runCommand("hdiutil attach %s -mountroot %s"%(
1248 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1249
1250 # Custom icon for the DMG, shown when the DMG is mounted.
1251 shutil.copy("../Icons/Disk Image.icns",
1252 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001253 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001254 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1255
1256 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1257
1258 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1259 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1260 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1261 setIcon(imagepath, "../Icons/Disk Image.icns")
1262
1263 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001264
1265 return imagepath
1266
1267
1268def setIcon(filePath, icnsPath):
1269 """
1270 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001271 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001272
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001273 dirPath = os.path.normpath(os.path.dirname(__file__))
1274 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001275 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1276 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1277 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001278 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1279 if not os.path.exists(appPath):
1280 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001281 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1282 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001283
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001284 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1285 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001286
1287def main():
1288 # First parse options and check if we can perform our work
1289 parseOptions()
1290 checkEnvironment()
1291
Ronald Oussoren508282e2009-03-30 19:34:51 +00001292 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001293 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001294 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001295
1296 if os.path.exists(WORKDIR):
1297 shutil.rmtree(WORKDIR)
1298 os.mkdir(WORKDIR)
1299
Ronald Oussoren287128a2010-04-18 14:01:05 +00001300 os.environ['LC_ALL'] = 'C'
1301
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001302 # Then build third-party libraries such as sleepycat DB4.
1303 buildLibraries()
1304
1305 # Now build python itself
1306 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001307
1308 # And then build the documentation
1309 # Remove the Deployment Target from the shell
1310 # environment, it's no longer needed and
1311 # an unexpected build target can cause problems
1312 # when Sphinx and its dependencies need to
1313 # be (re-)installed.
1314 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001315 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001316
1317
1318 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001319 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001320 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001321 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001322
Benjamin Petersonc3104762008-10-03 11:52:06 +00001323 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001324 getVersion(),))
Ned Deilye1c9794952013-01-29 00:07:46 -08001325 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001326 setIcon(folder, "../Icons/Python Folder.icns")
1327
1328 # Create the installer
1329 buildInstaller()
1330
1331 # And copy the readme into the directory containing the installer
1332 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1333
1334 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001335 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001336
1337 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001338 fp.write("# BUILD INFO\n")
1339 fp.write("# Date: %s\n" % time.ctime())
1340 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001341 fp.close()
1342
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001343 # And copy it to a DMG
1344 buildDMG()
1345
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001346if __name__ == "__main__":
1347 main()