blob: 64c6171294ef24b927cd65258238a2b5668d1ff6 [file] [log] [blame]
Ned Deily4a96a372013-01-29 00:08:32 -08001#!/usr/bin/env python
Thomas Wouters477c8d52006-05-27 19:21:47 +00002"""
Ned Deilye59e4c52011-01-29 18:56:28 +00003This script is used to build "official" universal installers on Mac OS X.
Ned Deily4a96a372013-01-29 00:08:32 -08004It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
Ned Deilye59e4c52011-01-29 18:56:28 +0000532-bit builds. 64-bit or four-way universal builds require at least
6OS X 10.5 and the 10.5 SDK.
Thomas Wouters477c8d52006-05-27 19:21:47 +00007
Ned Deilye59e4c52011-01-29 18:56:28 +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.
Thomas Wouters477c8d52006-05-27 19:21:47 +000012
Ned Deily4a96a372013-01-29 00:08:32 -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
Thomas Wouters477c8d52006-05-27 19:21:47 +000027Usage: see USAGE variable in the script.
28"""
Ned Deily4a96a372013-01-29 00:08:32 -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 )
Thomas Wouters477c8d52006-05-27 19:21:47 +000042
Thomas Wouters89f507f2006-12-13 04:49:30 +000043INCLUDE_TIMESTAMP = 1
44VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000045
46from plistlib import Plist
47
Thomas Wouters477c8d52006-05-27 19:21:47 +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 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000057 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -080068 raise RuntimeError("Cannot find variable %s" % variable[:-1])
69
70_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000071
72def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -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
Thomas Wouters477c8d52006-05-27 19:21:47 +000078
Benjamin Petersond9b7d482010-03-19 21:42:45 +000079def getVersionTuple():
80 return tuple([int(n) for n in getVersion().split('.')])
81
Ned Deily4a96a372013-01-29 00:08:32 -080082def getVersionMajorMinor():
83 return tuple([int(n) for n in getVersion().split('.', 2)])
84
85_cache_getFullVersion = None
86
Thomas Wouters477c8d52006-05-27 19:21:47 +000087def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080088 global _cache_getFullVersion
89 if _cache_getFullVersion is not None:
90 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000091 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
92 for ln in open(fn):
93 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -080094 _cache_getFullVersion = ln.split()[-1][1:-1]
95 return _cache_getFullVersion
96 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +000097
Thomas Wouters89f507f2006-12-13 04:49:30 +000098# The directory we'll use to create the build (will be erased and recreated)
99WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000100
Thomas Wouters89f507f2006-12-13 04:49:30 +0000101# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000102# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000103DEPSRC = os.path.join(WORKDIR, 'third-party')
104DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000105
106# Location of the preferred SDK
Benjamin Petersond9b7d482010-03-19 21:42:45 +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
111### isue is resolved.
112###
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
Thomas Wouters89f507f2006-12-13 04:49:30 +0000121SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000122
Ronald Oussoren1943f862009-03-30 19:39:14 +0000123universal_opts_map = { '32-bit': ('i386', 'ppc',),
124 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Oussoren1943f862009-03-30 19:39:14 +0000134
135UNIVERSALOPTS = tuple(universal_opts_map.keys())
136
137UNIVERSALARCHS = '32-bit'
138
139ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000140
Ezio Melotti42da6632011-03-15 05:18:48 +0200141# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000142SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000143 os.path.dirname(
144 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000145 os.path.abspath(__file__
146 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000147
Ronald Oussoren1943f862009-03-30 19:39:14 +0000148# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
149DEPTARGET = '10.3'
150
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000151target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -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++'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000158}
159
Ned Deily4a96a372013-01-29 00:08:32 -0800160CC, CXX = target_cc_map[DEPTARGET]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000161
162PYTHON_3 = getVersionTuple() >= (3, 0)
163
Thomas Wouters89f507f2006-12-13 04:49:30 +0000164USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +0000174 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
175 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000176""")% globals()
177
Ned Deily4a96a372013-01-29 00:08:32 -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 = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000185
186# Instructions for building libraries that are necessary for building a
187# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000188# [The recipes are defined here for convenience but instantiated later after
189# command line options have been processed.]
190def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000191 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000192
Ned Deily4a96a372013-01-29 00:08:32 -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
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000262 if DEPTARGET < '10.5':
263 result.extend([
264 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000265 name="Bzip2 1.0.6",
266 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
267 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000268 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800269 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
270 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000271 shellQuote(os.path.join(WORKDIR, 'libraries')),
272 ' -arch '.join(ARCHLIST),
273 SDKPATH,
274 ),
275 ),
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 Deily4a96a372013-01-29 00:08:32 -0800281 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
282 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Deily4f7ff782011-01-15 05:29:12 +0000290 name="GNU Readline 6.1.2",
291 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
292 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000293 patchlevel='0',
294 patches=[
295 # The readline maintainers don't do actual micro releases, but
296 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -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'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000301 ]
302 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000303 ])
304
Ned Deily4f7ff782011-01-15 05:29:12 +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 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000318
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000319 return result
320
Thomas Wouters477c8d52006-05-27 19:21:47 +0000321
Thomas Wouters477c8d52006-05-27 19:21:47 +0000322# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000323def pkg_recipes():
324 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
325 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",
336 selected='selected',
337 ),
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).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000346
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000347 It also installs a number of examples and demos.
348 """,
349 required=False,
350 selected='selected',
351 ),
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,
362 selected='selected',
363 ),
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 Deily4a96a372013-01-29 00:08:32 -0800371 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000372 """,
373 postflight="scripts/postflight.documentation",
374 required=False,
375 selected='selected',
376 ),
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.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000384
Benjamin Petersond9b7d482010-03-19 21:42:45 +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,
393 selected=unselected_for_python3,
394 ),
395 ]
396
Ned Deily4a96a372013-01-29 00:08:32 -0800397 if DEPTARGET < '10.4' and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +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,
411 selected=unselected_for_python3,
412 )
413 )
414 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000415
Thomas Wouters477c8d52006-05-27 19:21:47 +0000416def 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 Deily4a96a372013-01-29 00:08:32 -0800429 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000430
431def runCommand(commandline):
432 """
Ezio Melotti13925002011-03-16 11:05:33 +0200433 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000434 unless the command fails.
435 """
436 fd = os.popen(commandline, 'r')
437 data = fd.read()
438 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000439 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000440 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800441 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Peterson2a691a82008-03-31 01:51:45 +0000450 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000451 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800452 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000453
454 return data
455
Ronald Oussorenc45c3d92010-04-18 15:24:17 +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
Thomas Wouters477c8d52006-05-27 19:21:47 +0000473def checkEnvironment():
474 """
475 Check that we're running on a supported system.
476 """
477
Ned Deilye59e4c52011-01-29 18:56:28 +0000478 if sys.version_info[0:2] < (2, 4):
479 fatal("This script must be run with Python 2.4 or later")
480
Thomas Wouters477c8d52006-05-27 19:21:47 +0000481 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000482 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000483
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000484 if int(platform.release().split('.')[0]) < 8:
485 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -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 Oussorenc45c3d92010-04-18 15:24:17 +0000499
Ned Deily4a96a372013-01-29 00:08:32 -0800500 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000501 for framework in ['Tcl', 'Tk']:
Ned Deily4a96a372013-01-29 00:08:32 -0800502 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000503 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800504 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000505 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -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))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000511 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -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 Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -0800534 print("INFO: deleting environment variable %s=%s" % (
535 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000536 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000537
Ned Deily4a96a372013-01-29 00:08:32 -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 Oussoren1e99be72010-04-20 06:36:47 +0000548
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549
Thomas Wouters89f507f2006-12-13 04:49:30 +0000550def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000551 """
552 Parse arguments and update global settings.
553 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000554 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800555 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +0000556
557 if args is None:
558 args = sys.argv[1:]
559
560 try:
561 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000562 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
563 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800564 except getopt.GetoptError:
565 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000566 sys.exit(1)
567
568 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800569 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000570 sys.exit(1)
571
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000572 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000573 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000574 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800575 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +0000590 elif k in ('--dep-target', ):
591 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000592 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000593
594 elif k in ('--universal-archs', ):
595 if v in UNIVERSALOPTS:
596 UNIVERSALARCHS = v
597 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000598 if deptarget is None:
599 # Select alternate default deployment
600 # target
601 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000602 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800603 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000604
Thomas Wouters477c8d52006-05-27 19:21:47 +0000605 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800606 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800613 CC, CXX=target_cc_map[DEPTARGET]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000614
Ned Deily4a96a372013-01-29 00:08:32 -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("")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800669 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000670
671 return os.path.join(builddir, retval)
672
673 finally:
674 os.chdir(curdir)
675
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676def downloadURL(url, fname):
677 """
678 Download the contents of the url into the file.
679 """
Ned Deily4a96a372013-01-29 00:08:32 -0800680 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -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
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800733 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
734 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -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)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000752 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
753 shellQuote(fn),))
754
Ned Deily4a96a372013-01-29 00:08:32 -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
Thomas Wouters0e3f5912006-08-11 14:57:12 +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 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000776
Thomas Wouters0e3f5912006-08-11 14:57:12 +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)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000784
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000785 if recipe.get('useLDFlags', 1):
786 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -0800787 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
788 "-I%s/usr/local/include"%(
789 recipe.get('extra_cflags', ''),
790 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000791 ' -arch '.join(archList),
792 shellQuote(SDKPATH)[1:-1],
793 shellQuote(basedir)[1:-1],),
Ned Deily4a96a372013-01-29 00:08:32 -0800794 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
795 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000796 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000797 shellQuote(basedir)[1:-1],
798 ' -arch '.join(archList)),
799 ])
800 else:
801 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -0800802 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
803 "-I%s/usr/local/include"%(
804 recipe.get('extra_cflags', ''),
805 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000806 ' -arch '.join(archList),
807 shellQuote(SDKPATH)[1:-1],
808 shellQuote(basedir)[1:-1],),
809 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000810
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000811 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -0800812 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000813
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000814 configure_args.insert(0, configure)
815 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000816
Ned Deily4a96a372013-01-29 00:08:32 -0800817 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000818 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000819
Ned Deily4a96a372013-01-29 00:08:32 -0800820 print("Running install for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000821 runCommand('{ ' + install + ' ;} 2>&1')
822
Ned Deily4a96a372013-01-29 00:08:32 -0800823 print("Done %s"%(name,))
824 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000825
826 os.chdir(curdir)
827
828def buildLibraries():
829 """
830 Build our dependencies into $WORKDIR/libraries/usr/local
831 """
Ned Deily4a96a372013-01-29 00:08:32 -0800832 print("")
833 print("Building required libraries")
834 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +0000840 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000841 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000842
843
844
845def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000846 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000847 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -0800848 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000849 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000850 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000851 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000852 curDir = os.getcwd()
853 os.chdir(buildDir)
854 runCommand('make update')
Martin v. Löwis6120ddb2010-04-22 13:16:44 +0000855 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000856 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000857 if not os.path.exists(docdir):
858 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000859 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000860
861
862def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -0800863 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4f7ff782011-01-15 05:29:12 +0000872 os.makedirs(buildDir)
873 os.makedirs(rootDir)
874 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussorenac4b39f2009-03-30 20:05:35 +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 Deily4a96a372013-01-29 00:08:32 -0800891 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +0000892 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000893 "--with-universal-archs=%s "
894 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +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,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000899 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +0000900 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000901 shellQuote(WORKDIR)[1:-1]))
902
Ned Deily4a96a372013-01-29 00:08:32 -0800903 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000904 runCommand("make")
905
Ned Deily4a96a372013-01-29 00:08:32 -0800906 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000907 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000908 shellQuote(rootDir)))
909
Ned Deily4a96a372013-01-29 00:08:32 -0800910 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000911 runCommand("make frameworkinstallextras DESTDIR=%s"%(
912 shellQuote(rootDir)))
913
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000914 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -0800915 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800926 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000927 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000928 gid = grp.getgrnam('admin').gr_gid
929
Ned Deily4a96a372013-01-29 00:08:32 -0800930 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +0000931 for dirpath, dirnames, filenames in os.walk(frmDir):
932 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -0800933 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000934 os.chown(os.path.join(dirpath, dn), -1, gid)
935
Thomas Wouters477c8d52006-05-27 19:21:47 +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)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000943 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
944 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000945
Ned Deily4a96a372013-01-29 00:08:32 -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 Deilye59e4c52011-01-29 18:56:28 +0000959 if PYTHON_3:
960 LDVERSION=None
961 VERSION=None
962 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000963
Ned Deilye59e4c52011-01-29 18:56:28 +0000964 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000965 for ln in fp:
966 if ln.startswith('VERSION='):
967 VERSION=ln.split()[1]
968 if ln.startswith('ABIFLAGS='):
969 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000970 if ln.startswith('LDVERSION='):
971 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +0000972 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000973
Ned Deilye59e4c52011-01-29 18:56:28 +0000974 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
975 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
976 config_suffix = '-' + LDVERSION
977 else:
978 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000979
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800982 # the end-users system. Also remove the directories from _sysconfigdata.py
983 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000984
Ned Deily362532b2013-08-01 15:39:47 -0700985 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
986 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
987
Ned Deily4a96a372013-01-29 00:08:32 -0800988 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
989 'Python.framework', 'Versions',
990 version, 'lib', 'python%s'%(version,))
Ned Deily362532b2013-08-01 15:39:47 -0700991
992 # fix Makefile
993 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
994 fp = open(path, 'r')
995 data = fp.read()
996 fp.close()
997
998 for p in (include_path, lib_path):
999 data = data.replace(" " + p, '')
1000 data = data.replace(p + " ", '')
1001
1002 fp = open(path, 'w')
1003 fp.write(data)
1004 fp.close()
1005
1006 # fix _sysconfigdata if it exists
1007 #
1008 # TODO: make this more robust! test_sysconfig_module of
1009 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1010 # the output from get_config_var in both sysconfig and
1011 # distutils.sysconfig is exactly the same for both CFLAGS and
1012 # LDFLAGS. The fixing up is now complicated by the pretty
1013 # printing in _sysconfigdata.py. Also, we are using the
1014 # pprint from the Python running the installer build which
1015 # may not cosmetically format the same as the pprint in the Python
1016 # being built (and which is used to originally generate
1017 # _sysconfigdata.py).
1018
1019 import pprint
1020 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1021 if os.path.exists(path):
Ned Deily4a96a372013-01-29 00:08:32 -08001022 fp = open(path, 'r')
1023 data = fp.read()
1024 fp.close()
Ned Deily362532b2013-08-01 15:39:47 -07001025 # create build_time_vars dict
1026 exec(data)
1027 vars = {}
1028 for k, v in build_time_vars.items():
1029 if type(v) == type(''):
1030 for p in (include_path, lib_path):
1031 v = v.replace(' ' + p, '')
1032 v = v.replace(p + ' ', '')
1033 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001034
Ned Deily4a96a372013-01-29 00:08:32 -08001035 fp = open(path, 'w')
Ned Deily362532b2013-08-01 15:39:47 -07001036 # duplicated from sysconfig._generate_posix_vars()
1037 fp.write('# system configuration generated and used by'
1038 ' the sysconfig module\n')
1039 fp.write('build_time_vars = ')
1040 pprint.pprint(vars, stream=fp)
Ned Deily4a96a372013-01-29 00:08:32 -08001041 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001042
1043 # Add symlinks in /usr/local/bin, using relative links
1044 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1045 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1046 'Python.framework', 'Versions', version, 'bin')
1047 if os.path.exists(usr_local_bin):
1048 shutil.rmtree(usr_local_bin)
1049 os.makedirs(usr_local_bin)
1050 for fn in os.listdir(
1051 os.path.join(frmDir, 'Versions', version, 'bin')):
1052 os.symlink(os.path.join(to_framework, fn),
1053 os.path.join(usr_local_bin, fn))
1054
1055 os.chdir(curdir)
1056
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001057 if PYTHON_3:
1058 # Remove the 'Current' link, that way we don't accidently mess
1059 # with an already installed version of python 2
1060 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1061 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001062
1063def patchFile(inPath, outPath):
1064 data = fileContents(inPath)
1065 data = data.replace('$FULL_VERSION', getFullVersion())
1066 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001067 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001068 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001069 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001070
1071 # This one is not handy as a template variable
1072 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001073 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001074 fp.write(data)
1075 fp.close()
1076
1077def patchScript(inPath, outPath):
1078 data = fileContents(inPath)
1079 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001080 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001081 fp.write(data)
1082 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001083 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001084
1085
1086
1087def packageFromRecipe(targetDir, recipe):
1088 curdir = os.getcwd()
1089 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001090 # The major version (such as 2.5) is included in the package name
1091 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001092 # common.
1093 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001094 srcdir = recipe.get('source')
1095 pkgroot = recipe.get('topdir', srcdir)
1096 postflight = recipe.get('postflight')
1097 readme = textwrap.dedent(recipe['readme'])
1098 isRequired = recipe.get('required', True)
1099
Ned Deily4a96a372013-01-29 00:08:32 -08001100 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001101
1102 # Substitute some variables
1103 textvars = dict(
1104 VER=getVersion(),
1105 FULLVER=getFullVersion(),
1106 )
1107 readme = readme % textvars
1108
1109 if pkgroot is not None:
1110 pkgroot = pkgroot % textvars
1111 else:
1112 pkgroot = '/'
1113
1114 if srcdir is not None:
1115 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1116 srcdir = srcdir % textvars
1117
1118 if postflight is not None:
1119 postflight = os.path.abspath(postflight)
1120
1121 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1122 os.makedirs(packageContents)
1123
1124 if srcdir is not None:
1125 os.chdir(srcdir)
1126 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1127 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1128 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1129
1130 fn = os.path.join(packageContents, 'PkgInfo')
1131 fp = open(fn, 'w')
1132 fp.write('pmkrpkg1')
1133 fp.close()
1134
1135 rsrcDir = os.path.join(packageContents, "Resources")
1136 os.mkdir(rsrcDir)
1137 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1138 fp.write(readme)
1139 fp.close()
1140
1141 if postflight is not None:
1142 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1143
1144 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001145 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001146 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001147 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1148 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1149 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001150 CFBundleShortVersionString=vers,
1151 IFMajorVersion=major,
1152 IFMinorVersion=minor,
1153 IFPkgFormatVersion=0.10000000149011612,
1154 IFPkgFlagAllowBackRev=False,
1155 IFPkgFlagAuthorizationAction="RootAuthorization",
1156 IFPkgFlagDefaultLocation=pkgroot,
1157 IFPkgFlagFollowLinks=True,
1158 IFPkgFlagInstallFat=True,
1159 IFPkgFlagIsRequired=isRequired,
1160 IFPkgFlagOverwritePermissions=False,
1161 IFPkgFlagRelocatable=False,
1162 IFPkgFlagRestartAction="NoRestart",
1163 IFPkgFlagRootVolumeOnly=True,
1164 IFPkgFlagUpdateInstalledLangauges=False,
1165 )
1166 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1167
1168 pl = Plist(
1169 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001170 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001171 IFPkgDescriptionVersion=vers,
1172 )
1173 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1174
1175 finally:
1176 os.chdir(curdir)
1177
1178
1179def makeMpkgPlist(path):
1180
1181 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001182 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001183
1184 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001185 CFBundleGetInfoString="Python %s"%(vers,),
1186 CFBundleIdentifier='org.python.Python',
1187 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001188 CFBundleShortVersionString=vers,
1189 IFMajorVersion=major,
1190 IFMinorVersion=minor,
1191 IFPkgFlagComponentDirectory="Contents/Packages",
1192 IFPkgFlagPackageList=[
1193 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001194 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001195 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001196 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001197 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001198 ],
1199 IFPkgFormatVersion=0.10000000149011612,
1200 IFPkgFlagBackgroundScaling="proportional",
1201 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001202 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001203 )
1204
1205 writePlist(pl, path)
1206
1207
1208def buildInstaller():
1209
1210 # Zap all compiled files
1211 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1212 for fn in filenames:
1213 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1214 os.unlink(os.path.join(dirpath, fn))
1215
1216 outdir = os.path.join(WORKDIR, 'installer')
1217 if os.path.exists(outdir):
1218 shutil.rmtree(outdir)
1219 os.mkdir(outdir)
1220
Ronald Oussoren1943f862009-03-30 19:39:14 +00001221 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001222 pkgcontents = os.path.join(pkgroot, 'Packages')
1223 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001224 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001225 packageFromRecipe(pkgcontents, recipe)
1226
1227 rsrcDir = os.path.join(pkgroot, 'Resources')
1228
1229 fn = os.path.join(pkgroot, 'PkgInfo')
1230 fp = open(fn, 'w')
1231 fp.write('pmkrpkg1')
1232 fp.close()
1233
1234 os.mkdir(rsrcDir)
1235
1236 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1237 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001238 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001239 IFPkgDescriptionVersion=getVersion(),
1240 )
1241
1242 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1243 for fn in os.listdir('resources'):
1244 if fn == '.svn': continue
1245 if fn.endswith('.jpg'):
1246 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1247 else:
1248 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1249
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001250 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001251
1252
1253def installSize(clear=False, _saved=[]):
1254 if clear:
1255 del _saved[:]
1256 if not _saved:
1257 data = captureCommand("du -ks %s"%(
1258 shellQuote(os.path.join(WORKDIR, '_root'))))
1259 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1260 return _saved[0]
1261
1262
1263def buildDMG():
1264 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001265 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001266 """
1267 outdir = os.path.join(WORKDIR, 'diskimage')
1268 if os.path.exists(outdir):
1269 shutil.rmtree(outdir)
1270
1271 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001272 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001273 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001274 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001275 imagepath = imagepath + '.dmg'
1276
1277 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001278 volname='Python %s'%(getFullVersion())
1279 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1280 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001281 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001282 shellQuote(imagepath + ".tmp.dmg" )))
1283
1284
1285 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1286 os.mkdir(os.path.join(WORKDIR, "mnt"))
1287 runCommand("hdiutil attach %s -mountroot %s"%(
1288 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1289
1290 # Custom icon for the DMG, shown when the DMG is mounted.
1291 shutil.copy("../Icons/Disk Image.icns",
1292 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001293 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001294 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1295
1296 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1297
1298 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1299 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1300 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1301 setIcon(imagepath, "../Icons/Disk Image.icns")
1302
1303 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001304
1305 return imagepath
1306
1307
1308def setIcon(filePath, icnsPath):
1309 """
1310 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001311 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001312
Ronald Oussoren70050672010-04-30 15:00:26 +00001313 dirPath = os.path.normpath(os.path.dirname(__file__))
1314 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001315 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1316 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1317 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001318 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1319 if not os.path.exists(appPath):
1320 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001321 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1322 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001323
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001324 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1325 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001326
1327def main():
1328 # First parse options and check if we can perform our work
1329 parseOptions()
1330 checkEnvironment()
1331
Ronald Oussoren1943f862009-03-30 19:39:14 +00001332 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001333 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001334 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001335
1336 if os.path.exists(WORKDIR):
1337 shutil.rmtree(WORKDIR)
1338 os.mkdir(WORKDIR)
1339
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001340 os.environ['LC_ALL'] = 'C'
1341
Thomas Wouters477c8d52006-05-27 19:21:47 +00001342 # Then build third-party libraries such as sleepycat DB4.
1343 buildLibraries()
1344
1345 # Now build python itself
1346 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001347
1348 # And then build the documentation
1349 # Remove the Deployment Target from the shell
1350 # environment, it's no longer needed and
1351 # an unexpected build target can cause problems
1352 # when Sphinx and its dependencies need to
1353 # be (re-)installed.
1354 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001355 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001356
1357
1358 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001359 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001360 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001361 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001362
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001363 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001364 getVersion(),))
Ned Deily4a96a372013-01-29 00:08:32 -08001365 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001366 setIcon(folder, "../Icons/Python Folder.icns")
1367
1368 # Create the installer
1369 buildInstaller()
1370
1371 # And copy the readme into the directory containing the installer
1372 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1373
1374 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001375 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001376
1377 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001378 fp.write("# BUILD INFO\n")
1379 fp.write("# Date: %s\n" % time.ctime())
1380 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001381 fp.close()
1382
Thomas Wouters477c8d52006-05-27 19:21:47 +00001383 # And copy it to a DMG
1384 buildDMG()
1385
Thomas Wouters477c8d52006-05-27 19:21:47 +00001386if __name__ == "__main__":
1387 main()