blob: ca4c9489345161e51c77258e5b29d56a824aca8d [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 = {
152 '10.3': 'gcc-4.0',
153 '10.4': 'gcc-4.0',
Ned Deily1bc276d2012-06-24 01:27:51 -0700154 '10.5': 'gcc-4.2',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000155 '10.6': 'gcc-4.2',
Ned Deily1bc276d2012-06-24 01:27:51 -0700156 '10.7': 'clang',
157 '10.8': 'clang',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000158}
159
160CC = target_cc_map[DEPTARGET]
161
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,
267 install='make install CC=%s PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
268 CC,
269 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,
279 install='make install CC=%s prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
280 CC,
281 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
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000553 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC
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
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000611 CC=target_cc_map[DEPTARGET]
612
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)
621 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000622
623
624
625
626def extractArchive(builddir, archiveName):
627 """
628 Extract a source archive into 'builddir'. Returns the path of the
629 extracted archive.
630
631 XXX: This function assumes that archives contain a toplevel directory
632 that is has the same name as the basename of the archive. This is
633 save enough for anything we use.
634 """
635 curdir = os.getcwd()
636 try:
637 os.chdir(builddir)
638 if archiveName.endswith('.tar.gz'):
639 retval = os.path.basename(archiveName[:-7])
640 if os.path.exists(retval):
641 shutil.rmtree(retval)
642 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
643
644 elif archiveName.endswith('.tar.bz2'):
645 retval = os.path.basename(archiveName[:-8])
646 if os.path.exists(retval):
647 shutil.rmtree(retval)
648 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
649
650 elif archiveName.endswith('.tar'):
651 retval = os.path.basename(archiveName[:-4])
652 if os.path.exists(retval):
653 shutil.rmtree(retval)
654 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
655
656 elif archiveName.endswith('.zip'):
657 retval = os.path.basename(archiveName[:-4])
658 if os.path.exists(retval):
659 shutil.rmtree(retval)
660 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
661
662 data = fp.read()
663 xit = fp.close()
664 if xit is not None:
665 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800666 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000667
668 return os.path.join(builddir, retval)
669
670 finally:
671 os.chdir(curdir)
672
Thomas Wouters477c8d52006-05-27 19:21:47 +0000673def downloadURL(url, fname):
674 """
675 Download the contents of the url into the file.
676 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800677 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678 fpOut = open(fname, 'wb')
679 block = fpIn.read(10240)
680 try:
681 while block:
682 fpOut.write(block)
683 block = fpIn.read(10240)
684 fpIn.close()
685 fpOut.close()
686 except:
687 try:
688 os.unlink(fname)
689 except:
690 pass
691
Ned Deilya0abb442012-08-06 06:40:48 -0700692def verifyThirdPartyFile(url, checksum, fname):
693 """
694 Download file from url to filename fname if it does not already exist.
695 Abort if file contents does not match supplied md5 checksum.
696 """
697 name = os.path.basename(fname)
698 if os.path.exists(fname):
699 print("Using local copy of %s"%(name,))
700 else:
701 print("Did not find local copy of %s"%(name,))
702 print("Downloading %s"%(name,))
703 downloadURL(url, fname)
704 print("Archive for %s stored as %s"%(name, fname))
705 if os.system(
706 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
707 % (shellQuote(fname), checksum) ):
708 fatal('MD5 checksum mismatch for file %s' % fname)
709
Thomas Wouters477c8d52006-05-27 19:21:47 +0000710def buildRecipe(recipe, basedir, archList):
711 """
712 Build software using a recipe. This function does the
713 'configure;make;make install' dance for C software, with a possibility
714 to customize this process, basically a poor-mans DarwinPorts.
715 """
716 curdir = os.getcwd()
717
718 name = recipe['name']
719 url = recipe['url']
720 configure = recipe.get('configure', './configure')
721 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
722 shellQuote(basedir)))
723
724 archiveName = os.path.split(url)[-1]
725 sourceArchive = os.path.join(DEPSRC, archiveName)
726
727 if not os.path.exists(DEPSRC):
728 os.mkdir(DEPSRC)
729
Ned Deilya0abb442012-08-06 06:40:48 -0700730 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
Ned Deily7d9cf832010-11-27 16:42:15 -0800731 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000732 buildDir=os.path.join(WORKDIR, '_bld')
733 if not os.path.exists(buildDir):
734 os.mkdir(buildDir)
735
736 workDir = extractArchive(buildDir, sourceArchive)
737 os.chdir(workDir)
738 if 'buildDir' in recipe:
739 os.chdir(recipe['buildDir'])
740
Ned Deilya0abb442012-08-06 06:40:48 -0700741 for patch in recipe.get('patches', ()):
742 if isinstance(patch, tuple):
743 url, checksum = patch
744 fn = os.path.join(DEPSRC, os.path.basename(url))
745 verifyThirdPartyFile(url, checksum, fn)
746 else:
747 # patch is a file in the source directory
748 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000749 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
750 shellQuote(fn),))
751
Ned Deilya0abb442012-08-06 06:40:48 -0700752 for patchscript in recipe.get('patchscripts', ()):
753 if isinstance(patchscript, tuple):
754 url, checksum = patchscript
755 fn = os.path.join(DEPSRC, os.path.basename(url))
756 verifyThirdPartyFile(url, checksum, fn)
757 else:
758 # patch is a file in the source directory
759 fn = os.path.join(curdir, patchscript)
760 if fn.endswith('.bz2'):
761 runCommand('bunzip2 -fk %s' % shellQuote(fn))
762 fn = fn[:-4]
763 runCommand('sh %s' % shellQuote(fn))
764 os.unlink(fn)
765
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000766 if configure is not None:
767 configure_args = [
768 "--prefix=/usr/local",
769 "--enable-static",
770 "--disable-shared",
771 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
772 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000773
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000774 if 'configure_pre' in recipe:
775 args = list(recipe['configure_pre'])
776 if '--disable-static' in args:
777 configure_args.remove('--enable-static')
778 if '--enable-shared' in args:
779 configure_args.remove('--disable-shared')
780 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000781
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000782 if recipe.get('useLDFlags', 1):
783 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700784 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
785 "-I%s/usr/local/include"%(
786 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700787 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000788 ' -arch '.join(archList),
789 shellQuote(SDKPATH)[1:-1],
790 shellQuote(basedir)[1:-1],),
Ned Deilya0abb442012-08-06 06:40:48 -0700791 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
792 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000793 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000794 shellQuote(basedir)[1:-1],
795 ' -arch '.join(archList)),
796 ])
797 else:
798 configure_args.extend([
Ned Deily20416a22012-08-07 03:10:57 -0700799 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
800 "-I%s/usr/local/include"%(
801 recipe.get('extra_cflags', ''),
Ned Deilya0abb442012-08-06 06:40:48 -0700802 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000803 ' -arch '.join(archList),
804 shellQuote(SDKPATH)[1:-1],
805 shellQuote(basedir)[1:-1],),
806 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000807
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000808 if 'configure_post' in recipe:
Ned Deilya0abb442012-08-06 06:40:48 -0700809 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000810
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000811 configure_args.insert(0, configure)
812 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000813
Ned Deily7d9cf832010-11-27 16:42:15 -0800814 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000815 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000816
Ned Deily7d9cf832010-11-27 16:42:15 -0800817 print("Running install for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000818 runCommand('{ ' + install + ' ;} 2>&1')
819
Ned Deily7d9cf832010-11-27 16:42:15 -0800820 print("Done %s"%(name,))
821 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000822
823 os.chdir(curdir)
824
825def buildLibraries():
826 """
827 Build our dependencies into $WORKDIR/libraries/usr/local
828 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800829 print("")
830 print("Building required libraries")
831 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000832 universal = os.path.join(WORKDIR, 'libraries')
833 os.mkdir(universal)
834 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
835 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
836
Ronald Oussoren1943f862009-03-30 19:39:14 +0000837 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000838 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000839
840
841
842def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000843 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000844 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily7d9cf832010-11-27 16:42:15 -0800845 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000846 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000847 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000848 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000849 curDir = os.getcwd()
850 os.chdir(buildDir)
851 runCommand('make update')
Martin v. Löwis6120ddb2010-04-22 13:16:44 +0000852 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000853 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000854 if not os.path.exists(docdir):
855 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000856 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000857
858
859def buildPython():
Ned Deily7d9cf832010-11-27 16:42:15 -0800860 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000861
862 buildDir = os.path.join(WORKDIR, '_bld', 'python')
863 rootDir = os.path.join(WORKDIR, '_root')
864
865 if os.path.exists(buildDir):
866 shutil.rmtree(buildDir)
867 if os.path.exists(rootDir):
868 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +0000869 os.makedirs(buildDir)
870 os.makedirs(rootDir)
871 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000872 curdir = os.getcwd()
873 os.chdir(buildDir)
874
875 # Not sure if this is still needed, the original build script
876 # claims that parts of the install assume python.exe exists.
877 os.symlink('python', os.path.join(buildDir, 'python.exe'))
878
879 # Extract the version from the configure file, needed to calculate
880 # several paths.
881 version = getVersion()
882
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000883 # Since the extra libs are not in their installed framework location
884 # during the build, augment the library path so that the interpreter
885 # will find them during its extension import sanity checks.
886 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
887 'libraries', 'usr', 'local', 'lib')
Ned Deily7d9cf832010-11-27 16:42:15 -0800888 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +0000889 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000890 "--with-universal-archs=%s "
891 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +0000892 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
893 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
894 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
895 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000896 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +0000897 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000898 shellQuote(WORKDIR)[1:-1]))
899
Ned Deily7d9cf832010-11-27 16:42:15 -0800900 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000901 runCommand("make")
902
Ned Deily7d9cf832010-11-27 16:42:15 -0800903 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000904 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000905 shellQuote(rootDir)))
906
Ned Deily7d9cf832010-11-27 16:42:15 -0800907 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000908 runCommand("make frameworkinstallextras DESTDIR=%s"%(
909 shellQuote(rootDir)))
910
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000911 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily7d9cf832010-11-27 16:42:15 -0800912 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000913 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
914 runCommand("mv %s/* %s"%(
915 shellQuote(os.path.join(
916 WORKDIR, 'libraries', 'Library', 'Frameworks',
917 'Python.framework', 'Versions', getVersion(),
918 'lib')),
919 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
920 'Python.framework', 'Versions', getVersion(),
921 'lib'))))
922
Ned Deily7d9cf832010-11-27 16:42:15 -0800923 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000924 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000925 gid = grp.getgrnam('admin').gr_gid
926
Ned Deily2910a7b2012-07-30 02:35:58 -0700927 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +0000928 for dirpath, dirnames, filenames in os.walk(frmDir):
929 for dn in dirnames:
Ned Deily7d9cf832010-11-27 16:42:15 -0800930 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000931 os.chown(os.path.join(dirpath, dn), -1, gid)
932
Thomas Wouters477c8d52006-05-27 19:21:47 +0000933 for fn in filenames:
934 if os.path.islink(fn):
935 continue
936
937 # "chmod g+w $fn"
938 p = os.path.join(dirpath, fn)
939 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000940 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
941 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000942
Ned Deily2910a7b2012-07-30 02:35:58 -0700943 if fn in EXPECTED_SHARED_LIBS:
944 # check to see that this file was linked with the
945 # expected library path and version
946 data = captureCommand("otool -L %s" % shellQuote(p))
947 for sl in EXPECTED_SHARED_LIBS[fn]:
948 if ("\t%s " % sl) not in data:
949 print("Expected shared lib %s was not linked with %s"
950 % (sl, p))
951 shared_lib_error = True
952
953 if shared_lib_error:
954 fatal("Unexpected shared library errors.")
955
Ned Deilye59e4c52011-01-29 18:56:28 +0000956 if PYTHON_3:
957 LDVERSION=None
958 VERSION=None
959 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000960
Ned Deilye59e4c52011-01-29 18:56:28 +0000961 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000962 for ln in fp:
963 if ln.startswith('VERSION='):
964 VERSION=ln.split()[1]
965 if ln.startswith('ABIFLAGS='):
966 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000967 if ln.startswith('LDVERSION='):
968 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +0000969 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000970
Ned Deilye59e4c52011-01-29 18:56:28 +0000971 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
972 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
973 config_suffix = '-' + LDVERSION
974 else:
975 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000976
Thomas Wouters477c8d52006-05-27 19:21:47 +0000977 # We added some directories to the search path during the configure
978 # phase. Remove those because those directories won't be there on
Ned Deilya606aef2012-07-21 10:48:09 -0700979 # the end-users system. Also remove the directories from _sysconfigdata.py
980 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000981
Ned Deilya606aef2012-07-21 10:48:09 -0700982 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
983 'Python.framework', 'Versions',
984 version, 'lib', 'python%s'%(version,))
985 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
986 os.path.join(path_to_lib, '_sysconfigdata.py')]
987 for path in paths:
988 if not os.path.exists(path):
989 continue
990 fp = open(path, 'r')
991 data = fp.read()
992 fp.close()
993
Ned Deily2c80e122012-07-22 00:46:46 -0700994 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
995 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
Ned Deilya606aef2012-07-21 10:48:09 -0700996 fp = open(path, 'w')
997 fp.write(data)
998 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000999
1000 # Add symlinks in /usr/local/bin, using relative links
1001 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1002 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1003 'Python.framework', 'Versions', version, 'bin')
1004 if os.path.exists(usr_local_bin):
1005 shutil.rmtree(usr_local_bin)
1006 os.makedirs(usr_local_bin)
1007 for fn in os.listdir(
1008 os.path.join(frmDir, 'Versions', version, 'bin')):
1009 os.symlink(os.path.join(to_framework, fn),
1010 os.path.join(usr_local_bin, fn))
1011
1012 os.chdir(curdir)
1013
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001014 if PYTHON_3:
1015 # Remove the 'Current' link, that way we don't accidently mess
1016 # with an already installed version of python 2
1017 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1018 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001019
1020def patchFile(inPath, outPath):
1021 data = fileContents(inPath)
1022 data = data.replace('$FULL_VERSION', getFullVersion())
1023 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001024 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001025 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001026 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001027
1028 # This one is not handy as a template variable
1029 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily7d9cf832010-11-27 16:42:15 -08001030 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001031 fp.write(data)
1032 fp.close()
1033
1034def patchScript(inPath, outPath):
1035 data = fileContents(inPath)
1036 data = data.replace('@PYVER@', getVersion())
Ned Deily7d9cf832010-11-27 16:42:15 -08001037 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038 fp.write(data)
1039 fp.close()
Ned Deily7d9cf832010-11-27 16:42:15 -08001040 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001041
1042
1043
1044def packageFromRecipe(targetDir, recipe):
1045 curdir = os.getcwd()
1046 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001047 # The major version (such as 2.5) is included in the package name
1048 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001049 # common.
1050 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051 srcdir = recipe.get('source')
1052 pkgroot = recipe.get('topdir', srcdir)
1053 postflight = recipe.get('postflight')
1054 readme = textwrap.dedent(recipe['readme'])
1055 isRequired = recipe.get('required', True)
1056
Ned Deily7d9cf832010-11-27 16:42:15 -08001057 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001058
1059 # Substitute some variables
1060 textvars = dict(
1061 VER=getVersion(),
1062 FULLVER=getFullVersion(),
1063 )
1064 readme = readme % textvars
1065
1066 if pkgroot is not None:
1067 pkgroot = pkgroot % textvars
1068 else:
1069 pkgroot = '/'
1070
1071 if srcdir is not None:
1072 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1073 srcdir = srcdir % textvars
1074
1075 if postflight is not None:
1076 postflight = os.path.abspath(postflight)
1077
1078 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1079 os.makedirs(packageContents)
1080
1081 if srcdir is not None:
1082 os.chdir(srcdir)
1083 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1084 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1085 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1086
1087 fn = os.path.join(packageContents, 'PkgInfo')
1088 fp = open(fn, 'w')
1089 fp.write('pmkrpkg1')
1090 fp.close()
1091
1092 rsrcDir = os.path.join(packageContents, "Resources")
1093 os.mkdir(rsrcDir)
1094 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1095 fp.write(readme)
1096 fp.close()
1097
1098 if postflight is not None:
1099 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1100
1101 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001102 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001103 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001104 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1105 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1106 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107 CFBundleShortVersionString=vers,
1108 IFMajorVersion=major,
1109 IFMinorVersion=minor,
1110 IFPkgFormatVersion=0.10000000149011612,
1111 IFPkgFlagAllowBackRev=False,
1112 IFPkgFlagAuthorizationAction="RootAuthorization",
1113 IFPkgFlagDefaultLocation=pkgroot,
1114 IFPkgFlagFollowLinks=True,
1115 IFPkgFlagInstallFat=True,
1116 IFPkgFlagIsRequired=isRequired,
1117 IFPkgFlagOverwritePermissions=False,
1118 IFPkgFlagRelocatable=False,
1119 IFPkgFlagRestartAction="NoRestart",
1120 IFPkgFlagRootVolumeOnly=True,
1121 IFPkgFlagUpdateInstalledLangauges=False,
1122 )
1123 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1124
1125 pl = Plist(
1126 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001127 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001128 IFPkgDescriptionVersion=vers,
1129 )
1130 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1131
1132 finally:
1133 os.chdir(curdir)
1134
1135
1136def makeMpkgPlist(path):
1137
1138 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001139 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001140
1141 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001142 CFBundleGetInfoString="Python %s"%(vers,),
1143 CFBundleIdentifier='org.python.Python',
1144 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001145 CFBundleShortVersionString=vers,
1146 IFMajorVersion=major,
1147 IFMinorVersion=minor,
1148 IFPkgFlagComponentDirectory="Contents/Packages",
1149 IFPkgFlagPackageList=[
1150 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001151 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001152 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001153 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001154 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001155 ],
1156 IFPkgFormatVersion=0.10000000149011612,
1157 IFPkgFlagBackgroundScaling="proportional",
1158 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001159 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001160 )
1161
1162 writePlist(pl, path)
1163
1164
1165def buildInstaller():
1166
1167 # Zap all compiled files
1168 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1169 for fn in filenames:
1170 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1171 os.unlink(os.path.join(dirpath, fn))
1172
1173 outdir = os.path.join(WORKDIR, 'installer')
1174 if os.path.exists(outdir):
1175 shutil.rmtree(outdir)
1176 os.mkdir(outdir)
1177
Ronald Oussoren1943f862009-03-30 19:39:14 +00001178 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001179 pkgcontents = os.path.join(pkgroot, 'Packages')
1180 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001181 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001182 packageFromRecipe(pkgcontents, recipe)
1183
1184 rsrcDir = os.path.join(pkgroot, 'Resources')
1185
1186 fn = os.path.join(pkgroot, 'PkgInfo')
1187 fp = open(fn, 'w')
1188 fp.write('pmkrpkg1')
1189 fp.close()
1190
1191 os.mkdir(rsrcDir)
1192
1193 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1194 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001195 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001196 IFPkgDescriptionVersion=getVersion(),
1197 )
1198
1199 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1200 for fn in os.listdir('resources'):
1201 if fn == '.svn': continue
1202 if fn.endswith('.jpg'):
1203 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1204 else:
1205 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1206
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001207 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001208
1209
1210def installSize(clear=False, _saved=[]):
1211 if clear:
1212 del _saved[:]
1213 if not _saved:
1214 data = captureCommand("du -ks %s"%(
1215 shellQuote(os.path.join(WORKDIR, '_root'))))
1216 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1217 return _saved[0]
1218
1219
1220def buildDMG():
1221 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001222 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001223 """
1224 outdir = os.path.join(WORKDIR, 'diskimage')
1225 if os.path.exists(outdir):
1226 shutil.rmtree(outdir)
1227
1228 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001229 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001230 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001231 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001232 imagepath = imagepath + '.dmg'
1233
1234 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001235 volname='Python %s'%(getFullVersion())
1236 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1237 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001238 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001239 shellQuote(imagepath + ".tmp.dmg" )))
1240
1241
1242 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1243 os.mkdir(os.path.join(WORKDIR, "mnt"))
1244 runCommand("hdiutil attach %s -mountroot %s"%(
1245 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1246
1247 # Custom icon for the DMG, shown when the DMG is mounted.
1248 shutil.copy("../Icons/Disk Image.icns",
1249 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilydfca8c92012-08-06 06:34:00 -07001250 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001251 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1252
1253 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1254
1255 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1256 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1257 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1258 setIcon(imagepath, "../Icons/Disk Image.icns")
1259
1260 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001261
1262 return imagepath
1263
1264
1265def setIcon(filePath, icnsPath):
1266 """
1267 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001268 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001269
Ronald Oussoren70050672010-04-30 15:00:26 +00001270 dirPath = os.path.normpath(os.path.dirname(__file__))
1271 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001272 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1273 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1274 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001275 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1276 if not os.path.exists(appPath):
1277 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001278 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1279 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001280
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001281 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1282 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001283
1284def main():
1285 # First parse options and check if we can perform our work
1286 parseOptions()
1287 checkEnvironment()
1288
Ronald Oussoren1943f862009-03-30 19:39:14 +00001289 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001290 os.environ['CC'] = CC
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()