blob: a9d40912b09d505a25999a6e3fd99f0f590cab0e [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
196 if getVersionTuple() >= (3, 3):
197 result.extend([
198 dict(
199 name="XZ 5.0.3",
200 url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
201 checksum='fefe52f9ecd521de2a8ce38c21a27574',
202 configure_pre=[
203 '--disable-dependency-tracking',
204 ]
205 ),
206 ])
207
208 result.extend([
209 dict(
210 name="NCurses 5.9",
211 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
212 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
213 configure_pre=[
214 "--enable-widec",
215 "--without-cxx",
216 "--without-cxx-binding",
217 "--without-ada",
218 "--without-curses-h",
219 "--enable-shared",
220 "--with-shared",
221 "--without-debug",
222 "--without-normal",
223 "--without-tests",
224 "--without-manpages",
225 "--datadir=/usr/share",
226 "--sysconfdir=/etc",
227 "--sharedstatedir=/usr/com",
228 "--with-terminfo-dirs=/usr/share/terminfo",
229 "--with-default-terminfo-dir=/usr/share/terminfo",
230 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
231 ],
232 patchscripts=[
233 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
234 "f54bf02a349f96a7c4f0d00922f3a0d4"),
235 ],
236 useLDFlags=False,
237 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
238 shellQuote(os.path.join(WORKDIR, 'libraries')),
239 shellQuote(os.path.join(WORKDIR, 'libraries')),
240 getVersion(),
241 ),
242 ),
243 dict(
244 name="SQLite 3.7.13",
245 url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
246 checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
247 extra_cflags=('-Os '
248 '-DSQLITE_ENABLE_FTS4 '
249 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
250 '-DSQLITE_ENABLE_RTREE '
251 '-DSQLITE_TCL=0 '
252 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
253 configure_pre=[
254 '--enable-threadsafe',
255 '--enable-shared=no',
256 '--enable-static=yes',
257 '--disable-readline',
258 '--disable-dependency-tracking',
259 ]
260 ),
261 ])
262
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000263 if DEPTARGET < '10.5':
264 result.extend([
265 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000266 name="Bzip2 1.0.6",
267 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
268 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000269 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800270 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
271 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000272 shellQuote(os.path.join(WORKDIR, 'libraries')),
273 ' -arch '.join(ARCHLIST),
274 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000275 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000276 ),
277 dict(
278 name="ZLib 1.2.3",
279 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
280 checksum='debc62758716a169df9f62e6ab2bc634',
281 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800282 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
283 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000284 shellQuote(os.path.join(WORKDIR, 'libraries')),
285 ' -arch '.join(ARCHLIST),
286 SDKPATH,
287 ),
288 ),
289 dict(
290 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000291 name="GNU Readline 6.1.2",
292 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
293 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000294 patchlevel='0',
295 patches=[
296 # The readline maintainers don't do actual micro releases, but
297 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800298 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
299 'c642f2e84d820884b0bf9fd176bc6c3f'),
300 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
301 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000302 ]
303 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000304 ])
305
Ned Deily53c460d2011-01-30 01:43:40 +0000306 if not PYTHON_3:
307 result.extend([
308 dict(
309 name="Sleepycat DB 4.7.25",
310 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
311 checksum='ec2b87e833779681a0c3a814aa71359e',
312 buildDir="build_unix",
313 configure="../dist/configure",
314 configure_pre=[
315 '--includedir=/usr/local/include/db4',
316 ]
317 ),
318 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000319
320 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000321
322
323# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000324def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000325 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000326 result = [
327 dict(
328 name="PythonFramework",
329 long_name="Python Framework",
330 source="/Library/Frameworks/Python.framework",
331 readme="""\
332 This package installs Python.framework, that is the python
333 interpreter and the standard library. This also includes Python
334 wrappers for lots of Mac OS X API's.
335 """,
336 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000337 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000338 ),
339 dict(
340 name="PythonApplications",
341 long_name="GUI Applications",
342 source="/Applications/Python %(VER)s",
343 readme="""\
344 This package installs IDLE (an interactive Python IDE),
345 Python Launcher and Build Applet (create application bundles
346 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000347
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000348 It also installs a number of examples and demos.
349 """,
350 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000351 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000352 ),
353 dict(
354 name="PythonUnixTools",
355 long_name="UNIX command-line tools",
356 source="/usr/local/bin",
357 readme="""\
358 This package installs the unix tools in /usr/local/bin for
359 compatibility with older releases of Python. This package
360 is not necessary to use Python.
361 """,
362 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000363 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000364 ),
365 dict(
366 name="PythonDocumentation",
367 long_name="Python Documentation",
368 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
369 source="/pydocs",
370 readme="""\
371 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800372 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000373 """,
374 postflight="scripts/postflight.documentation",
375 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000376 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000377 ),
378 dict(
379 name="PythonProfileChanges",
380 long_name="Shell profile updater",
381 readme="""\
382 This packages updates your shell profile to make sure that
383 the Python tools are found by your shell in preference of
384 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000385
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000386 If you don't install this package you'll have to add
387 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
388 to your PATH by hand.
389 """,
390 postflight="scripts/postflight.patch-profile",
391 topdir="/Library/Frameworks/Python.framework",
392 source="/empty-dir",
393 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000394 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000395 ),
396 ]
397
Ned Deilye1c9794952013-01-29 00:07:46 -0800398 if DEPTARGET < '10.4' and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000399 result.append(
400 dict(
401 name="PythonSystemFixes",
402 long_name="Fix system Python",
403 readme="""\
404 This package updates the system python installation on
405 Mac OS X 10.3 to ensure that you can build new python extensions
406 using that copy of python after installing this version.
407 """,
408 postflight="../Tools/fixapplepython23.py",
409 topdir="/Library/Frameworks/Python.framework",
410 source="/empty-dir",
411 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000412 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000413 )
414 )
415 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000416
417def fatal(msg):
418 """
419 A fatal error, bail out.
420 """
421 sys.stderr.write('FATAL: ')
422 sys.stderr.write(msg)
423 sys.stderr.write('\n')
424 sys.exit(1)
425
426def fileContents(fn):
427 """
428 Return the contents of the named file
429 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800430 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000431
432def runCommand(commandline):
433 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200434 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000435 unless the command fails.
436 """
437 fd = os.popen(commandline, 'r')
438 data = fd.read()
439 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000440 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000441 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800442 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000443
444 if VERBOSE:
445 sys.stdout.write(data); sys.stdout.flush()
446
447def captureCommand(commandline):
448 fd = os.popen(commandline, 'r')
449 data = fd.read()
450 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000451 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000452 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800453 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000454
455 return data
456
Ronald Oussoren287128a2010-04-18 14:01:05 +0000457def getTclTkVersion(configfile, versionline):
458 """
459 search Tcl or Tk configuration file for version line
460 """
461 try:
462 f = open(configfile, "r")
463 except:
464 fatal("Framework configuration file not found: %s" % configfile)
465
466 for l in f:
467 if l.startswith(versionline):
468 f.close()
469 return l
470
471 fatal("Version variable %s not found in framework configuration file: %s"
472 % (versionline, configfile))
473
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000474def checkEnvironment():
475 """
476 Check that we're running on a supported system.
477 """
478
Ned Deily53c460d2011-01-30 01:43:40 +0000479 if sys.version_info[0:2] < (2, 4):
480 fatal("This script must be run with Python 2.4 or later")
481
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000482 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000483 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000484
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000485 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000486 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000487
488 if not os.path.exists(SDKPATH):
489 fatal("Please install the latest version of Xcode and the %s SDK"%(
490 os.path.basename(SDKPATH[:-4])))
491
Ronald Oussoren287128a2010-04-18 14:01:05 +0000492 # Because we only support dynamic load of only one major/minor version of
493 # Tcl/Tk, ensure:
494 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800495 # higher than the Apple-supplied system version in
496 # SDKROOT/System/Library/Frameworks
497 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
498 # in) SDKROOT/Library/Frameworks with the same version as the system
499 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000500
Ned Deilye1c9794952013-01-29 00:07:46 -0800501 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000502 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800503 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000504 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800505 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000506 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800507 frameworks[framework] = os.readlink(sysfw)
508 if not os.path.exists(libfw):
509 fatal("Please install a link to a current %s %s as %s so "
510 "the user can override the system framework."
511 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000512 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000513 fatal("Version of %s must match %s" % (libfw, sysfw) )
514 if os.path.exists(usrfw):
515 fatal("Please rename %s to avoid possible dynamic load issues."
516 % usrfw)
517
Ned Deilye1c9794952013-01-29 00:07:46 -0800518 if frameworks['Tcl'] != frameworks['Tk']:
519 fatal("The Tcl and Tk frameworks are not the same version.")
520
521 # add files to check after build
522 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
523 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
524 % frameworks['Tcl'],
525 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
526 % frameworks['Tk'],
527 ]
528
Ronald Oussoren287128a2010-04-18 14:01:05 +0000529 # Remove inherited environment variables which might influence build
530 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
531 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
532 for ev in list(os.environ):
533 for prefix in environ_var_prefixes:
534 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800535 print("INFO: deleting environment variable %s=%s" % (
536 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000537 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000538
Ned Deilye1c9794952013-01-29 00:07:46 -0800539 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
540 if 'SDK_TOOLS_BIN' in os.environ:
541 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
542 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
543 # add its fixed location here if it exists
544 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
545 if os.path.isdir(OLD_DEVELOPER_TOOLS):
546 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
547 os.environ['PATH'] = base_path
548 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussorenac4b7ad2010-04-20 05:50:44 +0000549
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000550
Ronald Oussoren158ad592006-11-07 16:00:34 +0000551def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000552 """
553 Parse arguments and update global settings.
554 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000555 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800556 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000557
558 if args is None:
559 args = sys.argv[1:]
560
561 try:
562 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000563 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
564 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800565 except getopt.GetoptError:
566 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000567 sys.exit(1)
568
569 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800570 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000571 sys.exit(1)
572
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000573 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000574 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000575 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800576 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000577 sys.exit(0)
578
579 elif k in ('-d', '--build-dir'):
580 WORKDIR=v
581
582 elif k in ('--third-party',):
583 DEPSRC=v
584
585 elif k in ('--sdk-path',):
586 SDKPATH=v
587
588 elif k in ('--src-dir',):
589 SRCDIR=v
590
Ronald Oussoren508282e2009-03-30 19:34:51 +0000591 elif k in ('--dep-target', ):
592 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000593 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000594
595 elif k in ('--universal-archs', ):
596 if v in UNIVERSALOPTS:
597 UNIVERSALARCHS = v
598 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000599 if deptarget is None:
600 # Select alternate default deployment
601 # target
602 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000603 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800604 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000605
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000606 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800607 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000608
609 SRCDIR=os.path.abspath(SRCDIR)
610 WORKDIR=os.path.abspath(WORKDIR)
611 SDKPATH=os.path.abspath(SDKPATH)
612 DEPSRC=os.path.abspath(DEPSRC)
613
Ned Deilye1c9794952013-01-29 00:07:46 -0800614 CC, CXX=target_cc_map[DEPTARGET]
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000615
Ned Deilye1c9794952013-01-29 00:07:46 -0800616 print("Settings:")
617 print(" * Source directory:", SRCDIR)
618 print(" * Build directory: ", WORKDIR)
619 print(" * SDK location: ", SDKPATH)
620 print(" * Third-party source:", DEPSRC)
621 print(" * Deployment target:", DEPTARGET)
622 print(" * Universal architectures:", ARCHLIST)
623 print(" * C compiler:", CC)
624 print(" * C++ compiler:", CXX)
625 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000626
627
628
629
630def extractArchive(builddir, archiveName):
631 """
632 Extract a source archive into 'builddir'. Returns the path of the
633 extracted archive.
634
635 XXX: This function assumes that archives contain a toplevel directory
636 that is has the same name as the basename of the archive. This is
637 save enough for anything we use.
638 """
639 curdir = os.getcwd()
640 try:
641 os.chdir(builddir)
642 if archiveName.endswith('.tar.gz'):
643 retval = os.path.basename(archiveName[:-7])
644 if os.path.exists(retval):
645 shutil.rmtree(retval)
646 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
647
648 elif archiveName.endswith('.tar.bz2'):
649 retval = os.path.basename(archiveName[:-8])
650 if os.path.exists(retval):
651 shutil.rmtree(retval)
652 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
653
654 elif archiveName.endswith('.tar'):
655 retval = os.path.basename(archiveName[:-4])
656 if os.path.exists(retval):
657 shutil.rmtree(retval)
658 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
659
660 elif archiveName.endswith('.zip'):
661 retval = os.path.basename(archiveName[:-4])
662 if os.path.exists(retval):
663 shutil.rmtree(retval)
664 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
665
666 data = fp.read()
667 xit = fp.close()
668 if xit is not None:
669 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800670 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000671
672 return os.path.join(builddir, retval)
673
674 finally:
675 os.chdir(curdir)
676
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000677def downloadURL(url, fname):
678 """
679 Download the contents of the url into the file.
680 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800681 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000682 fpOut = open(fname, 'wb')
683 block = fpIn.read(10240)
684 try:
685 while block:
686 fpOut.write(block)
687 block = fpIn.read(10240)
688 fpIn.close()
689 fpOut.close()
690 except:
691 try:
692 os.unlink(fname)
693 except:
694 pass
695
Ned Deilye1c9794952013-01-29 00:07:46 -0800696def verifyThirdPartyFile(url, checksum, fname):
697 """
698 Download file from url to filename fname if it does not already exist.
699 Abort if file contents does not match supplied md5 checksum.
700 """
701 name = os.path.basename(fname)
702 if os.path.exists(fname):
703 print("Using local copy of %s"%(name,))
704 else:
705 print("Did not find local copy of %s"%(name,))
706 print("Downloading %s"%(name,))
707 downloadURL(url, fname)
708 print("Archive for %s stored as %s"%(name, fname))
709 if os.system(
710 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
711 % (shellQuote(fname), checksum) ):
712 fatal('MD5 checksum mismatch for file %s' % fname)
713
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000714def buildRecipe(recipe, basedir, archList):
715 """
716 Build software using a recipe. This function does the
717 'configure;make;make install' dance for C software, with a possibility
718 to customize this process, basically a poor-mans DarwinPorts.
719 """
720 curdir = os.getcwd()
721
722 name = recipe['name']
723 url = recipe['url']
724 configure = recipe.get('configure', './configure')
725 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
726 shellQuote(basedir)))
727
728 archiveName = os.path.split(url)[-1]
729 sourceArchive = os.path.join(DEPSRC, archiveName)
730
731 if not os.path.exists(DEPSRC):
732 os.mkdir(DEPSRC)
733
Ned Deilye1c9794952013-01-29 00:07:46 -0800734 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
735 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000736 buildDir=os.path.join(WORKDIR, '_bld')
737 if not os.path.exists(buildDir):
738 os.mkdir(buildDir)
739
740 workDir = extractArchive(buildDir, sourceArchive)
741 os.chdir(workDir)
742 if 'buildDir' in recipe:
743 os.chdir(recipe['buildDir'])
744
Ned Deilye1c9794952013-01-29 00:07:46 -0800745 for patch in recipe.get('patches', ()):
746 if isinstance(patch, tuple):
747 url, checksum = patch
748 fn = os.path.join(DEPSRC, os.path.basename(url))
749 verifyThirdPartyFile(url, checksum, fn)
750 else:
751 # patch is a file in the source directory
752 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000753 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
754 shellQuote(fn),))
755
Ned Deilye1c9794952013-01-29 00:07:46 -0800756 for patchscript in recipe.get('patchscripts', ()):
757 if isinstance(patchscript, tuple):
758 url, checksum = patchscript
759 fn = os.path.join(DEPSRC, os.path.basename(url))
760 verifyThirdPartyFile(url, checksum, fn)
761 else:
762 # patch is a file in the source directory
763 fn = os.path.join(curdir, patchscript)
764 if fn.endswith('.bz2'):
765 runCommand('bunzip2 -fk %s' % shellQuote(fn))
766 fn = fn[:-4]
767 runCommand('sh %s' % shellQuote(fn))
768 os.unlink(fn)
769
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000770 if configure is not None:
771 configure_args = [
772 "--prefix=/usr/local",
773 "--enable-static",
774 "--disable-shared",
775 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
776 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000777
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000778 if 'configure_pre' in recipe:
779 args = list(recipe['configure_pre'])
780 if '--disable-static' in args:
781 configure_args.remove('--enable-static')
782 if '--enable-shared' in args:
783 configure_args.remove('--disable-shared')
784 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000785
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000786 if recipe.get('useLDFlags', 1):
787 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800788 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
789 "-I%s/usr/local/include"%(
790 recipe.get('extra_cflags', ''),
791 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000792 ' -arch '.join(archList),
793 shellQuote(SDKPATH)[1:-1],
794 shellQuote(basedir)[1:-1],),
Ned Deilye1c9794952013-01-29 00:07:46 -0800795 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
796 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000797 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000798 shellQuote(basedir)[1:-1],
799 ' -arch '.join(archList)),
800 ])
801 else:
802 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800803 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
804 "-I%s/usr/local/include"%(
805 recipe.get('extra_cflags', ''),
806 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000807 ' -arch '.join(archList),
808 shellQuote(SDKPATH)[1:-1],
809 shellQuote(basedir)[1:-1],),
810 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000811
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000812 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -0800813 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000814
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000815 configure_args.insert(0, configure)
816 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000817
Ned Deilye1c9794952013-01-29 00:07:46 -0800818 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000819 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000820
Ned Deilye1c9794952013-01-29 00:07:46 -0800821 print("Running install for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000822 runCommand('{ ' + install + ' ;} 2>&1')
823
Ned Deilye1c9794952013-01-29 00:07:46 -0800824 print("Done %s"%(name,))
825 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000826
827 os.chdir(curdir)
828
829def buildLibraries():
830 """
831 Build our dependencies into $WORKDIR/libraries/usr/local
832 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800833 print("")
834 print("Building required libraries")
835 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000836 universal = os.path.join(WORKDIR, 'libraries')
837 os.mkdir(universal)
838 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
839 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
840
Ronald Oussoren508282e2009-03-30 19:34:51 +0000841 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000842 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000843
844
845
846def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000847 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000848 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -0800849 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000850 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000851 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000852 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000853 curDir = os.getcwd()
854 os.chdir(buildDir)
855 runCommand('make update')
Martin v. Löwis896c4772010-04-22 11:34:36 +0000856 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000857 os.chdir(curDir)
858 if not os.path.exists(docdir):
859 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000860 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000861
862
863def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -0800864 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000865
866 buildDir = os.path.join(WORKDIR, '_bld', 'python')
867 rootDir = os.path.join(WORKDIR, '_root')
868
869 if os.path.exists(buildDir):
870 shutil.rmtree(buildDir)
871 if os.path.exists(rootDir):
872 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +0000873 os.makedirs(buildDir)
874 os.makedirs(rootDir)
875 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000876 curdir = os.getcwd()
877 os.chdir(buildDir)
878
879 # Not sure if this is still needed, the original build script
880 # claims that parts of the install assume python.exe exists.
881 os.symlink('python', os.path.join(buildDir, 'python.exe'))
882
883 # Extract the version from the configure file, needed to calculate
884 # several paths.
885 version = getVersion()
886
Ronald Oussoren008af852009-03-30 20:02:08 +0000887 # Since the extra libs are not in their installed framework location
888 # during the build, augment the library path so that the interpreter
889 # will find them during its extension import sanity checks.
890 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
891 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -0800892 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +0000893 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
894 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +0000895 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +0000896 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
897 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
898 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
899 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +0000900 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +0000901 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000902 shellQuote(WORKDIR)[1:-1]))
903
Ned Deilye1c9794952013-01-29 00:07:46 -0800904 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000905 runCommand("make")
906
Ned Deilye1c9794952013-01-29 00:07:46 -0800907 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +0000908 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000909 shellQuote(rootDir)))
910
Ned Deilye1c9794952013-01-29 00:07:46 -0800911 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000912 runCommand("make frameworkinstallextras DESTDIR=%s"%(
913 shellQuote(rootDir)))
914
Ronald Oussoren008af852009-03-30 20:02:08 +0000915 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -0800916 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000917 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
918 runCommand("mv %s/* %s"%(
919 shellQuote(os.path.join(
920 WORKDIR, 'libraries', 'Library', 'Frameworks',
921 'Python.framework', 'Versions', getVersion(),
922 'lib')),
923 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
924 'Python.framework', 'Versions', getVersion(),
925 'lib'))))
926
Ned Deilye1c9794952013-01-29 00:07:46 -0800927 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000928 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000929 gid = grp.getgrnam('admin').gr_gid
930
Ned Deilye1c9794952013-01-29 00:07:46 -0800931 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000932 for dirpath, dirnames, filenames in os.walk(frmDir):
933 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -0800934 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000935 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +0000936
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000937 for fn in filenames:
938 if os.path.islink(fn):
939 continue
940
941 # "chmod g+w $fn"
942 p = os.path.join(dirpath, fn)
943 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000944 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
945 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000946
Ned Deilye1c9794952013-01-29 00:07:46 -0800947 if fn in EXPECTED_SHARED_LIBS:
948 # check to see that this file was linked with the
949 # expected library path and version
950 data = captureCommand("otool -L %s" % shellQuote(p))
951 for sl in EXPECTED_SHARED_LIBS[fn]:
952 if ("\t%s " % sl) not in data:
953 print("Expected shared lib %s was not linked with %s"
954 % (sl, p))
955 shared_lib_error = True
956
957 if shared_lib_error:
958 fatal("Unexpected shared library errors.")
959
Ned Deily53c460d2011-01-30 01:43:40 +0000960 if PYTHON_3:
961 LDVERSION=None
962 VERSION=None
963 ABIFLAGS=None
964
965 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
966 for ln in fp:
967 if ln.startswith('VERSION='):
968 VERSION=ln.split()[1]
969 if ln.startswith('ABIFLAGS='):
970 ABIFLAGS=ln.split()[1]
971 if ln.startswith('LDVERSION='):
972 LDVERSION=ln.split()[1]
973 fp.close()
974
975 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
976 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
977 config_suffix = '-' + LDVERSION
978 else:
979 config_suffix = '' # Python 2.x
980
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000981 # We added some directories to the search path during the configure
982 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -0800983 # the end-users system. Also remove the directories from _sysconfigdata.py
984 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000985
Ned Deilye1c9794952013-01-29 00:07:46 -0800986 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
987 'Python.framework', 'Versions',
988 version, 'lib', 'python%s'%(version,))
989 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
990 os.path.join(path_to_lib, '_sysconfigdata.py')]
991 for path in paths:
992 if not os.path.exists(path):
993 continue
994 fp = open(path, 'r')
995 data = fp.read()
996 fp.close()
997
998 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
999 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
1000 fp = open(path, 'w')
1001 fp.write(data)
1002 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001003
1004 # Add symlinks in /usr/local/bin, using relative links
1005 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1006 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1007 'Python.framework', 'Versions', version, 'bin')
1008 if os.path.exists(usr_local_bin):
1009 shutil.rmtree(usr_local_bin)
1010 os.makedirs(usr_local_bin)
1011 for fn in os.listdir(
1012 os.path.join(frmDir, 'Versions', version, 'bin')):
1013 os.symlink(os.path.join(to_framework, fn),
1014 os.path.join(usr_local_bin, fn))
1015
1016 os.chdir(curdir)
1017
Ned Deily53c460d2011-01-30 01:43:40 +00001018 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001019 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001020 # with an already installed version of python 2
1021 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1022 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001023
1024def patchFile(inPath, outPath):
1025 data = fileContents(inPath)
1026 data = data.replace('$FULL_VERSION', getFullVersion())
1027 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001028 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001029 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001030 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +00001031
1032 # This one is not handy as a template variable
1033 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001034 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001035 fp.write(data)
1036 fp.close()
1037
1038def patchScript(inPath, outPath):
1039 data = fileContents(inPath)
1040 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001041 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001042 fp.write(data)
1043 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001044 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001045
1046
1047
1048def packageFromRecipe(targetDir, recipe):
1049 curdir = os.getcwd()
1050 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001051 # The major version (such as 2.5) is included in the package name
1052 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001053 # common.
1054 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001055 srcdir = recipe.get('source')
1056 pkgroot = recipe.get('topdir', srcdir)
1057 postflight = recipe.get('postflight')
1058 readme = textwrap.dedent(recipe['readme'])
1059 isRequired = recipe.get('required', True)
1060
Ned Deilye1c9794952013-01-29 00:07:46 -08001061 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001062
1063 # Substitute some variables
1064 textvars = dict(
1065 VER=getVersion(),
1066 FULLVER=getFullVersion(),
1067 )
1068 readme = readme % textvars
1069
1070 if pkgroot is not None:
1071 pkgroot = pkgroot % textvars
1072 else:
1073 pkgroot = '/'
1074
1075 if srcdir is not None:
1076 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1077 srcdir = srcdir % textvars
1078
1079 if postflight is not None:
1080 postflight = os.path.abspath(postflight)
1081
1082 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1083 os.makedirs(packageContents)
1084
1085 if srcdir is not None:
1086 os.chdir(srcdir)
1087 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1088 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1089 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1090
1091 fn = os.path.join(packageContents, 'PkgInfo')
1092 fp = open(fn, 'w')
1093 fp.write('pmkrpkg1')
1094 fp.close()
1095
1096 rsrcDir = os.path.join(packageContents, "Resources")
1097 os.mkdir(rsrcDir)
1098 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1099 fp.write(readme)
1100 fp.close()
1101
1102 if postflight is not None:
1103 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1104
1105 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001106 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001107 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001108 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1109 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1110 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001111 CFBundleShortVersionString=vers,
1112 IFMajorVersion=major,
1113 IFMinorVersion=minor,
1114 IFPkgFormatVersion=0.10000000149011612,
1115 IFPkgFlagAllowBackRev=False,
1116 IFPkgFlagAuthorizationAction="RootAuthorization",
1117 IFPkgFlagDefaultLocation=pkgroot,
1118 IFPkgFlagFollowLinks=True,
1119 IFPkgFlagInstallFat=True,
1120 IFPkgFlagIsRequired=isRequired,
1121 IFPkgFlagOverwritePermissions=False,
1122 IFPkgFlagRelocatable=False,
1123 IFPkgFlagRestartAction="NoRestart",
1124 IFPkgFlagRootVolumeOnly=True,
1125 IFPkgFlagUpdateInstalledLangauges=False,
1126 )
1127 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1128
1129 pl = Plist(
1130 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001131 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001132 IFPkgDescriptionVersion=vers,
1133 )
1134 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1135
1136 finally:
1137 os.chdir(curdir)
1138
1139
1140def makeMpkgPlist(path):
1141
1142 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001143 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001144
1145 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001146 CFBundleGetInfoString="Python %s"%(vers,),
1147 CFBundleIdentifier='org.python.Python',
1148 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001149 CFBundleShortVersionString=vers,
1150 IFMajorVersion=major,
1151 IFMinorVersion=minor,
1152 IFPkgFlagComponentDirectory="Contents/Packages",
1153 IFPkgFlagPackageList=[
1154 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001155 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001156 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001157 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001158 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001159 ],
1160 IFPkgFormatVersion=0.10000000149011612,
1161 IFPkgFlagBackgroundScaling="proportional",
1162 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001163 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001164 )
1165
1166 writePlist(pl, path)
1167
1168
1169def buildInstaller():
1170
1171 # Zap all compiled files
1172 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1173 for fn in filenames:
1174 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1175 os.unlink(os.path.join(dirpath, fn))
1176
1177 outdir = os.path.join(WORKDIR, 'installer')
1178 if os.path.exists(outdir):
1179 shutil.rmtree(outdir)
1180 os.mkdir(outdir)
1181
Ronald Oussoren508282e2009-03-30 19:34:51 +00001182 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001183 pkgcontents = os.path.join(pkgroot, 'Packages')
1184 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001185 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001186 packageFromRecipe(pkgcontents, recipe)
1187
1188 rsrcDir = os.path.join(pkgroot, 'Resources')
1189
1190 fn = os.path.join(pkgroot, 'PkgInfo')
1191 fp = open(fn, 'w')
1192 fp.write('pmkrpkg1')
1193 fp.close()
1194
1195 os.mkdir(rsrcDir)
1196
1197 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1198 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001199 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001200 IFPkgDescriptionVersion=getVersion(),
1201 )
1202
1203 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1204 for fn in os.listdir('resources'):
1205 if fn == '.svn': continue
1206 if fn.endswith('.jpg'):
1207 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1208 else:
1209 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1210
Ronald Oussorenc5555542006-06-11 20:24:45 +00001211 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001212
1213
1214def installSize(clear=False, _saved=[]):
1215 if clear:
1216 del _saved[:]
1217 if not _saved:
1218 data = captureCommand("du -ks %s"%(
1219 shellQuote(os.path.join(WORKDIR, '_root'))))
1220 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1221 return _saved[0]
1222
1223
1224def buildDMG():
1225 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001226 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001227 """
1228 outdir = os.path.join(WORKDIR, 'diskimage')
1229 if os.path.exists(outdir):
1230 shutil.rmtree(outdir)
1231
1232 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001233 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001234 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001235 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001236 imagepath = imagepath + '.dmg'
1237
1238 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001239 volname='Python %s'%(getFullVersion())
1240 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1241 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001242 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001243 shellQuote(imagepath + ".tmp.dmg" )))
1244
1245
1246 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1247 os.mkdir(os.path.join(WORKDIR, "mnt"))
1248 runCommand("hdiutil attach %s -mountroot %s"%(
1249 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1250
1251 # Custom icon for the DMG, shown when the DMG is mounted.
1252 shutil.copy("../Icons/Disk Image.icns",
1253 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001254 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001255 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1256
1257 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1258
1259 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1260 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1261 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1262 setIcon(imagepath, "../Icons/Disk Image.icns")
1263
1264 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001265
1266 return imagepath
1267
1268
1269def setIcon(filePath, icnsPath):
1270 """
1271 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001272 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001273
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001274 dirPath = os.path.normpath(os.path.dirname(__file__))
1275 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001276 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1277 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1278 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001279 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1280 if not os.path.exists(appPath):
1281 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001282 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1283 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001284
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001285 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1286 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001287
1288def main():
1289 # First parse options and check if we can perform our work
1290 parseOptions()
1291 checkEnvironment()
1292
Ronald Oussoren508282e2009-03-30 19:34:51 +00001293 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001294 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001295 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001296
1297 if os.path.exists(WORKDIR):
1298 shutil.rmtree(WORKDIR)
1299 os.mkdir(WORKDIR)
1300
Ronald Oussoren287128a2010-04-18 14:01:05 +00001301 os.environ['LC_ALL'] = 'C'
1302
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001303 # Then build third-party libraries such as sleepycat DB4.
1304 buildLibraries()
1305
1306 # Now build python itself
1307 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001308
1309 # And then build the documentation
1310 # Remove the Deployment Target from the shell
1311 # environment, it's no longer needed and
1312 # an unexpected build target can cause problems
1313 # when Sphinx and its dependencies need to
1314 # be (re-)installed.
1315 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001316 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001317
1318
1319 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001320 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001321 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001322 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001323
Benjamin Petersonc3104762008-10-03 11:52:06 +00001324 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001325 getVersion(),))
Ned Deilye1c9794952013-01-29 00:07:46 -08001326 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001327 setIcon(folder, "../Icons/Python Folder.icns")
1328
1329 # Create the installer
1330 buildInstaller()
1331
1332 # And copy the readme into the directory containing the installer
1333 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1334
1335 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001336 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001337
1338 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001339 fp.write("# BUILD INFO\n")
1340 fp.write("# Date: %s\n" % time.ctime())
1341 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001342 fp.close()
1343
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001344 # And copy it to a DMG
1345 buildDMG()
1346
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001347if __name__ == "__main__":
1348 main()