blob: a1477d97594c3144098193152808d16787124a3e [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 Deily4d4c0ee2012-04-01 00:17:33 -0700193 result.extend([
194 dict(
195 name="XZ 5.0.3",
196 url="http://tukaani.org/xz/xz-5.0.3.tar.gz",
197 checksum='fefe52f9ecd521de2a8ce38c21a27574',
198 configure_pre=[
199 '--disable-dependency-tracking',
200 ]
Ned Deilya0abb442012-08-06 06:40:48 -0700201 ),
202 dict(
203 name="NCurses 5.9",
204 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
205 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
206 configure_pre=[
207 "--enable-widec",
208 "--without-cxx",
209 "--without-cxx-binding",
210 "--without-ada",
211 "--without-curses-h",
212 "--enable-shared",
213 "--with-shared",
214 "--without-debug",
215 "--without-normal",
216 "--without-termlib",
217 "--without-ticlib",
218 "--without-tests",
219 "--without-manpages",
220 "--datadir=/usr/share",
221 "--sysconfdir=/etc",
222 "--sharedstatedir=/usr/com",
223 "--with-terminfo-dirs=/usr/share/terminfo",
224 "--with-default-terminfo-dir=/usr/share/terminfo",
225 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
226 ],
227 patchscripts=[
228 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
229 "f54bf02a349f96a7c4f0d00922f3a0d4"),
230 ],
231 useLDFlags=False,
232 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
233 shellQuote(os.path.join(WORKDIR, 'libraries')),
234 shellQuote(os.path.join(WORKDIR, 'libraries')),
235 getVersion(),
236 ),
237 ),
Ned Deily4d4c0ee2012-04-01 00:17:33 -0700238 ])
239
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000240 if DEPTARGET < '10.5':
241 result.extend([
242 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000243 name="Bzip2 1.0.6",
244 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
245 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000246 configure=None,
247 install='make install CC=%s PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
248 CC,
249 shellQuote(os.path.join(WORKDIR, 'libraries')),
250 ' -arch '.join(ARCHLIST),
251 SDKPATH,
252 ),
253 ),
254 dict(
255 name="ZLib 1.2.3",
256 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
257 checksum='debc62758716a169df9f62e6ab2bc634',
258 configure=None,
259 install='make install CC=%s prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
260 CC,
261 shellQuote(os.path.join(WORKDIR, 'libraries')),
262 ' -arch '.join(ARCHLIST),
263 SDKPATH,
264 ),
265 ),
266 dict(
267 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000268 name="GNU Readline 6.1.2",
269 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
270 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000271 patchlevel='0',
272 patches=[
273 # The readline maintainers don't do actual micro releases, but
274 # just ship a set of patches.
Ned Deilya0abb442012-08-06 06:40:48 -0700275 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
276 'c642f2e84d820884b0bf9fd176bc6c3f'),
277 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
278 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000279 ]
280 ),
281 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000282 name="SQLite 3.7.4",
283 url="http://www.sqlite.org/sqlite-autoconf-3070400.tar.gz",
284 checksum='8f0c690bfb33c3cbbc2471c3d9ba0158',
285 configure_env=('CFLAGS="-Os'
286 ' -DSQLITE_ENABLE_FTS3'
287 ' -DSQLITE_ENABLE_FTS3_PARENTHESIS'
288 ' -DSQLITE_ENABLE_RTREE'
289 ' -DSQLITE_TCL=0'
290 '"'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000291 configure_pre=[
292 '--enable-threadsafe',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000293 '--enable-shared=no',
294 '--enable-static=yes',
Ned Deily4f7ff782011-01-15 05:29:12 +0000295 '--disable-readline',
296 '--disable-dependency-tracking',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000297 ]
298 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000299 ])
300
Ned Deily4f7ff782011-01-15 05:29:12 +0000301 if not PYTHON_3:
302 result.extend([
303 dict(
304 name="Sleepycat DB 4.7.25",
305 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
306 checksum='ec2b87e833779681a0c3a814aa71359e',
307 buildDir="build_unix",
308 configure="../dist/configure",
309 configure_pre=[
310 '--includedir=/usr/local/include/db4',
311 ]
312 ),
313 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000314
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000315 return result
316
Thomas Wouters477c8d52006-05-27 19:21:47 +0000317
Thomas Wouters477c8d52006-05-27 19:21:47 +0000318# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000319def pkg_recipes():
320 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
321 result = [
322 dict(
323 name="PythonFramework",
324 long_name="Python Framework",
325 source="/Library/Frameworks/Python.framework",
326 readme="""\
327 This package installs Python.framework, that is the python
328 interpreter and the standard library. This also includes Python
329 wrappers for lots of Mac OS X API's.
330 """,
331 postflight="scripts/postflight.framework",
332 selected='selected',
333 ),
334 dict(
335 name="PythonApplications",
336 long_name="GUI Applications",
337 source="/Applications/Python %(VER)s",
338 readme="""\
339 This package installs IDLE (an interactive Python IDE),
340 Python Launcher and Build Applet (create application bundles
341 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000342
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000343 It also installs a number of examples and demos.
344 """,
345 required=False,
346 selected='selected',
347 ),
348 dict(
349 name="PythonUnixTools",
350 long_name="UNIX command-line tools",
351 source="/usr/local/bin",
352 readme="""\
353 This package installs the unix tools in /usr/local/bin for
354 compatibility with older releases of Python. This package
355 is not necessary to use Python.
356 """,
357 required=False,
358 selected='selected',
359 ),
360 dict(
361 name="PythonDocumentation",
362 long_name="Python Documentation",
363 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
364 source="/pydocs",
365 readme="""\
366 This package installs the python documentation at a location
Ned Deilydfca8c92012-08-06 06:34:00 -0700367 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000368 """,
369 postflight="scripts/postflight.documentation",
370 required=False,
371 selected='selected',
372 ),
373 dict(
374 name="PythonProfileChanges",
375 long_name="Shell profile updater",
376 readme="""\
377 This packages updates your shell profile to make sure that
378 the Python tools are found by your shell in preference of
379 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000380
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000381 If you don't install this package you'll have to add
382 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
383 to your PATH by hand.
384 """,
385 postflight="scripts/postflight.patch-profile",
386 topdir="/Library/Frameworks/Python.framework",
387 source="/empty-dir",
388 required=False,
389 selected=unselected_for_python3,
390 ),
391 ]
392
Ned Deily430d7a32012-06-24 00:19:31 -0700393 if DEPTARGET < '10.4' and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000394 result.append(
395 dict(
396 name="PythonSystemFixes",
397 long_name="Fix system Python",
398 readme="""\
399 This package updates the system python installation on
400 Mac OS X 10.3 to ensure that you can build new python extensions
401 using that copy of python after installing this version.
402 """,
403 postflight="../Tools/fixapplepython23.py",
404 topdir="/Library/Frameworks/Python.framework",
405 source="/empty-dir",
406 required=False,
407 selected=unselected_for_python3,
408 )
409 )
410 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000411
Thomas Wouters477c8d52006-05-27 19:21:47 +0000412def fatal(msg):
413 """
414 A fatal error, bail out.
415 """
416 sys.stderr.write('FATAL: ')
417 sys.stderr.write(msg)
418 sys.stderr.write('\n')
419 sys.exit(1)
420
421def fileContents(fn):
422 """
423 Return the contents of the named file
424 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800425 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426
427def runCommand(commandline):
428 """
Ezio Melotti13925002011-03-16 11:05:33 +0200429 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000430 unless the command fails.
431 """
432 fd = os.popen(commandline, 'r')
433 data = fd.read()
434 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000435 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000436 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800437 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000438
439 if VERBOSE:
440 sys.stdout.write(data); sys.stdout.flush()
441
442def captureCommand(commandline):
443 fd = os.popen(commandline, 'r')
444 data = fd.read()
445 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000446 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000447 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800448 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000449
450 return data
451
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000452def getTclTkVersion(configfile, versionline):
453 """
454 search Tcl or Tk configuration file for version line
455 """
456 try:
457 f = open(configfile, "r")
458 except:
459 fatal("Framework configuration file not found: %s" % configfile)
460
461 for l in f:
462 if l.startswith(versionline):
463 f.close()
464 return l
465
466 fatal("Version variable %s not found in framework configuration file: %s"
467 % (versionline, configfile))
468
Thomas Wouters477c8d52006-05-27 19:21:47 +0000469def checkEnvironment():
470 """
471 Check that we're running on a supported system.
472 """
473
Ned Deilye59e4c52011-01-29 18:56:28 +0000474 if sys.version_info[0:2] < (2, 4):
475 fatal("This script must be run with Python 2.4 or later")
476
Thomas Wouters477c8d52006-05-27 19:21:47 +0000477 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000478 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000479
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000480 if int(platform.release().split('.')[0]) < 8:
481 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000482
483 if not os.path.exists(SDKPATH):
484 fatal("Please install the latest version of Xcode and the %s SDK"%(
485 os.path.basename(SDKPATH[:-4])))
486
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000487 # Because we only support dynamic load of only one major/minor version of
488 # Tcl/Tk, ensure:
489 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily2910a7b2012-07-30 02:35:58 -0700490 # higher than the Apple-supplied system version in
491 # SDKROOT/System/Library/Frameworks
492 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
493 # in) SDKROOT/Library/Frameworks with the same version as the system
494 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000495
Ned Deily2910a7b2012-07-30 02:35:58 -0700496 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000497 for framework in ['Tcl', 'Tk']:
Ned Deily2910a7b2012-07-30 02:35:58 -0700498 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000499 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700500 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000501 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily2910a7b2012-07-30 02:35:58 -0700502 frameworks[framework] = os.readlink(sysfw)
503 if not os.path.exists(libfw):
504 fatal("Please install a link to a current %s %s as %s so "
505 "the user can override the system framework."
506 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000507 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000508 fatal("Version of %s must match %s" % (libfw, sysfw) )
509 if os.path.exists(usrfw):
510 fatal("Please rename %s to avoid possible dynamic load issues."
511 % usrfw)
512
Ned Deily2910a7b2012-07-30 02:35:58 -0700513 if frameworks['Tcl'] != frameworks['Tk']:
514 fatal("The Tcl and Tk frameworks are not the same version.")
515
516 # add files to check after build
517 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
518 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
519 % frameworks['Tcl'],
520 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
521 % frameworks['Tk'],
522 ]
523
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000524 # Remove inherited environment variables which might influence build
525 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
526 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
527 for ev in list(os.environ):
528 for prefix in environ_var_prefixes:
529 if ev.startswith(prefix) :
Ned Deily7d9cf832010-11-27 16:42:15 -0800530 print("INFO: deleting environment variable %s=%s" % (
531 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000532 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533
Ned Deilydfca8c92012-08-06 06:34:00 -0700534 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
535 if 'SDK_TOOLS_BIN' in os.environ:
536 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
537 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
538 # add its fixed location here if it exists
539 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
540 if os.path.isdir(OLD_DEVELOPER_TOOLS):
541 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
542 os.environ['PATH'] = base_path
Ned Deily7d9cf832010-11-27 16:42:15 -0800543 print("Setting default PATH: %s"%(os.environ['PATH']))
Ronald Oussoren1e99be72010-04-20 06:36:47 +0000544
Thomas Wouters477c8d52006-05-27 19:21:47 +0000545
Thomas Wouters89f507f2006-12-13 04:49:30 +0000546def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000547 """
548 Parse arguments and update global settings.
549 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000550 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000551 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552
553 if args is None:
554 args = sys.argv[1:]
555
556 try:
557 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000558 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
559 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily7d9cf832010-11-27 16:42:15 -0800560 except getopt.GetoptError:
561 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000562 sys.exit(1)
563
564 if args:
Ned Deily7d9cf832010-11-27 16:42:15 -0800565 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000566 sys.exit(1)
567
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000568 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000569 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000570 if k in ('-h', '-?', '--help'):
Ned Deily7d9cf832010-11-27 16:42:15 -0800571 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000572 sys.exit(0)
573
574 elif k in ('-d', '--build-dir'):
575 WORKDIR=v
576
577 elif k in ('--third-party',):
578 DEPSRC=v
579
580 elif k in ('--sdk-path',):
581 SDKPATH=v
582
583 elif k in ('--src-dir',):
584 SRCDIR=v
585
Ronald Oussoren1943f862009-03-30 19:39:14 +0000586 elif k in ('--dep-target', ):
587 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000588 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000589
590 elif k in ('--universal-archs', ):
591 if v in UNIVERSALOPTS:
592 UNIVERSALARCHS = v
593 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000594 if deptarget is None:
595 # Select alternate default deployment
596 # target
597 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000598 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800599 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000600
Thomas Wouters477c8d52006-05-27 19:21:47 +0000601 else:
Ned Deily7d9cf832010-11-27 16:42:15 -0800602 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000603
604 SRCDIR=os.path.abspath(SRCDIR)
605 WORKDIR=os.path.abspath(WORKDIR)
606 SDKPATH=os.path.abspath(SDKPATH)
607 DEPSRC=os.path.abspath(DEPSRC)
608
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000609 CC=target_cc_map[DEPTARGET]
610
Ned Deily7d9cf832010-11-27 16:42:15 -0800611 print("Settings:")
612 print(" * Source directory:", SRCDIR)
613 print(" * Build directory: ", WORKDIR)
614 print(" * SDK location: ", SDKPATH)
615 print(" * Third-party source:", DEPSRC)
616 print(" * Deployment target:", DEPTARGET)
617 print(" * Universal architectures:", ARCHLIST)
618 print(" * C compiler:", CC)
619 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000620
621
622
623
624def extractArchive(builddir, archiveName):
625 """
626 Extract a source archive into 'builddir'. Returns the path of the
627 extracted archive.
628
629 XXX: This function assumes that archives contain a toplevel directory
630 that is has the same name as the basename of the archive. This is
631 save enough for anything we use.
632 """
633 curdir = os.getcwd()
634 try:
635 os.chdir(builddir)
636 if archiveName.endswith('.tar.gz'):
637 retval = os.path.basename(archiveName[:-7])
638 if os.path.exists(retval):
639 shutil.rmtree(retval)
640 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
641
642 elif archiveName.endswith('.tar.bz2'):
643 retval = os.path.basename(archiveName[:-8])
644 if os.path.exists(retval):
645 shutil.rmtree(retval)
646 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
647
648 elif archiveName.endswith('.tar'):
649 retval = os.path.basename(archiveName[:-4])
650 if os.path.exists(retval):
651 shutil.rmtree(retval)
652 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
653
654 elif archiveName.endswith('.zip'):
655 retval = os.path.basename(archiveName[:-4])
656 if os.path.exists(retval):
657 shutil.rmtree(retval)
658 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
659
660 data = fp.read()
661 xit = fp.close()
662 if xit is not None:
663 sys.stdout.write(data)
Ned Deily7d9cf832010-11-27 16:42:15 -0800664 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665
666 return os.path.join(builddir, retval)
667
668 finally:
669 os.chdir(curdir)
670
Thomas Wouters477c8d52006-05-27 19:21:47 +0000671def downloadURL(url, fname):
672 """
673 Download the contents of the url into the file.
674 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800675 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000676 fpOut = open(fname, 'wb')
677 block = fpIn.read(10240)
678 try:
679 while block:
680 fpOut.write(block)
681 block = fpIn.read(10240)
682 fpIn.close()
683 fpOut.close()
684 except:
685 try:
686 os.unlink(fname)
687 except:
688 pass
689
Ned Deilya0abb442012-08-06 06:40:48 -0700690def verifyThirdPartyFile(url, checksum, fname):
691 """
692 Download file from url to filename fname if it does not already exist.
693 Abort if file contents does not match supplied md5 checksum.
694 """
695 name = os.path.basename(fname)
696 if os.path.exists(fname):
697 print("Using local copy of %s"%(name,))
698 else:
699 print("Did not find local copy of %s"%(name,))
700 print("Downloading %s"%(name,))
701 downloadURL(url, fname)
702 print("Archive for %s stored as %s"%(name, fname))
703 if os.system(
704 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
705 % (shellQuote(fname), checksum) ):
706 fatal('MD5 checksum mismatch for file %s' % fname)
707
Thomas Wouters477c8d52006-05-27 19:21:47 +0000708def buildRecipe(recipe, basedir, archList):
709 """
710 Build software using a recipe. This function does the
711 'configure;make;make install' dance for C software, with a possibility
712 to customize this process, basically a poor-mans DarwinPorts.
713 """
714 curdir = os.getcwd()
715
716 name = recipe['name']
717 url = recipe['url']
718 configure = recipe.get('configure', './configure')
719 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
720 shellQuote(basedir)))
721
722 archiveName = os.path.split(url)[-1]
723 sourceArchive = os.path.join(DEPSRC, archiveName)
724
725 if not os.path.exists(DEPSRC):
726 os.mkdir(DEPSRC)
727
Ned Deilya0abb442012-08-06 06:40:48 -0700728 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
Ned Deily7d9cf832010-11-27 16:42:15 -0800729 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000730 buildDir=os.path.join(WORKDIR, '_bld')
731 if not os.path.exists(buildDir):
732 os.mkdir(buildDir)
733
734 workDir = extractArchive(buildDir, sourceArchive)
735 os.chdir(workDir)
736 if 'buildDir' in recipe:
737 os.chdir(recipe['buildDir'])
738
Ned Deilya0abb442012-08-06 06:40:48 -0700739 for patch in recipe.get('patches', ()):
740 if isinstance(patch, tuple):
741 url, checksum = patch
742 fn = os.path.join(DEPSRC, os.path.basename(url))
743 verifyThirdPartyFile(url, checksum, fn)
744 else:
745 # patch is a file in the source directory
746 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000747 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
748 shellQuote(fn),))
749
Ned Deilya0abb442012-08-06 06:40:48 -0700750 for patchscript in recipe.get('patchscripts', ()):
751 if isinstance(patchscript, tuple):
752 url, checksum = patchscript
753 fn = os.path.join(DEPSRC, os.path.basename(url))
754 verifyThirdPartyFile(url, checksum, fn)
755 else:
756 # patch is a file in the source directory
757 fn = os.path.join(curdir, patchscript)
758 if fn.endswith('.bz2'):
759 runCommand('bunzip2 -fk %s' % shellQuote(fn))
760 fn = fn[:-4]
761 runCommand('sh %s' % shellQuote(fn))
762 os.unlink(fn)
763
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000764 if configure is not None:
765 configure_args = [
766 "--prefix=/usr/local",
767 "--enable-static",
768 "--disable-shared",
769 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
770 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000771
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000772 if 'configure_pre' in recipe:
773 args = list(recipe['configure_pre'])
774 if '--disable-static' in args:
775 configure_args.remove('--enable-static')
776 if '--enable-shared' in args:
777 configure_args.remove('--disable-shared')
778 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000779
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000780 if recipe.get('useLDFlags', 1):
781 configure_args.extend([
Ned Deilya0abb442012-08-06 06:40:48 -0700782 "CFLAGS=-mmacosx-version-min=%s -arch %s -isysroot %s -I%s/usr/local/include"%(
783 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000784 ' -arch '.join(archList),
785 shellQuote(SDKPATH)[1:-1],
786 shellQuote(basedir)[1:-1],),
Ned Deilya0abb442012-08-06 06:40:48 -0700787 "LDFLAGS=-mmacosx-version-min=%s -syslibroot,%s -L%s/usr/local/lib -arch %s"%(
788 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +0000789 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000790 shellQuote(basedir)[1:-1],
791 ' -arch '.join(archList)),
792 ])
793 else:
794 configure_args.extend([
Ned Deilya0abb442012-08-06 06:40:48 -0700795 "CFLAGS=-mmacosx-version-min=%s -arch %s -isysroot %s -I%s/usr/local/include"%(
796 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000797 ' -arch '.join(archList),
798 shellQuote(SDKPATH)[1:-1],
799 shellQuote(basedir)[1:-1],),
800 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000801
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000802 if 'configure_post' in recipe:
Ned Deilya0abb442012-08-06 06:40:48 -0700803 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000804
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000805 configure_args.insert(0, configure)
806 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000807
Ned Deily4f7ff782011-01-15 05:29:12 +0000808 if 'configure_env' in recipe:
809 configure_args.insert(0, recipe['configure_env'])
810
Ned Deily7d9cf832010-11-27 16:42:15 -0800811 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000812 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000813
Ned Deily7d9cf832010-11-27 16:42:15 -0800814 print("Running install for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000815 runCommand('{ ' + install + ' ;} 2>&1')
816
Ned Deily7d9cf832010-11-27 16:42:15 -0800817 print("Done %s"%(name,))
818 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000819
820 os.chdir(curdir)
821
822def buildLibraries():
823 """
824 Build our dependencies into $WORKDIR/libraries/usr/local
825 """
Ned Deily7d9cf832010-11-27 16:42:15 -0800826 print("")
827 print("Building required libraries")
828 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000829 universal = os.path.join(WORKDIR, 'libraries')
830 os.mkdir(universal)
831 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
832 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
833
Ronald Oussoren1943f862009-03-30 19:39:14 +0000834 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000835 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000836
837
838
839def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000840 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000841 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily7d9cf832010-11-27 16:42:15 -0800842 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000843 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000844 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000845 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000846 curDir = os.getcwd()
847 os.chdir(buildDir)
848 runCommand('make update')
Martin v. Löwis6120ddb2010-04-22 13:16:44 +0000849 runCommand("make html PYTHON='%s'" % os.path.abspath(sys.executable))
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000850 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000851 if not os.path.exists(docdir):
852 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000853 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000854
855
856def buildPython():
Ned Deily7d9cf832010-11-27 16:42:15 -0800857 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000858
859 buildDir = os.path.join(WORKDIR, '_bld', 'python')
860 rootDir = os.path.join(WORKDIR, '_root')
861
862 if os.path.exists(buildDir):
863 shutil.rmtree(buildDir)
864 if os.path.exists(rootDir):
865 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +0000866 os.makedirs(buildDir)
867 os.makedirs(rootDir)
868 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000869 curdir = os.getcwd()
870 os.chdir(buildDir)
871
872 # Not sure if this is still needed, the original build script
873 # claims that parts of the install assume python.exe exists.
874 os.symlink('python', os.path.join(buildDir, 'python.exe'))
875
876 # Extract the version from the configure file, needed to calculate
877 # several paths.
878 version = getVersion()
879
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000880 # Since the extra libs are not in their installed framework location
881 # during the build, augment the library path so that the interpreter
882 # will find them during its extension import sanity checks.
883 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
884 'libraries', 'usr', 'local', 'lib')
Ned Deily7d9cf832010-11-27 16:42:15 -0800885 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +0000886 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000887 "--with-universal-archs=%s "
888 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +0000889 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
890 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
891 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
892 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000893 (' ', '--with-computed-gotos ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +0000894 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000895 shellQuote(WORKDIR)[1:-1]))
896
Ned Deily7d9cf832010-11-27 16:42:15 -0800897 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000898 runCommand("make")
899
Ned Deily7d9cf832010-11-27 16:42:15 -0800900 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000901 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000902 shellQuote(rootDir)))
903
Ned Deily7d9cf832010-11-27 16:42:15 -0800904 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000905 runCommand("make frameworkinstallextras DESTDIR=%s"%(
906 shellQuote(rootDir)))
907
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000908 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily7d9cf832010-11-27 16:42:15 -0800909 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000910 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
911 runCommand("mv %s/* %s"%(
912 shellQuote(os.path.join(
913 WORKDIR, 'libraries', 'Library', 'Frameworks',
914 'Python.framework', 'Versions', getVersion(),
915 'lib')),
916 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
917 'Python.framework', 'Versions', getVersion(),
918 'lib'))))
919
Ned Deily7d9cf832010-11-27 16:42:15 -0800920 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000921 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000922 gid = grp.getgrnam('admin').gr_gid
923
Ned Deily2910a7b2012-07-30 02:35:58 -0700924 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +0000925 for dirpath, dirnames, filenames in os.walk(frmDir):
926 for dn in dirnames:
Ned Deily7d9cf832010-11-27 16:42:15 -0800927 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000928 os.chown(os.path.join(dirpath, dn), -1, gid)
929
Thomas Wouters477c8d52006-05-27 19:21:47 +0000930 for fn in filenames:
931 if os.path.islink(fn):
932 continue
933
934 # "chmod g+w $fn"
935 p = os.path.join(dirpath, fn)
936 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000937 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
938 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000939
Ned Deily2910a7b2012-07-30 02:35:58 -0700940 if fn in EXPECTED_SHARED_LIBS:
941 # check to see that this file was linked with the
942 # expected library path and version
943 data = captureCommand("otool -L %s" % shellQuote(p))
944 for sl in EXPECTED_SHARED_LIBS[fn]:
945 if ("\t%s " % sl) not in data:
946 print("Expected shared lib %s was not linked with %s"
947 % (sl, p))
948 shared_lib_error = True
949
950 if shared_lib_error:
951 fatal("Unexpected shared library errors.")
952
Ned Deilye59e4c52011-01-29 18:56:28 +0000953 if PYTHON_3:
954 LDVERSION=None
955 VERSION=None
956 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000957
Ned Deilye59e4c52011-01-29 18:56:28 +0000958 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000959 for ln in fp:
960 if ln.startswith('VERSION='):
961 VERSION=ln.split()[1]
962 if ln.startswith('ABIFLAGS='):
963 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000964 if ln.startswith('LDVERSION='):
965 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +0000966 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000967
Ned Deilye59e4c52011-01-29 18:56:28 +0000968 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
969 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
970 config_suffix = '-' + LDVERSION
971 else:
972 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000973
Thomas Wouters477c8d52006-05-27 19:21:47 +0000974 # We added some directories to the search path during the configure
975 # phase. Remove those because those directories won't be there on
Ned Deilya606aef2012-07-21 10:48:09 -0700976 # the end-users system. Also remove the directories from _sysconfigdata.py
977 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000978
Ned Deilya606aef2012-07-21 10:48:09 -0700979 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
980 'Python.framework', 'Versions',
981 version, 'lib', 'python%s'%(version,))
982 paths = [os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile'),
983 os.path.join(path_to_lib, '_sysconfigdata.py')]
984 for path in paths:
985 if not os.path.exists(path):
986 continue
987 fp = open(path, 'r')
988 data = fp.read()
989 fp.close()
990
Ned Deily2c80e122012-07-22 00:46:46 -0700991 data = data.replace(' -L%s/libraries/usr/local/lib'%(WORKDIR,), '')
992 data = data.replace(' -I%s/libraries/usr/local/include'%(WORKDIR,), '')
Ned Deilya606aef2012-07-21 10:48:09 -0700993 fp = open(path, 'w')
994 fp.write(data)
995 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000996
997 # Add symlinks in /usr/local/bin, using relative links
998 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
999 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1000 'Python.framework', 'Versions', version, 'bin')
1001 if os.path.exists(usr_local_bin):
1002 shutil.rmtree(usr_local_bin)
1003 os.makedirs(usr_local_bin)
1004 for fn in os.listdir(
1005 os.path.join(frmDir, 'Versions', version, 'bin')):
1006 os.symlink(os.path.join(to_framework, fn),
1007 os.path.join(usr_local_bin, fn))
1008
1009 os.chdir(curdir)
1010
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001011 if PYTHON_3:
1012 # Remove the 'Current' link, that way we don't accidently mess
1013 # with an already installed version of python 2
1014 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1015 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001016
1017def patchFile(inPath, outPath):
1018 data = fileContents(inPath)
1019 data = data.replace('$FULL_VERSION', getFullVersion())
1020 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001021 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001022 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001023 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001024
1025 # This one is not handy as a template variable
1026 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily7d9cf832010-11-27 16:42:15 -08001027 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001028 fp.write(data)
1029 fp.close()
1030
1031def patchScript(inPath, outPath):
1032 data = fileContents(inPath)
1033 data = data.replace('@PYVER@', getVersion())
Ned Deily7d9cf832010-11-27 16:42:15 -08001034 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001035 fp.write(data)
1036 fp.close()
Ned Deily7d9cf832010-11-27 16:42:15 -08001037 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038
1039
1040
1041def packageFromRecipe(targetDir, recipe):
1042 curdir = os.getcwd()
1043 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001044 # The major version (such as 2.5) is included in the package name
1045 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001046 # common.
1047 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001048 srcdir = recipe.get('source')
1049 pkgroot = recipe.get('topdir', srcdir)
1050 postflight = recipe.get('postflight')
1051 readme = textwrap.dedent(recipe['readme'])
1052 isRequired = recipe.get('required', True)
1053
Ned Deily7d9cf832010-11-27 16:42:15 -08001054 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001055
1056 # Substitute some variables
1057 textvars = dict(
1058 VER=getVersion(),
1059 FULLVER=getFullVersion(),
1060 )
1061 readme = readme % textvars
1062
1063 if pkgroot is not None:
1064 pkgroot = pkgroot % textvars
1065 else:
1066 pkgroot = '/'
1067
1068 if srcdir is not None:
1069 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1070 srcdir = srcdir % textvars
1071
1072 if postflight is not None:
1073 postflight = os.path.abspath(postflight)
1074
1075 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1076 os.makedirs(packageContents)
1077
1078 if srcdir is not None:
1079 os.chdir(srcdir)
1080 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1081 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1082 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1083
1084 fn = os.path.join(packageContents, 'PkgInfo')
1085 fp = open(fn, 'w')
1086 fp.write('pmkrpkg1')
1087 fp.close()
1088
1089 rsrcDir = os.path.join(packageContents, "Resources")
1090 os.mkdir(rsrcDir)
1091 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1092 fp.write(readme)
1093 fp.close()
1094
1095 if postflight is not None:
1096 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1097
1098 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001099 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001100 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001101 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1102 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1103 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001104 CFBundleShortVersionString=vers,
1105 IFMajorVersion=major,
1106 IFMinorVersion=minor,
1107 IFPkgFormatVersion=0.10000000149011612,
1108 IFPkgFlagAllowBackRev=False,
1109 IFPkgFlagAuthorizationAction="RootAuthorization",
1110 IFPkgFlagDefaultLocation=pkgroot,
1111 IFPkgFlagFollowLinks=True,
1112 IFPkgFlagInstallFat=True,
1113 IFPkgFlagIsRequired=isRequired,
1114 IFPkgFlagOverwritePermissions=False,
1115 IFPkgFlagRelocatable=False,
1116 IFPkgFlagRestartAction="NoRestart",
1117 IFPkgFlagRootVolumeOnly=True,
1118 IFPkgFlagUpdateInstalledLangauges=False,
1119 )
1120 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1121
1122 pl = Plist(
1123 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001124 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001125 IFPkgDescriptionVersion=vers,
1126 )
1127 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1128
1129 finally:
1130 os.chdir(curdir)
1131
1132
1133def makeMpkgPlist(path):
1134
1135 vers = getFullVersion()
Ned Deily7d9cf832010-11-27 16:42:15 -08001136 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001137
1138 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001139 CFBundleGetInfoString="Python %s"%(vers,),
1140 CFBundleIdentifier='org.python.Python',
1141 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001142 CFBundleShortVersionString=vers,
1143 IFMajorVersion=major,
1144 IFMinorVersion=minor,
1145 IFPkgFlagComponentDirectory="Contents/Packages",
1146 IFPkgFlagPackageList=[
1147 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001148 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001149 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001150 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001151 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001152 ],
1153 IFPkgFormatVersion=0.10000000149011612,
1154 IFPkgFlagBackgroundScaling="proportional",
1155 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001156 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001157 )
1158
1159 writePlist(pl, path)
1160
1161
1162def buildInstaller():
1163
1164 # Zap all compiled files
1165 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1166 for fn in filenames:
1167 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1168 os.unlink(os.path.join(dirpath, fn))
1169
1170 outdir = os.path.join(WORKDIR, 'installer')
1171 if os.path.exists(outdir):
1172 shutil.rmtree(outdir)
1173 os.mkdir(outdir)
1174
Ronald Oussoren1943f862009-03-30 19:39:14 +00001175 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001176 pkgcontents = os.path.join(pkgroot, 'Packages')
1177 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001178 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001179 packageFromRecipe(pkgcontents, recipe)
1180
1181 rsrcDir = os.path.join(pkgroot, 'Resources')
1182
1183 fn = os.path.join(pkgroot, 'PkgInfo')
1184 fp = open(fn, 'w')
1185 fp.write('pmkrpkg1')
1186 fp.close()
1187
1188 os.mkdir(rsrcDir)
1189
1190 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1191 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001192 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001193 IFPkgDescriptionVersion=getVersion(),
1194 )
1195
1196 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1197 for fn in os.listdir('resources'):
1198 if fn == '.svn': continue
1199 if fn.endswith('.jpg'):
1200 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1201 else:
1202 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1203
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001204 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001205
1206
1207def installSize(clear=False, _saved=[]):
1208 if clear:
1209 del _saved[:]
1210 if not _saved:
1211 data = captureCommand("du -ks %s"%(
1212 shellQuote(os.path.join(WORKDIR, '_root'))))
1213 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1214 return _saved[0]
1215
1216
1217def buildDMG():
1218 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001219 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001220 """
1221 outdir = os.path.join(WORKDIR, 'diskimage')
1222 if os.path.exists(outdir):
1223 shutil.rmtree(outdir)
1224
1225 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001226 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001227 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001228 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001229 imagepath = imagepath + '.dmg'
1230
1231 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001232 volname='Python %s'%(getFullVersion())
1233 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1234 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001235 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001236 shellQuote(imagepath + ".tmp.dmg" )))
1237
1238
1239 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1240 os.mkdir(os.path.join(WORKDIR, "mnt"))
1241 runCommand("hdiutil attach %s -mountroot %s"%(
1242 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1243
1244 # Custom icon for the DMG, shown when the DMG is mounted.
1245 shutil.copy("../Icons/Disk Image.icns",
1246 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilydfca8c92012-08-06 06:34:00 -07001247 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001248 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1249
1250 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1251
1252 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1253 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1254 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1255 setIcon(imagepath, "../Icons/Disk Image.icns")
1256
1257 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001258
1259 return imagepath
1260
1261
1262def setIcon(filePath, icnsPath):
1263 """
1264 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001265 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001266
Ronald Oussoren70050672010-04-30 15:00:26 +00001267 dirPath = os.path.normpath(os.path.dirname(__file__))
1268 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001269 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1270 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1271 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001272 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1273 if not os.path.exists(appPath):
1274 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001275 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1276 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001277
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001278 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1279 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001280
1281def main():
1282 # First parse options and check if we can perform our work
1283 parseOptions()
1284 checkEnvironment()
1285
Ronald Oussoren1943f862009-03-30 19:39:14 +00001286 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001287 os.environ['CC'] = CC
Thomas Wouters477c8d52006-05-27 19:21:47 +00001288
1289 if os.path.exists(WORKDIR):
1290 shutil.rmtree(WORKDIR)
1291 os.mkdir(WORKDIR)
1292
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001293 os.environ['LC_ALL'] = 'C'
1294
Thomas Wouters477c8d52006-05-27 19:21:47 +00001295 # Then build third-party libraries such as sleepycat DB4.
1296 buildLibraries()
1297
1298 # Now build python itself
1299 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001300
1301 # And then build the documentation
1302 # Remove the Deployment Target from the shell
1303 # environment, it's no longer needed and
1304 # an unexpected build target can cause problems
1305 # when Sphinx and its dependencies need to
1306 # be (re-)installed.
1307 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001308 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001309
1310
1311 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001312 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001313 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001314 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001315
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001316 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001317 getVersion(),))
Ned Deily7d9cf832010-11-27 16:42:15 -08001318 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001319 setIcon(folder, "../Icons/Python Folder.icns")
1320
1321 # Create the installer
1322 buildInstaller()
1323
1324 # And copy the readme into the directory containing the installer
1325 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1326
1327 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001328 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001329
1330 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily7d9cf832010-11-27 16:42:15 -08001331 fp.write("# BUILD INFO\n")
1332 fp.write("# Date: %s\n" % time.ctime())
1333 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001334 fp.close()
1335
Thomas Wouters477c8d52006-05-27 19:21:47 +00001336 # And copy it to a DMG
1337 buildDMG()
1338
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339if __name__ == "__main__":
1340 main()