blob: bd0c476daf07b8f078d2a608d2b09adab01a1a74 [file] [log] [blame]
Ned Deily7d9cf832010-11-27 16:42:15 -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 Deilydfca8c92012-08-06 06:34:00 -07004It 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 Deilydfca8c92012-08-06 06:34:00 -070013In 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 Deily7d9cf832010-11-27 16:42:15 -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 Deily7d9cf832010-11-27 16:42:15 -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 Deily7d9cf832010-11-27 16:42:15 -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 Deily7d9cf832010-11-27 16:42:15 -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 Deily7d9cf832010-11-27 16:42:15 -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 Deily7d9cf832010-11-27 16:42:15 -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 Deilybbd34372012-08-22 23:34:50 -0700152 '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 Deilybbd34372012-08-22 23:34:50 -0700160CC, 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 Deily2910a7b2012-07-30 02:35:58 -0700178# 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 Deily20416a22012-08-07 03:10:57 -0700193 LT_10_5 = bool(DEPTARGET < '10.5')
194
Ned Deily4d4c0ee2012-04-01 00:17:33 -0700195 result.extend([
196 dict(
197 name="XZ 5.0.3",
198 url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
199 checksum='fefe52f9ecd521de2a8ce38c21a27574',
200 configure_pre=[
201 '--disable-dependency-tracking',
202 ]
Ned Deilya0abb442012-08-06 06:40:48 -0700203 ),
204 dict(
205 name="NCurses 5.9",
206 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
207 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
208 configure_pre=[
209 "--enable-widec",
210 "--without-cxx",
211 "--without-cxx-binding",
212 "--without-ada",
213 "--without-curses-h",
214 "--enable-shared",
215 "--with-shared",
216 "--without-debug",
217 "--without-normal",
Ned Deilya0abb442012-08-06 06:40:48 -0700218 "--without-tests",
219 "--without-manpages",
220 "--datadir=/usr/share",
221 "--sysconfdir=/etc",
222 "--sharedstatedir=/usr/com",
223 "--with-terminfo-dirs=/usr/share/terminfo",
224 "--with-default-terminfo-dir=/usr/share/terminfo",
225 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
226 ],
227 patchscripts=[
228 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
229 "f54bf02a349f96a7c4f0d00922f3a0d4"),
230 ],
231 useLDFlags=False,
232 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
233 shellQuote(os.path.join(WORKDIR, 'libraries')),
234 shellQuote(os.path.join(WORKDIR, 'libraries')),
235 getVersion(),
236 ),
237 ),
Ned Deily20416a22012-08-07 03:10:57 -0700238 dict(
239 name="SQLite 3.7.13",
240 url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
241 checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
242 extra_cflags=('-Os '
243 '-DSQLITE_ENABLE_FTS4 '
244 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
245 '-DSQLITE_ENABLE_RTREE '
246 '-DSQLITE_TCL=0 '
247 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
248 configure_pre=[
249 '--enable-threadsafe',
250 '--enable-shared=no',
251 '--enable-static=yes',
252 '--disable-readline',
253 '--disable-dependency-tracking',
254 ]
255 ),
256 ])
Ned Deily4d4c0ee2012-04-01 00:17:33 -0700257
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000258 if DEPTARGET < '10.5':
259 result.extend([
260 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000261 name="Bzip2 1.0.6",
262 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
263 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000264 configure=None,
Ned Deilybbd34372012-08-22 23:34:50 -0700265 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
266 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000267 shellQuote(os.path.join(WORKDIR, 'libraries')),
268 ' -arch '.join(ARCHLIST),
269 SDKPATH,
270 ),
271 ),
272 dict(
273 name="ZLib 1.2.3",
274 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
275 checksum='debc62758716a169df9f62e6ab2bc634',
276 configure=None,
Ned Deilybbd34372012-08-22 23:34:50 -0700277 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
278 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000279 shellQuote(os.path.join(WORKDIR, 'libraries')),
280 ' -arch '.join(ARCHLIST),
281 SDKPATH,
282 ),
283 ),
284 dict(
285 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000286 name="GNU Readline 6.1.2",
287 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
288 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000289 patchlevel='0',
290 patches=[
291 # The readline maintainers don't do actual micro releases, but
292 # just ship a set of patches.
Ned Deilya0abb442012-08-06 06:40:48 -0700293 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
294 'c642f2e84d820884b0bf9fd176bc6c3f'),
295 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
296 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000297 ]
298 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000299 ])
300
Ned Deily4f7ff782011-01-15 05:29:12 +0000301 if not PYTHON_3:
302 result.extend([
303 dict(
304 name="Sleepycat DB 4.7.25",
305 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
306 checksum='ec2b87e833779681a0c3a814aa71359e',
307 buildDir="build_unix",
308 configure="../dist/configure",
309 configure_pre=[
310 '--includedir=/usr/local/include/db4',
311 ]
312 ),
313 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000314
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000315 return result
316
Thomas Wouters477c8d52006-05-27 19:21:47 +0000317
Thomas Wouters477c8d52006-05-27 19:21:47 +0000318# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000319def pkg_recipes():
320 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
321 result = [
322 dict(
323 name="PythonFramework",
324 long_name="Python Framework",
325 source="/Library/Frameworks/Python.framework",
326 readme="""\
327 This package installs Python.framework, that is the python
328 interpreter and the standard library. This also includes Python
329 wrappers for lots of Mac OS X API's.
330 """,
331 postflight="scripts/postflight.framework",
332 selected='selected',
333 ),
334 dict(
335 name="PythonApplications",
336 long_name="GUI Applications",
337 source="/Applications/Python %(VER)s",
338 readme="""\
339 This package installs IDLE (an interactive Python IDE),
340 Python Launcher and Build Applet (create application bundles
341 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000342
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000343 It also installs a number of examples and demos.
344 """,
345 required=False,
346 selected='selected',
347 ),
348 dict(
349 name="PythonUnixTools",
350 long_name="UNIX command-line tools",
351 source="/usr/local/bin",
352 readme="""\
353 This package installs the unix tools in /usr/local/bin for
354 compatibility with older releases of Python. This package
355 is not necessary to use Python.
356 """,
357 required=False,
358 selected='selected',
359 ),
360 dict(
361 name="PythonDocumentation",
362 long_name="Python Documentation",
363 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
364 source="/pydocs",
365 readme="""\
366 This package installs the python documentation at a location
Ned Deilydfca8c92012-08-06 06:34:00 -0700367 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000368 """,
369 postflight="scripts/postflight.documentation",
370 required=False,
371 selected='selected',
372 ),
373 dict(
374 name="PythonProfileChanges",
375 long_name="Shell profile updater",
376 readme="""\
377 This packages updates your shell profile to make sure that
378 the Python tools are found by your shell in preference of
379 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000380
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000381 If you don't install this package you'll have to add
382 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
383 to your PATH by hand.
384 """,
385 postflight="scripts/postflight.patch-profile",
386 topdir="/Library/Frameworks/Python.framework",
387 source="/empty-dir",
388 required=False,
389 selected=unselected_for_python3,
390 ),
391 ]
392
Ned Deily430d7a32012-06-24 00:19:31 -0700393 if DEPTARGET < '10.4' and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000394 result.append(
395 dict(
396 name="PythonSystemFixes",
397 long_name="Fix system Python",
398 readme="""\
399 This package updates the system python installation on
400 Mac OS X 10.3 to ensure that you can build new python extensions
401 using that copy of python after installing this version.
402 """,
403 postflight="../Tools/fixapplepython23.py",
404 topdir="/Library/Frameworks/Python.framework",
405 source="/empty-dir",
406 required=False,
407 selected=unselected_for_python3,
408 )
409 )
410 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000411
Thomas Wouters477c8d52006-05-27 19:21:47 +0000412def fatal(msg):
413 """
414 A fatal error, bail out.
415 """
416 sys.stderr.write('FATAL: ')
417 sys.stderr.write(msg)
418 sys.stderr.write('\n')
419 sys.exit(1)
420
421def fileContents(fn):
422 """
423 Return the contents of the named file
424 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800425 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426
427def runCommand(commandline):
428 """
Ezio Melotti13925002011-03-16 11:05:33 +0200429 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000430 unless the command fails.
431 """
432 fd = os.popen(commandline, 'r')
433 data = fd.read()
434 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000435 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000436 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800437 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000438
439 if VERBOSE:
440 sys.stdout.write(data); sys.stdout.flush()
441
442def captureCommand(commandline):
443 fd = os.popen(commandline, 'r')
444 data = fd.read()
445 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000446 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000447 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800448 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000449
450 return data
451
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000452def getTclTkVersion(configfile, versionline):
453 """
454 search Tcl or Tk configuration file for version line
455 """
456 try:
457 f = open(configfile, "r")
458 except:
459 fatal("Framework configuration file not found: %s" % configfile)
460
461 for l in f:
462 if l.startswith(versionline):
463 f.close()
464 return l
465
466 fatal("Version variable %s not found in framework configuration file: %s"
467 % (versionline, configfile))
468
Thomas Wouters477c8d52006-05-27 19:21:47 +0000469def checkEnvironment():
470 """
471 Check that we're running on a supported system.
472 """
473
Ned Deilye59e4c52011-01-29 18:56:28 +0000474 if sys.version_info[0:2] < (2, 4):
475 fatal("This script must be run with Python 2.4 or later")
476
Thomas Wouters477c8d52006-05-27 19:21:47 +0000477 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000478 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000479
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000480 if int(platform.release().split('.')[0]) < 8:
481 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000482
483 if not os.path.exists(SDKPATH):
484 fatal("Please install the latest version of Xcode and the %s SDK"%(
485 os.path.basename(SDKPATH[:-4])))
486
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000487 # Because we only support dynamic load of only one major/minor version of
488 # Tcl/Tk, ensure:
489 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily2910a7b2012-07-30 02:35:58 -0700490 # higher than the Apple-supplied system version in
491 # SDKROOT/System/Library/Frameworks
492 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
493 # in) SDKROOT/Library/Frameworks with the same version as the system
494 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000495
Ned Deily2910a7b2012-07-30 02:35:58 -0700496 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000497 for framework in ['Tcl', 'Tk']:
Ned Deily2910a7b2012-07-30 02:35:58 -0700498 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000499 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700500 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000501 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700502 frameworks[framework] = os.readlink(sysfw)
503 if not os.path.exists(libfw):
504 fatal("Please install a link to a current %s %s as %s so "
505 "the user can override the system framework."
506 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000507 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000508 fatal("Version of %s must match %s" % (libfw, sysfw) )
509 if os.path.exists(usrfw):
510 fatal("Please rename %s to avoid possible dynamic load issues."
511 % usrfw)
512
Ned Deily2910a7b2012-07-30 02:35:58 -0700513 if frameworks['Tcl'] != frameworks['Tk']:
514 fatal("The Tcl and Tk frameworks are not the same version.")
515
516 # add files to check after build
517 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
518 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
519 % frameworks['Tcl'],
520 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
521 % frameworks['Tk'],
522 ]
523
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000524 # Remove inherited environment variables which might influence build
525 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
526 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
527 for ev in list(os.environ):
528 for prefix in environ_var_prefixes:
529 if ev.startswith(prefix) :
Ned Deily7d9cf832010-11-27 16:42:15 -0800530 print("INFO: deleting environment variable %s=%s" % (
531 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000532 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533
Ned Deilydfca8c92012-08-06 06:34:00 -0700534 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
535 if 'SDK_TOOLS_BIN' in os.environ:
536 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
537 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
538 # add its fixed location here if it exists
539 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
540 if os.path.isdir(OLD_DEVELOPER_TOOLS):
541 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
542 os.environ['PATH'] = base_path
Ned Deily7d9cf832010-11-27 16:42:15 -0800543 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussoren1e99be72010-04-20 06:36:47 +0000544
Thomas Wouters477c8d52006-05-27 19:21:47 +0000545
Thomas Wouters89f507f2006-12-13 04:49:30 +0000546def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000547 """
548 Parse arguments and update global settings.
549 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000550 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilybbd34372012-08-22 23:34:50 -0700551 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552
553 if args is None:
554 args = sys.argv[1:]
555
556 try:
557 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000558 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
559 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily7d9cf832010-11-27 16:42:15 -0800560 except getopt.GetoptError:
561 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000562 sys.exit(1)
563
564 if args:
Ned Deily7d9cf832010-11-27 16:42:15 -0800565 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000566 sys.exit(1)
567
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000568 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000569 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000570 if k in ('-h', '-?', '--help'):
Ned Deily7d9cf832010-11-27 16:42:15 -0800571 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000572 sys.exit(0)
573
574 elif k in ('-d', '--build-dir'):
575 WORKDIR=v
576
577 elif k in ('--third-party',):
578 DEPSRC=v
579
580 elif k in ('--sdk-path',):
581 SDKPATH=v
582
583 elif k in ('--src-dir',):
584 SRCDIR=v
585
Ronald Oussoren1943f862009-03-30 19:39:14 +0000586 elif k in ('--dep-target', ):
587 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000588 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000589
590 elif k in ('--universal-archs', ):
591 if v in UNIVERSALOPTS:
592 UNIVERSALARCHS = v
593 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000594 if deptarget is None:
595 # Select alternate default deployment
596 # target
597 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000598 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800599 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000600
Thomas Wouters477c8d52006-05-27 19:21:47 +0000601 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800602 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000603
604 SRCDIR=os.path.abspath(SRCDIR)
605 WORKDIR=os.path.abspath(WORKDIR)
606 SDKPATH=os.path.abspath(SDKPATH)
607 DEPSRC=os.path.abspath(DEPSRC)
608
Ned Deilybbd34372012-08-22 23:34:50 -0700609 CC, CXX=target_cc_map[DEPTARGET]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000610
Ned Deily7d9cf832010-11-27 16:42:15 -0800611 print("Settings:")
612 print(" * Source directory:", SRCDIR)
613 print(" * Build directory: ", WORKDIR)
614 print(" * SDK location: ", SDKPATH)
615 print(" * Third-party source:", DEPSRC)
616 print(" * Deployment target:", DEPTARGET)
617 print(" * Universal architectures:", ARCHLIST)
618 print(" * C compiler:", CC)
Ned Deilybbd34372012-08-22 23:34:50 -0700619 print(" * C++ compiler:", CXX)
Ned Deily7d9cf832010-11-27 16:42:15 -0800620 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000621
622
623
624
625def extractArchive(builddir, archiveName):
626 """
627 Extract a source archive into 'builddir'. Returns the path of the
628 extracted archive.
629
630 XXX: This function assumes that archives contain a toplevel directory
631 that is has the same name as the basename of the archive. This is
632 save enough for anything we use.
633 """
634 curdir = os.getcwd()
635 try:
636 os.chdir(builddir)
637 if archiveName.endswith('.tar.gz'):
638 retval = os.path.basename(archiveName[:-7])
639 if os.path.exists(retval):
640 shutil.rmtree(retval)
641 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
642
643 elif archiveName.endswith('.tar.bz2'):
644 retval = os.path.basename(archiveName[:-8])
645 if os.path.exists(retval):
646 shutil.rmtree(retval)
647 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
648
649 elif archiveName.endswith('.tar'):
650 retval = os.path.basename(archiveName[:-4])
651 if os.path.exists(retval):
652 shutil.rmtree(retval)
653 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
654
655 elif archiveName.endswith('.zip'):
656 retval = os.path.basename(archiveName[:-4])
657 if os.path.exists(retval):
658 shutil.rmtree(retval)
659 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
660
661 data = fp.read()
662 xit = fp.close()
663 if xit is not None:
664 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800665 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000666
667 return os.path.join(builddir, retval)
668
669 finally:
670 os.chdir(curdir)
671
Thomas Wouters477c8d52006-05-27 19:21:47 +0000672def downloadURL(url, fname):
673 """
674 Download the contents of the url into the file.
675 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800676 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000677 fpOut = open(fname, 'wb')
678 block = fpIn.read(10240)
679 try:
680 while block:
681 fpOut.write(block)
682 block = fpIn.read(10240)
683 fpIn.close()
684 fpOut.close()
685 except:
686 try:
687 os.unlink(fname)
688 except:
689 pass
690
Ned Deilya0abb442012-08-06 06:40:48 -0700691def verifyThirdPartyFile(url, checksum, fname):
692 """
693 Download file from url to filename fname if it does not already exist.
694 Abort if file contents does not match supplied md5 checksum.
695 """
696 name = os.path.basename(fname)
697 if os.path.exists(fname):
698 print("Using local copy of %s"%(name,))
699 else:
700 print("Did not find local copy of %s"%(name,))
701 print("Downloading %s"%(name,))
702 downloadURL(url, fname)
703 print("Archive for %s stored as %s"%(name, fname))
704 if os.system(
705 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
706 % (shellQuote(fname), checksum) ):
707 fatal('MD5 checksum mismatch for file %s' % fname)
708
Thomas Wouters477c8d52006-05-27 19:21:47 +0000709def buildRecipe(recipe, basedir, archList):
710 """
711 Build software using a recipe. This function does the
712 'configure;make;make install' dance for C software, with a possibility
713 to customize this process, basically a poor-mans DarwinPorts.
714 """
715 curdir = os.getcwd()
716
717 name = recipe['name']
718 url = recipe['url']
719 configure = recipe.get('configure', './configure')
720 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
721 shellQuote(basedir)))
722
723 archiveName = os.path.split(url)[-1]
724 sourceArchive = os.path.join(DEPSRC, archiveName)
725
726 if not os.path.exists(DEPSRC):
727 os.mkdir(DEPSRC)
728
Ned Deilya0abb442012-08-06 06:40:48 -0700729 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
Ned Deily7d9cf832010-11-27 16:42:15 -0800730 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000731 buildDir=os.path.join(WORKDIR, '_bld')
732 if not os.path.exists(buildDir):
733 os.mkdir(buildDir)
734
735 workDir = extractArchive(buildDir, sourceArchive)
736 os.chdir(workDir)
737 if 'buildDir' in recipe:
738 os.chdir(recipe['buildDir'])
739
Ned Deilya0abb442012-08-06 06:40:48 -0700740 for patch in recipe.get('patches', ()):
741 if isinstance(patch, tuple):
742 url, checksum = patch
743 fn = os.path.join(DEPSRC, os.path.basename(url))
744 verifyThirdPartyFile(url, checksum, fn)
745 else:
746 # patch is a file in the source directory
747 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000748 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
749 shellQuote(fn),))
750
Ned Deilya0abb442012-08-06 06:40:48 -0700751 for patchscript in recipe.get('patchscripts', ()):
752 if isinstance(patchscript, tuple):
753 url, checksum = patchscript
754 fn = os.path.join(DEPSRC, os.path.basename(url))
755 verifyThirdPartyFile(url, checksum, fn)
756 else:
757 # patch is a file in the source directory
758 fn = os.path.join(curdir, patchscript)
759 if fn.endswith('.bz2'):
760 runCommand('bunzip2 -fk %s' % shellQuote(fn))
761 fn = fn[:-4]
762 runCommand('sh %s' % shellQuote(fn))
763 os.unlink(fn)
764
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000765 if configure is not None:
766 configure_args = [
767 "--prefix=/usr/local",
768 "--enable-static",
769 "--disable-shared",
770 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
771 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000772
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000773 if 'configure_pre' in recipe:
774 args = list(recipe['configure_pre'])
775 if '--disable-static' in args:
776 configure_args.remove('--enable-static')
777 if '--enable-shared' in args:
778 configure_args.remove('--disable-shared')
779 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000780
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000781 if recipe.get('useLDFlags', 1):
782 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700783 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
784 "-I%s/usr/local/include"%(
785 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700786 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000787 ' -arch '.join(archList),
788 shellQuote(SDKPATH)[1:-1],
789 shellQuote(basedir)[1:-1],),
Ned Deilya0abb442012-08-06 06:40:48 -0700790 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
791 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000792 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000793 shellQuote(basedir)[1:-1],
794 ' -arch '.join(archList)),
795 ])
796 else:
797 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700798 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
799 "-I%s/usr/local/include"%(
800 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700801 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000802 ' -arch '.join(archList),
803 shellQuote(SDKPATH)[1:-1],
804 shellQuote(basedir)[1:-1],),
805 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000806
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000807 if 'configure_post' in recipe:
Ned Deilya0abb442012-08-06 06:40:48 -0700808 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000809
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000810 configure_args.insert(0, configure)
811 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000812
Ned Deily7d9cf832010-11-27 16:42:15 -0800813 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000814 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000815
Ned Deily7d9cf832010-11-27 16:42:15 -0800816 print("Running install for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000817 runCommand('{ ' + install + ' ;} 2>&1')
818
Ned Deily7d9cf832010-11-27 16:42:15 -0800819 print("Done %s"%(name,))
820 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000821
822 os.chdir(curdir)
823
824def buildLibraries():
825 """
826 Build our dependencies into $WORKDIR/libraries/usr/local
827 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800828 print("")
829 print("Building required libraries")
830 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000831 universal = os.path.join(WORKDIR, 'libraries')
832 os.mkdir(universal)
833 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
834 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
835
Ronald Oussoren1943f862009-03-30 19:39:14 +0000836 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000837 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000838
839
840
841def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000842 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000843 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily7d9cf832010-11-27 16:42:15 -0800844 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000845 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000846 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000847 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000848 curDir = os.getcwd()
849 os.chdir(buildDir)
850 runCommand('make update')
Martin v. Löwis6120ddb2010-04-22 13:16:44 +0000851 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000852 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000853 if not os.path.exists(docdir):
854 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000855 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000856
857
858def buildPython():
Ned Deily7d9cf832010-11-27 16:42:15 -0800859 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000860
861 buildDir = os.path.join(WORKDIR, '_bld', 'python')
862 rootDir = os.path.join(WORKDIR, '_root')
863
864 if os.path.exists(buildDir):
865 shutil.rmtree(buildDir)
866 if os.path.exists(rootDir):
867 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +0000868 os.makedirs(buildDir)
869 os.makedirs(rootDir)
870 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000871 curdir = os.getcwd()
872 os.chdir(buildDir)
873
874 # Not sure if this is still needed, the original build script
875 # claims that parts of the install assume python.exe exists.
876 os.symlink('python', os.path.join(buildDir, 'python.exe'))
877
878 # Extract the version from the configure file, needed to calculate
879 # several paths.
880 version = getVersion()
881
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000882 # Since the extra libs are not in their installed framework location
883 # during the build, augment the library path so that the interpreter
884 # will find them during its extension import sanity checks.
885 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
886 'libraries', 'usr', 'local', 'lib')
Ned Deily7d9cf832010-11-27 16:42:15 -0800887 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +0000888 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000889 "--with-universal-archs=%s "
890 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +0000891 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
892 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
893 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
894 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000895 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +0000896 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000897 shellQuote(WORKDIR)[1:-1]))
898
Ned Deily7d9cf832010-11-27 16:42:15 -0800899 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000900 runCommand("make")
901
Ned Deily7d9cf832010-11-27 16:42:15 -0800902 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000903 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000904 shellQuote(rootDir)))
905
Ned Deily7d9cf832010-11-27 16:42:15 -0800906 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000907 runCommand("make frameworkinstallextras DESTDIR=%s"%(
908 shellQuote(rootDir)))
909
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000910 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily7d9cf832010-11-27 16:42:15 -0800911 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000912 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
913 runCommand("mv %s/* %s"%(
914 shellQuote(os.path.join(
915 WORKDIR, 'libraries', 'Library', 'Frameworks',
916 'Python.framework', 'Versions', getVersion(),
917 'lib')),
918 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
919 'Python.framework', 'Versions', getVersion(),
920 'lib'))))
921
Ned Deily7d9cf832010-11-27 16:42:15 -0800922 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000923 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000924 gid = grp.getgrnam('admin').gr_gid
925
Ned Deily2910a7b2012-07-30 02:35:58 -0700926 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +0000927 for dirpath, dirnames, filenames in os.walk(frmDir):
928 for dn in dirnames:
Ned Deily7d9cf832010-11-27 16:42:15 -0800929 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000930 os.chown(os.path.join(dirpath, dn), -1, gid)
931
Thomas Wouters477c8d52006-05-27 19:21:47 +0000932 for fn in filenames:
933 if os.path.islink(fn):
934 continue
935
936 # "chmod g+w $fn"
937 p = os.path.join(dirpath, fn)
938 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000939 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
940 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000941
Ned Deily2910a7b2012-07-30 02:35:58 -0700942 if fn in EXPECTED_SHARED_LIBS:
943 # check to see that this file was linked with the
944 # expected library path and version
945 data = captureCommand("otool -L %s" % shellQuote(p))
946 for sl in EXPECTED_SHARED_LIBS[fn]:
947 if ("\t%s " % sl) not in data:
948 print("Expected shared lib %s was not linked with %s"
949 % (sl, p))
950 shared_lib_error = True
951
952 if shared_lib_error:
953 fatal("Unexpected shared library errors.")
954
Ned Deilye59e4c52011-01-29 18:56:28 +0000955 if PYTHON_3:
956 LDVERSION=None
957 VERSION=None
958 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000959
Ned Deilye59e4c52011-01-29 18:56:28 +0000960 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000961 for ln in fp:
962 if ln.startswith('VERSION='):
963 VERSION=ln.split()[1]
964 if ln.startswith('ABIFLAGS='):
965 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000966 if ln.startswith('LDVERSION='):
967 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +0000968 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000969
Ned Deilye59e4c52011-01-29 18:56:28 +0000970 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
971 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
972 config_suffix = '-' + LDVERSION
973 else:
974 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000975
Thomas Wouters477c8d52006-05-27 19:21:47 +0000976 # We added some directories to the search path during the configure
977 # phase. Remove those because those directories won't be there on
Ned Deilya606aef2012-07-21 10:48:09 -0700978 # the end-users system. Also remove the directories from _sysconfigdata.py
979 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000980
Ned Deilya606aef2012-07-21 10:48:09 -0700981 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
982 'Python.framework', 'Versions',
983 version, 'lib', 'python%s'%(version,))
984 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
985 os.path.join(path_to_lib, '_sysconfigdata.py')]
986 for path in paths:
987 if not os.path.exists(path):
988 continue
989 fp = open(path, 'r')
990 data = fp.read()
991 fp.close()
992
Ned Deily2c80e122012-07-22 00:46:46 -0700993 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
994 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
Ned Deilya606aef2012-07-21 10:48:09 -0700995 fp = open(path, 'w')
996 fp.write(data)
997 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000998
999 # Add symlinks in /usr/local/bin, using relative links
1000 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1001 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1002 'Python.framework', 'Versions', version, 'bin')
1003 if os.path.exists(usr_local_bin):
1004 shutil.rmtree(usr_local_bin)
1005 os.makedirs(usr_local_bin)
1006 for fn in os.listdir(
1007 os.path.join(frmDir, 'Versions', version, 'bin')):
1008 os.symlink(os.path.join(to_framework, fn),
1009 os.path.join(usr_local_bin, fn))
1010
1011 os.chdir(curdir)
1012
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001013 if PYTHON_3:
1014 # Remove the 'Current' link, that way we don't accidently mess
1015 # with an already installed version of python 2
1016 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1017 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001018
1019def patchFile(inPath, outPath):
1020 data = fileContents(inPath)
1021 data = data.replace('$FULL_VERSION', getFullVersion())
1022 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001023 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001024 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001025 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001026
1027 # This one is not handy as a template variable
1028 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily7d9cf832010-11-27 16:42:15 -08001029 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001030 fp.write(data)
1031 fp.close()
1032
1033def patchScript(inPath, outPath):
1034 data = fileContents(inPath)
1035 data = data.replace('@PYVER@', getVersion())
Ned Deily7d9cf832010-11-27 16:42:15 -08001036 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001037 fp.write(data)
1038 fp.close()
Ned Deily7d9cf832010-11-27 16:42:15 -08001039 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001040
1041
1042
1043def packageFromRecipe(targetDir, recipe):
1044 curdir = os.getcwd()
1045 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001046 # The major version (such as 2.5) is included in the package name
1047 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001048 # common.
1049 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001050 srcdir = recipe.get('source')
1051 pkgroot = recipe.get('topdir', srcdir)
1052 postflight = recipe.get('postflight')
1053 readme = textwrap.dedent(recipe['readme'])
1054 isRequired = recipe.get('required', True)
1055
Ned Deily7d9cf832010-11-27 16:42:15 -08001056 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001057
1058 # Substitute some variables
1059 textvars = dict(
1060 VER=getVersion(),
1061 FULLVER=getFullVersion(),
1062 )
1063 readme = readme % textvars
1064
1065 if pkgroot is not None:
1066 pkgroot = pkgroot % textvars
1067 else:
1068 pkgroot = '/'
1069
1070 if srcdir is not None:
1071 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1072 srcdir = srcdir % textvars
1073
1074 if postflight is not None:
1075 postflight = os.path.abspath(postflight)
1076
1077 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1078 os.makedirs(packageContents)
1079
1080 if srcdir is not None:
1081 os.chdir(srcdir)
1082 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1083 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1084 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1085
1086 fn = os.path.join(packageContents, 'PkgInfo')
1087 fp = open(fn, 'w')
1088 fp.write('pmkrpkg1')
1089 fp.close()
1090
1091 rsrcDir = os.path.join(packageContents, "Resources")
1092 os.mkdir(rsrcDir)
1093 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1094 fp.write(readme)
1095 fp.close()
1096
1097 if postflight is not None:
1098 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1099
1100 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001101 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001102 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001103 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1104 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1105 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001106 CFBundleShortVersionString=vers,
1107 IFMajorVersion=major,
1108 IFMinorVersion=minor,
1109 IFPkgFormatVersion=0.10000000149011612,
1110 IFPkgFlagAllowBackRev=False,
1111 IFPkgFlagAuthorizationAction="RootAuthorization",
1112 IFPkgFlagDefaultLocation=pkgroot,
1113 IFPkgFlagFollowLinks=True,
1114 IFPkgFlagInstallFat=True,
1115 IFPkgFlagIsRequired=isRequired,
1116 IFPkgFlagOverwritePermissions=False,
1117 IFPkgFlagRelocatable=False,
1118 IFPkgFlagRestartAction="NoRestart",
1119 IFPkgFlagRootVolumeOnly=True,
1120 IFPkgFlagUpdateInstalledLangauges=False,
1121 )
1122 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1123
1124 pl = Plist(
1125 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001126 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001127 IFPkgDescriptionVersion=vers,
1128 )
1129 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1130
1131 finally:
1132 os.chdir(curdir)
1133
1134
1135def makeMpkgPlist(path):
1136
1137 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001138 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001139
1140 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001141 CFBundleGetInfoString="Python %s"%(vers,),
1142 CFBundleIdentifier='org.python.Python',
1143 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001144 CFBundleShortVersionString=vers,
1145 IFMajorVersion=major,
1146 IFMinorVersion=minor,
1147 IFPkgFlagComponentDirectory="Contents/Packages",
1148 IFPkgFlagPackageList=[
1149 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001150 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001151 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001153 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001154 ],
1155 IFPkgFormatVersion=0.10000000149011612,
1156 IFPkgFlagBackgroundScaling="proportional",
1157 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001158 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001159 )
1160
1161 writePlist(pl, path)
1162
1163
1164def buildInstaller():
1165
1166 # Zap all compiled files
1167 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1168 for fn in filenames:
1169 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1170 os.unlink(os.path.join(dirpath, fn))
1171
1172 outdir = os.path.join(WORKDIR, 'installer')
1173 if os.path.exists(outdir):
1174 shutil.rmtree(outdir)
1175 os.mkdir(outdir)
1176
Ronald Oussoren1943f862009-03-30 19:39:14 +00001177 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001178 pkgcontents = os.path.join(pkgroot, 'Packages')
1179 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001180 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001181 packageFromRecipe(pkgcontents, recipe)
1182
1183 rsrcDir = os.path.join(pkgroot, 'Resources')
1184
1185 fn = os.path.join(pkgroot, 'PkgInfo')
1186 fp = open(fn, 'w')
1187 fp.write('pmkrpkg1')
1188 fp.close()
1189
1190 os.mkdir(rsrcDir)
1191
1192 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1193 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001194 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001195 IFPkgDescriptionVersion=getVersion(),
1196 )
1197
1198 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1199 for fn in os.listdir('resources'):
1200 if fn == '.svn': continue
1201 if fn.endswith('.jpg'):
1202 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1203 else:
1204 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1205
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001206 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001207
1208
1209def installSize(clear=False, _saved=[]):
1210 if clear:
1211 del _saved[:]
1212 if not _saved:
1213 data = captureCommand("du -ks %s"%(
1214 shellQuote(os.path.join(WORKDIR, '_root'))))
1215 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1216 return _saved[0]
1217
1218
1219def buildDMG():
1220 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001221 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001222 """
1223 outdir = os.path.join(WORKDIR, 'diskimage')
1224 if os.path.exists(outdir):
1225 shutil.rmtree(outdir)
1226
1227 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001228 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001229 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001230 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001231 imagepath = imagepath + '.dmg'
1232
1233 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001234 volname='Python %s'%(getFullVersion())
1235 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1236 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001237 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001238 shellQuote(imagepath + ".tmp.dmg" )))
1239
1240
1241 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1242 os.mkdir(os.path.join(WORKDIR, "mnt"))
1243 runCommand("hdiutil attach %s -mountroot %s"%(
1244 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1245
1246 # Custom icon for the DMG, shown when the DMG is mounted.
1247 shutil.copy("../Icons/Disk Image.icns",
1248 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilydfca8c92012-08-06 06:34:00 -07001249 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001250 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1251
1252 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1253
1254 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1255 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1256 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1257 setIcon(imagepath, "../Icons/Disk Image.icns")
1258
1259 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001260
1261 return imagepath
1262
1263
1264def setIcon(filePath, icnsPath):
1265 """
1266 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001267 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001268
Ronald Oussoren70050672010-04-30 15:00:26 +00001269 dirPath = os.path.normpath(os.path.dirname(__file__))
1270 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001271 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1272 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1273 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001274 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1275 if not os.path.exists(appPath):
1276 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001277 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1278 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001279
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001280 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1281 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001282
1283def main():
1284 # First parse options and check if we can perform our work
1285 parseOptions()
1286 checkEnvironment()
1287
Ronald Oussoren1943f862009-03-30 19:39:14 +00001288 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001289 os.environ['CC'] = CC
Ned Deilybbd34372012-08-22 23:34:50 -07001290 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001291
1292 if os.path.exists(WORKDIR):
1293 shutil.rmtree(WORKDIR)
1294 os.mkdir(WORKDIR)
1295
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001296 os.environ['LC_ALL'] = 'C'
1297
Thomas Wouters477c8d52006-05-27 19:21:47 +00001298 # Then build third-party libraries such as sleepycat DB4.
1299 buildLibraries()
1300
1301 # Now build python itself
1302 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001303
1304 # And then build the documentation
1305 # Remove the Deployment Target from the shell
1306 # environment, it's no longer needed and
1307 # an unexpected build target can cause problems
1308 # when Sphinx and its dependencies need to
1309 # be (re-)installed.
1310 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001311 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001312
1313
1314 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001315 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001316 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001317 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001318
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001319 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001320 getVersion(),))
Ned Deily7d9cf832010-11-27 16:42:15 -08001321 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001322 setIcon(folder, "../Icons/Python Folder.icns")
1323
1324 # Create the installer
1325 buildInstaller()
1326
1327 # And copy the readme into the directory containing the installer
1328 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1329
1330 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001331 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001332
1333 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily7d9cf832010-11-27 16:42:15 -08001334 fp.write("# BUILD INFO\n")
1335 fp.write("# Date: %s\n" % time.ctime())
1336 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001337 fp.close()
1338
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339 # And copy it to a DMG
1340 buildDMG()
1341
Thomas Wouters477c8d52006-05-27 19:21:47 +00001342if __name__ == "__main__":
1343 main()