blob: 11c881cd9de26cb2f908f9b3db10c2c21a148cf1 [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",
218 "--without-termlib",
219 "--without-ticlib",
220 "--without-tests",
221 "--without-manpages",
222 "--datadir=/usr/share",
223 "--sysconfdir=/etc",
224 "--sharedstatedir=/usr/com",
225 "--with-terminfo-dirs=/usr/share/terminfo",
226 "--with-default-terminfo-dir=/usr/share/terminfo",
227 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
228 ],
229 patchscripts=[
230 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
231 "f54bf02a349f96a7c4f0d00922f3a0d4"),
232 ],
233 useLDFlags=False,
234 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
235 shellQuote(os.path.join(WORKDIR, 'libraries')),
236 shellQuote(os.path.join(WORKDIR, 'libraries')),
237 getVersion(),
238 ),
239 ),
Ned Deily20416a22012-08-07 03:10:57 -0700240 dict(
241 name="SQLite 3.7.13",
242 url="http://www.sqlite.org/sqlite-autoconf-3071300.tar.gz",
243 checksum='c97df403e8a3d5b67bb408fcd6aabd8e',
244 extra_cflags=('-Os '
245 '-DSQLITE_ENABLE_FTS4 '
246 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
247 '-DSQLITE_ENABLE_RTREE '
248 '-DSQLITE_TCL=0 '
249 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
250 configure_pre=[
251 '--enable-threadsafe',
252 '--enable-shared=no',
253 '--enable-static=yes',
254 '--disable-readline',
255 '--disable-dependency-tracking',
256 ]
257 ),
258 ])
Ned Deily4d4c0ee2012-04-01 00:17:33 -0700259
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000260 if DEPTARGET < '10.5':
261 result.extend([
262 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000263 name="Bzip2 1.0.6",
264 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
265 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000266 configure=None,
Ned Deilybbd34372012-08-22 23:34:50 -0700267 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
268 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000269 shellQuote(os.path.join(WORKDIR, 'libraries')),
270 ' -arch '.join(ARCHLIST),
271 SDKPATH,
272 ),
273 ),
274 dict(
275 name="ZLib 1.2.3",
276 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
277 checksum='debc62758716a169df9f62e6ab2bc634',
278 configure=None,
Ned Deilybbd34372012-08-22 23:34:50 -0700279 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
280 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000281 shellQuote(os.path.join(WORKDIR, 'libraries')),
282 ' -arch '.join(ARCHLIST),
283 SDKPATH,
284 ),
285 ),
286 dict(
287 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000288 name="GNU Readline 6.1.2",
289 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
290 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000291 patchlevel='0',
292 patches=[
293 # The readline maintainers don't do actual micro releases, but
294 # just ship a set of patches.
Ned Deilya0abb442012-08-06 06:40:48 -0700295 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
296 'c642f2e84d820884b0bf9fd176bc6c3f'),
297 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
298 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000299 ]
300 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000301 ])
302
Ned Deily4f7ff782011-01-15 05:29:12 +0000303 if not PYTHON_3:
304 result.extend([
305 dict(
306 name="Sleepycat DB 4.7.25",
307 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
308 checksum='ec2b87e833779681a0c3a814aa71359e',
309 buildDir="build_unix",
310 configure="../dist/configure",
311 configure_pre=[
312 '--includedir=/usr/local/include/db4',
313 ]
314 ),
315 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000316
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000317 return result
318
Thomas Wouters477c8d52006-05-27 19:21:47 +0000319
Thomas Wouters477c8d52006-05-27 19:21:47 +0000320# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000321def pkg_recipes():
322 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
323 result = [
324 dict(
325 name="PythonFramework",
326 long_name="Python Framework",
327 source="/Library/Frameworks/Python.framework",
328 readme="""\
329 This package installs Python.framework, that is the python
330 interpreter and the standard library. This also includes Python
331 wrappers for lots of Mac OS X API's.
332 """,
333 postflight="scripts/postflight.framework",
334 selected='selected',
335 ),
336 dict(
337 name="PythonApplications",
338 long_name="GUI Applications",
339 source="/Applications/Python %(VER)s",
340 readme="""\
341 This package installs IDLE (an interactive Python IDE),
342 Python Launcher and Build Applet (create application bundles
343 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000344
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000345 It also installs a number of examples and demos.
346 """,
347 required=False,
348 selected='selected',
349 ),
350 dict(
351 name="PythonUnixTools",
352 long_name="UNIX command-line tools",
353 source="/usr/local/bin",
354 readme="""\
355 This package installs the unix tools in /usr/local/bin for
356 compatibility with older releases of Python. This package
357 is not necessary to use Python.
358 """,
359 required=False,
360 selected='selected',
361 ),
362 dict(
363 name="PythonDocumentation",
364 long_name="Python Documentation",
365 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
366 source="/pydocs",
367 readme="""\
368 This package installs the python documentation at a location
Ned Deilydfca8c92012-08-06 06:34:00 -0700369 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000370 """,
371 postflight="scripts/postflight.documentation",
372 required=False,
373 selected='selected',
374 ),
375 dict(
376 name="PythonProfileChanges",
377 long_name="Shell profile updater",
378 readme="""\
379 This packages updates your shell profile to make sure that
380 the Python tools are found by your shell in preference of
381 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000382
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000383 If you don't install this package you'll have to add
384 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
385 to your PATH by hand.
386 """,
387 postflight="scripts/postflight.patch-profile",
388 topdir="/Library/Frameworks/Python.framework",
389 source="/empty-dir",
390 required=False,
391 selected=unselected_for_python3,
392 ),
393 ]
394
Ned Deily430d7a32012-06-24 00:19:31 -0700395 if DEPTARGET < '10.4' and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000396 result.append(
397 dict(
398 name="PythonSystemFixes",
399 long_name="Fix system Python",
400 readme="""\
401 This package updates the system python installation on
402 Mac OS X 10.3 to ensure that you can build new python extensions
403 using that copy of python after installing this version.
404 """,
405 postflight="../Tools/fixapplepython23.py",
406 topdir="/Library/Frameworks/Python.framework",
407 source="/empty-dir",
408 required=False,
409 selected=unselected_for_python3,
410 )
411 )
412 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000413
Thomas Wouters477c8d52006-05-27 19:21:47 +0000414def fatal(msg):
415 """
416 A fatal error, bail out.
417 """
418 sys.stderr.write('FATAL: ')
419 sys.stderr.write(msg)
420 sys.stderr.write('\n')
421 sys.exit(1)
422
423def fileContents(fn):
424 """
425 Return the contents of the named file
426 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800427 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000428
429def runCommand(commandline):
430 """
Ezio Melotti13925002011-03-16 11:05:33 +0200431 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000432 unless the command fails.
433 """
434 fd = os.popen(commandline, 'r')
435 data = fd.read()
436 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000437 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000438 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800439 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000440
441 if VERBOSE:
442 sys.stdout.write(data); sys.stdout.flush()
443
444def captureCommand(commandline):
445 fd = os.popen(commandline, 'r')
446 data = fd.read()
447 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000448 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000449 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800450 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000451
452 return data
453
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000454def getTclTkVersion(configfile, versionline):
455 """
456 search Tcl or Tk configuration file for version line
457 """
458 try:
459 f = open(configfile, "r")
460 except:
461 fatal("Framework configuration file not found: %s" % configfile)
462
463 for l in f:
464 if l.startswith(versionline):
465 f.close()
466 return l
467
468 fatal("Version variable %s not found in framework configuration file: %s"
469 % (versionline, configfile))
470
Thomas Wouters477c8d52006-05-27 19:21:47 +0000471def checkEnvironment():
472 """
473 Check that we're running on a supported system.
474 """
475
Ned Deilye59e4c52011-01-29 18:56:28 +0000476 if sys.version_info[0:2] < (2, 4):
477 fatal("This script must be run with Python 2.4 or later")
478
Thomas Wouters477c8d52006-05-27 19:21:47 +0000479 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000480 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000481
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000482 if int(platform.release().split('.')[0]) < 8:
483 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000484
485 if not os.path.exists(SDKPATH):
486 fatal("Please install the latest version of Xcode and the %s SDK"%(
487 os.path.basename(SDKPATH[:-4])))
488
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000489 # Because we only support dynamic load of only one major/minor version of
490 # Tcl/Tk, ensure:
491 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily2910a7b2012-07-30 02:35:58 -0700492 # higher than the Apple-supplied system version in
493 # SDKROOT/System/Library/Frameworks
494 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
495 # in) SDKROOT/Library/Frameworks with the same version as the system
496 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000497
Ned Deily2910a7b2012-07-30 02:35:58 -0700498 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000499 for framework in ['Tcl', 'Tk']:
Ned Deily2910a7b2012-07-30 02:35:58 -0700500 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000501 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700502 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000503 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700504 frameworks[framework] = os.readlink(sysfw)
505 if not os.path.exists(libfw):
506 fatal("Please install a link to a current %s %s as %s so "
507 "the user can override the system framework."
508 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000509 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000510 fatal("Version of %s must match %s" % (libfw, sysfw) )
511 if os.path.exists(usrfw):
512 fatal("Please rename %s to avoid possible dynamic load issues."
513 % usrfw)
514
Ned Deily2910a7b2012-07-30 02:35:58 -0700515 if frameworks['Tcl'] != frameworks['Tk']:
516 fatal("The Tcl and Tk frameworks are not the same version.")
517
518 # add files to check after build
519 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
520 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
521 % frameworks['Tcl'],
522 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
523 % frameworks['Tk'],
524 ]
525
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000526 # Remove inherited environment variables which might influence build
527 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
528 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
529 for ev in list(os.environ):
530 for prefix in environ_var_prefixes:
531 if ev.startswith(prefix) :
Ned Deily7d9cf832010-11-27 16:42:15 -0800532 print("INFO: deleting environment variable %s=%s" % (
533 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000534 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535
Ned Deilydfca8c92012-08-06 06:34:00 -0700536 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
537 if 'SDK_TOOLS_BIN' in os.environ:
538 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
539 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
540 # add its fixed location here if it exists
541 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
542 if os.path.isdir(OLD_DEVELOPER_TOOLS):
543 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
544 os.environ['PATH'] = base_path
Ned Deily7d9cf832010-11-27 16:42:15 -0800545 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussoren1e99be72010-04-20 06:36:47 +0000546
Thomas Wouters477c8d52006-05-27 19:21:47 +0000547
Thomas Wouters89f507f2006-12-13 04:49:30 +0000548def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549 """
550 Parse arguments and update global settings.
551 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000552 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilybbd34372012-08-22 23:34:50 -0700553 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +0000554
555 if args is None:
556 args = sys.argv[1:]
557
558 try:
559 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000560 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
561 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily7d9cf832010-11-27 16:42:15 -0800562 except getopt.GetoptError:
563 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000564 sys.exit(1)
565
566 if args:
Ned Deily7d9cf832010-11-27 16:42:15 -0800567 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000568 sys.exit(1)
569
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000570 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000571 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000572 if k in ('-h', '-?', '--help'):
Ned Deily7d9cf832010-11-27 16:42:15 -0800573 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000574 sys.exit(0)
575
576 elif k in ('-d', '--build-dir'):
577 WORKDIR=v
578
579 elif k in ('--third-party',):
580 DEPSRC=v
581
582 elif k in ('--sdk-path',):
583 SDKPATH=v
584
585 elif k in ('--src-dir',):
586 SRCDIR=v
587
Ronald Oussoren1943f862009-03-30 19:39:14 +0000588 elif k in ('--dep-target', ):
589 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000590 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000591
592 elif k in ('--universal-archs', ):
593 if v in UNIVERSALOPTS:
594 UNIVERSALARCHS = v
595 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000596 if deptarget is None:
597 # Select alternate default deployment
598 # target
599 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000600 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800601 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000602
Thomas Wouters477c8d52006-05-27 19:21:47 +0000603 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800604 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000605
606 SRCDIR=os.path.abspath(SRCDIR)
607 WORKDIR=os.path.abspath(WORKDIR)
608 SDKPATH=os.path.abspath(SDKPATH)
609 DEPSRC=os.path.abspath(DEPSRC)
610
Ned Deilybbd34372012-08-22 23:34:50 -0700611 CC, CXX=target_cc_map[DEPTARGET]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000612
Ned Deily7d9cf832010-11-27 16:42:15 -0800613 print("Settings:")
614 print(" * Source directory:", SRCDIR)
615 print(" * Build directory: ", WORKDIR)
616 print(" * SDK location: ", SDKPATH)
617 print(" * Third-party source:", DEPSRC)
618 print(" * Deployment target:", DEPTARGET)
619 print(" * Universal architectures:", ARCHLIST)
620 print(" * C compiler:", CC)
Ned Deilybbd34372012-08-22 23:34:50 -0700621 print(" * C++ compiler:", CXX)
Ned Deily7d9cf832010-11-27 16:42:15 -0800622 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000623
624
625
626
627def extractArchive(builddir, archiveName):
628 """
629 Extract a source archive into 'builddir'. Returns the path of the
630 extracted archive.
631
632 XXX: This function assumes that archives contain a toplevel directory
633 that is has the same name as the basename of the archive. This is
634 save enough for anything we use.
635 """
636 curdir = os.getcwd()
637 try:
638 os.chdir(builddir)
639 if archiveName.endswith('.tar.gz'):
640 retval = os.path.basename(archiveName[:-7])
641 if os.path.exists(retval):
642 shutil.rmtree(retval)
643 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
644
645 elif archiveName.endswith('.tar.bz2'):
646 retval = os.path.basename(archiveName[:-8])
647 if os.path.exists(retval):
648 shutil.rmtree(retval)
649 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
650
651 elif archiveName.endswith('.tar'):
652 retval = os.path.basename(archiveName[:-4])
653 if os.path.exists(retval):
654 shutil.rmtree(retval)
655 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
656
657 elif archiveName.endswith('.zip'):
658 retval = os.path.basename(archiveName[:-4])
659 if os.path.exists(retval):
660 shutil.rmtree(retval)
661 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
662
663 data = fp.read()
664 xit = fp.close()
665 if xit is not None:
666 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800667 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000668
669 return os.path.join(builddir, retval)
670
671 finally:
672 os.chdir(curdir)
673
Thomas Wouters477c8d52006-05-27 19:21:47 +0000674def downloadURL(url, fname):
675 """
676 Download the contents of the url into the file.
677 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800678 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000679 fpOut = open(fname, 'wb')
680 block = fpIn.read(10240)
681 try:
682 while block:
683 fpOut.write(block)
684 block = fpIn.read(10240)
685 fpIn.close()
686 fpOut.close()
687 except:
688 try:
689 os.unlink(fname)
690 except:
691 pass
692
Ned Deilya0abb442012-08-06 06:40:48 -0700693def verifyThirdPartyFile(url, checksum, fname):
694 """
695 Download file from url to filename fname if it does not already exist.
696 Abort if file contents does not match supplied md5 checksum.
697 """
698 name = os.path.basename(fname)
699 if os.path.exists(fname):
700 print("Using local copy of %s"%(name,))
701 else:
702 print("Did not find local copy of %s"%(name,))
703 print("Downloading %s"%(name,))
704 downloadURL(url, fname)
705 print("Archive for %s stored as %s"%(name, fname))
706 if os.system(
707 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
708 % (shellQuote(fname), checksum) ):
709 fatal('MD5 checksum mismatch for file %s' % fname)
710
Thomas Wouters477c8d52006-05-27 19:21:47 +0000711def buildRecipe(recipe, basedir, archList):
712 """
713 Build software using a recipe. This function does the
714 'configure;make;make install' dance for C software, with a possibility
715 to customize this process, basically a poor-mans DarwinPorts.
716 """
717 curdir = os.getcwd()
718
719 name = recipe['name']
720 url = recipe['url']
721 configure = recipe.get('configure', './configure')
722 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
723 shellQuote(basedir)))
724
725 archiveName = os.path.split(url)[-1]
726 sourceArchive = os.path.join(DEPSRC, archiveName)
727
728 if not os.path.exists(DEPSRC):
729 os.mkdir(DEPSRC)
730
Ned Deilya0abb442012-08-06 06:40:48 -0700731 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
Ned Deily7d9cf832010-11-27 16:42:15 -0800732 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000733 buildDir=os.path.join(WORKDIR, '_bld')
734 if not os.path.exists(buildDir):
735 os.mkdir(buildDir)
736
737 workDir = extractArchive(buildDir, sourceArchive)
738 os.chdir(workDir)
739 if 'buildDir' in recipe:
740 os.chdir(recipe['buildDir'])
741
Ned Deilya0abb442012-08-06 06:40:48 -0700742 for patch in recipe.get('patches', ()):
743 if isinstance(patch, tuple):
744 url, checksum = patch
745 fn = os.path.join(DEPSRC, os.path.basename(url))
746 verifyThirdPartyFile(url, checksum, fn)
747 else:
748 # patch is a file in the source directory
749 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000750 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
751 shellQuote(fn),))
752
Ned Deilya0abb442012-08-06 06:40:48 -0700753 for patchscript in recipe.get('patchscripts', ()):
754 if isinstance(patchscript, tuple):
755 url, checksum = patchscript
756 fn = os.path.join(DEPSRC, os.path.basename(url))
757 verifyThirdPartyFile(url, checksum, fn)
758 else:
759 # patch is a file in the source directory
760 fn = os.path.join(curdir, patchscript)
761 if fn.endswith('.bz2'):
762 runCommand('bunzip2 -fk %s' % shellQuote(fn))
763 fn = fn[:-4]
764 runCommand('sh %s' % shellQuote(fn))
765 os.unlink(fn)
766
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000767 if configure is not None:
768 configure_args = [
769 "--prefix=/usr/local",
770 "--enable-static",
771 "--disable-shared",
772 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
773 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000774
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000775 if 'configure_pre' in recipe:
776 args = list(recipe['configure_pre'])
777 if '--disable-static' in args:
778 configure_args.remove('--enable-static')
779 if '--enable-shared' in args:
780 configure_args.remove('--disable-shared')
781 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000782
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000783 if recipe.get('useLDFlags', 1):
784 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700785 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
786 "-I%s/usr/local/include"%(
787 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700788 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000789 ' -arch '.join(archList),
790 shellQuote(SDKPATH)[1:-1],
791 shellQuote(basedir)[1:-1],),
Ned Deilya0abb442012-08-06 06:40:48 -0700792 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
793 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000794 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000795 shellQuote(basedir)[1:-1],
796 ' -arch '.join(archList)),
797 ])
798 else:
799 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700800 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
801 "-I%s/usr/local/include"%(
802 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700803 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000804 ' -arch '.join(archList),
805 shellQuote(SDKPATH)[1:-1],
806 shellQuote(basedir)[1:-1],),
807 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000808
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000809 if 'configure_post' in recipe:
Ned Deilya0abb442012-08-06 06:40:48 -0700810 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000811
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000812 configure_args.insert(0, configure)
813 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000814
Ned Deily7d9cf832010-11-27 16:42:15 -0800815 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000816 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000817
Ned Deily7d9cf832010-11-27 16:42:15 -0800818 print("Running install for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000819 runCommand('{ ' + install + ' ;} 2>&1')
820
Ned Deily7d9cf832010-11-27 16:42:15 -0800821 print("Done %s"%(name,))
822 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000823
824 os.chdir(curdir)
825
826def buildLibraries():
827 """
828 Build our dependencies into $WORKDIR/libraries/usr/local
829 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800830 print("")
831 print("Building required libraries")
832 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000833 universal = os.path.join(WORKDIR, 'libraries')
834 os.mkdir(universal)
835 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
836 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
837
Ronald Oussoren1943f862009-03-30 19:39:14 +0000838 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000839 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000840
841
842
843def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000844 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000845 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily7d9cf832010-11-27 16:42:15 -0800846 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000847 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000848 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000849 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000850 curDir = os.getcwd()
851 os.chdir(buildDir)
852 runCommand('make update')
Martin v. Löwis6120ddb2010-04-22 13:16:44 +0000853 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000854 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000855 if not os.path.exists(docdir):
856 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000857 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000858
859
860def buildPython():
Ned Deily7d9cf832010-11-27 16:42:15 -0800861 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000862
863 buildDir = os.path.join(WORKDIR, '_bld', 'python')
864 rootDir = os.path.join(WORKDIR, '_root')
865
866 if os.path.exists(buildDir):
867 shutil.rmtree(buildDir)
868 if os.path.exists(rootDir):
869 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +0000870 os.makedirs(buildDir)
871 os.makedirs(rootDir)
872 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000873 curdir = os.getcwd()
874 os.chdir(buildDir)
875
876 # Not sure if this is still needed, the original build script
877 # claims that parts of the install assume python.exe exists.
878 os.symlink('python', os.path.join(buildDir, 'python.exe'))
879
880 # Extract the version from the configure file, needed to calculate
881 # several paths.
882 version = getVersion()
883
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000884 # Since the extra libs are not in their installed framework location
885 # during the build, augment the library path so that the interpreter
886 # will find them during its extension import sanity checks.
887 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
888 'libraries', 'usr', 'local', 'lib')
Ned Deily7d9cf832010-11-27 16:42:15 -0800889 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +0000890 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000891 "--with-universal-archs=%s "
892 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +0000893 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
894 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
895 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
896 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000897 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +0000898 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000899 shellQuote(WORKDIR)[1:-1]))
900
Ned Deily7d9cf832010-11-27 16:42:15 -0800901 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000902 runCommand("make")
903
Ned Deily7d9cf832010-11-27 16:42:15 -0800904 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000905 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000906 shellQuote(rootDir)))
907
Ned Deily7d9cf832010-11-27 16:42:15 -0800908 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000909 runCommand("make frameworkinstallextras DESTDIR=%s"%(
910 shellQuote(rootDir)))
911
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000912 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily7d9cf832010-11-27 16:42:15 -0800913 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000914 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
915 runCommand("mv %s/* %s"%(
916 shellQuote(os.path.join(
917 WORKDIR, 'libraries', 'Library', 'Frameworks',
918 'Python.framework', 'Versions', getVersion(),
919 'lib')),
920 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
921 'Python.framework', 'Versions', getVersion(),
922 'lib'))))
923
Ned Deily7d9cf832010-11-27 16:42:15 -0800924 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000925 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000926 gid = grp.getgrnam('admin').gr_gid
927
Ned Deily2910a7b2012-07-30 02:35:58 -0700928 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +0000929 for dirpath, dirnames, filenames in os.walk(frmDir):
930 for dn in dirnames:
Ned Deily7d9cf832010-11-27 16:42:15 -0800931 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000932 os.chown(os.path.join(dirpath, dn), -1, gid)
933
Thomas Wouters477c8d52006-05-27 19:21:47 +0000934 for fn in filenames:
935 if os.path.islink(fn):
936 continue
937
938 # "chmod g+w $fn"
939 p = os.path.join(dirpath, fn)
940 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000941 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
942 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000943
Ned Deily2910a7b2012-07-30 02:35:58 -0700944 if fn in EXPECTED_SHARED_LIBS:
945 # check to see that this file was linked with the
946 # expected library path and version
947 data = captureCommand("otool -L %s" % shellQuote(p))
948 for sl in EXPECTED_SHARED_LIBS[fn]:
949 if ("\t%s " % sl) not in data:
950 print("Expected shared lib %s was not linked with %s"
951 % (sl, p))
952 shared_lib_error = True
953
954 if shared_lib_error:
955 fatal("Unexpected shared library errors.")
956
Ned Deilye59e4c52011-01-29 18:56:28 +0000957 if PYTHON_3:
958 LDVERSION=None
959 VERSION=None
960 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000961
Ned Deilye59e4c52011-01-29 18:56:28 +0000962 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000963 for ln in fp:
964 if ln.startswith('VERSION='):
965 VERSION=ln.split()[1]
966 if ln.startswith('ABIFLAGS='):
967 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000968 if ln.startswith('LDVERSION='):
969 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +0000970 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000971
Ned Deilye59e4c52011-01-29 18:56:28 +0000972 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
973 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
974 config_suffix = '-' + LDVERSION
975 else:
976 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000977
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978 # We added some directories to the search path during the configure
979 # phase. Remove those because those directories won't be there on
Ned Deilya606aef2012-07-21 10:48:09 -0700980 # the end-users system. Also remove the directories from _sysconfigdata.py
981 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000982
Ned Deilya606aef2012-07-21 10:48:09 -0700983 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
984 'Python.framework', 'Versions',
985 version, 'lib', 'python%s'%(version,))
986 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
987 os.path.join(path_to_lib, '_sysconfigdata.py')]
988 for path in paths:
989 if not os.path.exists(path):
990 continue
991 fp = open(path, 'r')
992 data = fp.read()
993 fp.close()
994
Ned Deily2c80e122012-07-22 00:46:46 -0700995 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
996 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
Ned Deilya606aef2012-07-21 10:48:09 -0700997 fp = open(path, 'w')
998 fp.write(data)
999 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001000
1001 # Add symlinks in /usr/local/bin, using relative links
1002 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1003 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1004 'Python.framework', 'Versions', version, 'bin')
1005 if os.path.exists(usr_local_bin):
1006 shutil.rmtree(usr_local_bin)
1007 os.makedirs(usr_local_bin)
1008 for fn in os.listdir(
1009 os.path.join(frmDir, 'Versions', version, 'bin')):
1010 os.symlink(os.path.join(to_framework, fn),
1011 os.path.join(usr_local_bin, fn))
1012
1013 os.chdir(curdir)
1014
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001015 if PYTHON_3:
1016 # Remove the 'Current' link, that way we don't accidently mess
1017 # with an already installed version of python 2
1018 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1019 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001020
1021def patchFile(inPath, outPath):
1022 data = fileContents(inPath)
1023 data = data.replace('$FULL_VERSION', getFullVersion())
1024 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001025 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001026 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001027 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001028
1029 # This one is not handy as a template variable
1030 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily7d9cf832010-11-27 16:42:15 -08001031 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001032 fp.write(data)
1033 fp.close()
1034
1035def patchScript(inPath, outPath):
1036 data = fileContents(inPath)
1037 data = data.replace('@PYVER@', getVersion())
Ned Deily7d9cf832010-11-27 16:42:15 -08001038 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001039 fp.write(data)
1040 fp.close()
Ned Deily7d9cf832010-11-27 16:42:15 -08001041 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001042
1043
1044
1045def packageFromRecipe(targetDir, recipe):
1046 curdir = os.getcwd()
1047 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001048 # The major version (such as 2.5) is included in the package name
1049 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001050 # common.
1051 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001052 srcdir = recipe.get('source')
1053 pkgroot = recipe.get('topdir', srcdir)
1054 postflight = recipe.get('postflight')
1055 readme = textwrap.dedent(recipe['readme'])
1056 isRequired = recipe.get('required', True)
1057
Ned Deily7d9cf832010-11-27 16:42:15 -08001058 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001059
1060 # Substitute some variables
1061 textvars = dict(
1062 VER=getVersion(),
1063 FULLVER=getFullVersion(),
1064 )
1065 readme = readme % textvars
1066
1067 if pkgroot is not None:
1068 pkgroot = pkgroot % textvars
1069 else:
1070 pkgroot = '/'
1071
1072 if srcdir is not None:
1073 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1074 srcdir = srcdir % textvars
1075
1076 if postflight is not None:
1077 postflight = os.path.abspath(postflight)
1078
1079 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1080 os.makedirs(packageContents)
1081
1082 if srcdir is not None:
1083 os.chdir(srcdir)
1084 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1085 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1086 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1087
1088 fn = os.path.join(packageContents, 'PkgInfo')
1089 fp = open(fn, 'w')
1090 fp.write('pmkrpkg1')
1091 fp.close()
1092
1093 rsrcDir = os.path.join(packageContents, "Resources")
1094 os.mkdir(rsrcDir)
1095 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1096 fp.write(readme)
1097 fp.close()
1098
1099 if postflight is not None:
1100 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1101
1102 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001103 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001104 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001105 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1106 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1107 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001108 CFBundleShortVersionString=vers,
1109 IFMajorVersion=major,
1110 IFMinorVersion=minor,
1111 IFPkgFormatVersion=0.10000000149011612,
1112 IFPkgFlagAllowBackRev=False,
1113 IFPkgFlagAuthorizationAction="RootAuthorization",
1114 IFPkgFlagDefaultLocation=pkgroot,
1115 IFPkgFlagFollowLinks=True,
1116 IFPkgFlagInstallFat=True,
1117 IFPkgFlagIsRequired=isRequired,
1118 IFPkgFlagOverwritePermissions=False,
1119 IFPkgFlagRelocatable=False,
1120 IFPkgFlagRestartAction="NoRestart",
1121 IFPkgFlagRootVolumeOnly=True,
1122 IFPkgFlagUpdateInstalledLangauges=False,
1123 )
1124 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1125
1126 pl = Plist(
1127 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001128 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001129 IFPkgDescriptionVersion=vers,
1130 )
1131 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1132
1133 finally:
1134 os.chdir(curdir)
1135
1136
1137def makeMpkgPlist(path):
1138
1139 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001140 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001141
1142 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001143 CFBundleGetInfoString="Python %s"%(vers,),
1144 CFBundleIdentifier='org.python.Python',
1145 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001146 CFBundleShortVersionString=vers,
1147 IFMajorVersion=major,
1148 IFMinorVersion=minor,
1149 IFPkgFlagComponentDirectory="Contents/Packages",
1150 IFPkgFlagPackageList=[
1151 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001152 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001153 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001154 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001155 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001156 ],
1157 IFPkgFormatVersion=0.10000000149011612,
1158 IFPkgFlagBackgroundScaling="proportional",
1159 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001160 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001161 )
1162
1163 writePlist(pl, path)
1164
1165
1166def buildInstaller():
1167
1168 # Zap all compiled files
1169 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1170 for fn in filenames:
1171 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1172 os.unlink(os.path.join(dirpath, fn))
1173
1174 outdir = os.path.join(WORKDIR, 'installer')
1175 if os.path.exists(outdir):
1176 shutil.rmtree(outdir)
1177 os.mkdir(outdir)
1178
Ronald Oussoren1943f862009-03-30 19:39:14 +00001179 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001180 pkgcontents = os.path.join(pkgroot, 'Packages')
1181 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001182 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001183 packageFromRecipe(pkgcontents, recipe)
1184
1185 rsrcDir = os.path.join(pkgroot, 'Resources')
1186
1187 fn = os.path.join(pkgroot, 'PkgInfo')
1188 fp = open(fn, 'w')
1189 fp.write('pmkrpkg1')
1190 fp.close()
1191
1192 os.mkdir(rsrcDir)
1193
1194 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1195 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001196 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001197 IFPkgDescriptionVersion=getVersion(),
1198 )
1199
1200 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1201 for fn in os.listdir('resources'):
1202 if fn == '.svn': continue
1203 if fn.endswith('.jpg'):
1204 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1205 else:
1206 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1207
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001208 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001209
1210
1211def installSize(clear=False, _saved=[]):
1212 if clear:
1213 del _saved[:]
1214 if not _saved:
1215 data = captureCommand("du -ks %s"%(
1216 shellQuote(os.path.join(WORKDIR, '_root'))))
1217 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1218 return _saved[0]
1219
1220
1221def buildDMG():
1222 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001223 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001224 """
1225 outdir = os.path.join(WORKDIR, 'diskimage')
1226 if os.path.exists(outdir):
1227 shutil.rmtree(outdir)
1228
1229 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001230 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001231 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001232 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001233 imagepath = imagepath + '.dmg'
1234
1235 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001236 volname='Python %s'%(getFullVersion())
1237 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1238 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001239 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001240 shellQuote(imagepath + ".tmp.dmg" )))
1241
1242
1243 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1244 os.mkdir(os.path.join(WORKDIR, "mnt"))
1245 runCommand("hdiutil attach %s -mountroot %s"%(
1246 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1247
1248 # Custom icon for the DMG, shown when the DMG is mounted.
1249 shutil.copy("../Icons/Disk Image.icns",
1250 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilydfca8c92012-08-06 06:34:00 -07001251 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001252 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1253
1254 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1255
1256 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1257 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1258 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1259 setIcon(imagepath, "../Icons/Disk Image.icns")
1260
1261 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001262
1263 return imagepath
1264
1265
1266def setIcon(filePath, icnsPath):
1267 """
1268 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001269 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001270
Ronald Oussoren70050672010-04-30 15:00:26 +00001271 dirPath = os.path.normpath(os.path.dirname(__file__))
1272 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001273 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1274 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1275 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001276 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1277 if not os.path.exists(appPath):
1278 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001279 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1280 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001281
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001282 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1283 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001284
1285def main():
1286 # First parse options and check if we can perform our work
1287 parseOptions()
1288 checkEnvironment()
1289
Ronald Oussoren1943f862009-03-30 19:39:14 +00001290 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001291 os.environ['CC'] = CC
Ned Deilybbd34372012-08-22 23:34:50 -07001292 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001293
1294 if os.path.exists(WORKDIR):
1295 shutil.rmtree(WORKDIR)
1296 os.mkdir(WORKDIR)
1297
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001298 os.environ['LC_ALL'] = 'C'
1299
Thomas Wouters477c8d52006-05-27 19:21:47 +00001300 # Then build third-party libraries such as sleepycat DB4.
1301 buildLibraries()
1302
1303 # Now build python itself
1304 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001305
1306 # And then build the documentation
1307 # Remove the Deployment Target from the shell
1308 # environment, it's no longer needed and
1309 # an unexpected build target can cause problems
1310 # when Sphinx and its dependencies need to
1311 # be (re-)installed.
1312 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001313 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001314
1315
1316 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001317 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001318 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001319 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001320
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001321 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001322 getVersion(),))
Ned Deily7d9cf832010-11-27 16:42:15 -08001323 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001324 setIcon(folder, "../Icons/Python Folder.icns")
1325
1326 # Create the installer
1327 buildInstaller()
1328
1329 # And copy the readme into the directory containing the installer
1330 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1331
1332 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001333 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001334
1335 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily7d9cf832010-11-27 16:42:15 -08001336 fp.write("# BUILD INFO\n")
1337 fp.write("# Date: %s\n" % time.ctime())
1338 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339 fp.close()
1340
Thomas Wouters477c8d52006-05-27 19:21:47 +00001341 # And copy it to a DMG
1342 buildDMG()
1343
Thomas Wouters477c8d52006-05-27 19:21:47 +00001344if __name__ == "__main__":
1345 main()