blob: 740e45ebb61e1f569c89707592beb77b3bbf62c6 [file] [log] [blame]
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001#!/usr/bin/python
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00002"""
3This script is used to build the "official unofficial" universal build on
4Mac OS X. It requires Mac OS X 10.4, Xcode 2.2 and the 10.4u SDK to do its
Ronald Oussoren508282e2009-03-30 19:34:51 +00005work. 64-bit or four-way universal builds require at least OS X 10.5 and
6the 10.5 SDK.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00007
8Please ensure that this script keeps working with Python 2.3, to avoid
9bootstrap issues (/usr/bin/python is Python 2.3 on OSX 10.4)
10
11Usage: see USAGE variable in the script.
12"""
13import platform, os, sys, getopt, textwrap, shutil, urllib2, stat, time, pwd
Ronald Oussoren74d3eef2006-10-10 07:55:06 +000014import grp
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000015
Ronald Oussoren158ad592006-11-07 16:00:34 +000016INCLUDE_TIMESTAMP = 1
17VERBOSE = 1
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000018
19from plistlib import Plist
20
21import MacOS
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000022
23try:
24 from plistlib import writePlist
25except ImportError:
26 # We're run using python2.3
27 def writePlist(plist, path):
28 plist.write(path)
29
30def shellQuote(value):
31 """
Ronald Oussorenaa560962006-11-07 15:53:38 +000032 Return the string value in a form that can safely be inserted into
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000033 a shell command.
34 """
35 return "'%s'"%(value.replace("'", "'\"'\"'"))
36
37def grepValue(fn, variable):
38 variable = variable + '='
39 for ln in open(fn, 'r'):
40 if ln.startswith(variable):
41 value = ln[len(variable):].strip()
42 return value[1:-1]
43
44def getVersion():
45 return grepValue(os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
46
47def getFullVersion():
48 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
49 for ln in open(fn):
50 if 'PY_VERSION' in ln:
51 return ln.split()[-1][1:-1]
52
53 raise RuntimeError, "Cannot find full version??"
54
Ronald Oussorenaa560962006-11-07 15:53:38 +000055# The directory we'll use to create the build (will be erased and recreated)
Ronald Oussoren158ad592006-11-07 16:00:34 +000056WORKDIR = "/tmp/_py"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000057
Ronald Oussorenaa560962006-11-07 15:53:38 +000058# The directory we'll use to store third-party sources. Set this to something
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000059# else if you don't want to re-fetch required libraries every time.
Ronald Oussoren158ad592006-11-07 16:00:34 +000060DEPSRC = os.path.join(WORKDIR, 'third-party')
61DEPSRC = os.path.expanduser('~/Universal/other-sources')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000062
63# Location of the preferred SDK
Ronald Oussoren209d4c32009-09-29 19:34:13 +000064
65### There are some issues with the SDK selection below here,
66### The resulting binary doesn't work on all platforms that
67### it should. Always default to the 10.4u SDK until that
68### isue is resolved.
69###
70##if int(os.uname()[2].split('.')[0]) == 8:
71## # Explicitly use the 10.4u (universal) SDK when
72## # building on 10.4, the system headers are not
73## # useable for a universal build
74## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
75##else:
76## SDKPATH = "/"
77
78SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000079
Ronald Oussoren508282e2009-03-30 19:34:51 +000080universal_opts_map = { '32-bit': ('i386', 'ppc',),
81 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +000082 'intel': ('i386', 'x86_64'),
83 '3-way': ('ppc', 'i386', 'x86_64'),
84 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
85default_target_map = {
86 '64-bit': '10.5',
87 '3-way': '10.5',
88 'intel': '10.5',
89 'all': '10.5',
90}
Ronald Oussoren508282e2009-03-30 19:34:51 +000091
92UNIVERSALOPTS = tuple(universal_opts_map.keys())
93
94UNIVERSALARCHS = '32-bit'
95
96ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +000097
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000098# Source directory (asume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +000099SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000100 os.path.dirname(
101 os.path.dirname(
102 os.path.abspath(__file__
103 ))))
104
Ronald Oussoren508282e2009-03-30 19:34:51 +0000105# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
106DEPTARGET = '10.3'
107
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000108target_cc_map = {
109 '10.3': 'gcc-4.0',
110 '10.4': 'gcc-4.0',
111 '10.5': 'gcc-4.0',
112 '10.6': 'gcc-4.2',
113}
114
115CC = target_cc_map[DEPTARGET]
116
Ronald Oussoren158ad592006-11-07 16:00:34 +0000117USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000118 Usage: build_python [options]
119
120 Options:
121 -? or -h: Show this message
122 -b DIR
123 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
124 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
125 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
126 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000127 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
128 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000129""")% globals()
130
131
132# Instructions for building libraries that are necessary for building a
133# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000134# [The recipes are defined here for convenience but instantiated later after
135# command line options have been processed.]
136def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000137 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000138
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000139 if DEPTARGET < '10.5':
140 result.extend([
141 dict(
142 name="Bzip2 1.0.5",
143 url="http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz",
144 checksum='3c15a0c8d1d3ee1c46a1634d00617b1a',
145 configure=None,
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000146 install='make install CC=%s PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
147 CC,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000148 shellQuote(os.path.join(WORKDIR, 'libraries')),
149 ' -arch '.join(ARCHLIST),
150 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000151 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000152 ),
153 dict(
154 name="ZLib 1.2.3",
155 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
156 checksum='debc62758716a169df9f62e6ab2bc634',
157 configure=None,
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000158 install='make install CC=%s prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
159 CC,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000160 shellQuote(os.path.join(WORKDIR, 'libraries')),
161 ' -arch '.join(ARCHLIST),
162 SDKPATH,
163 ),
164 ),
165 dict(
166 # Note that GNU readline is GPL'd software
167 name="GNU Readline 5.1.4",
168 url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" ,
169 checksum='7ee5a692db88b30ca48927a13fd60e46',
170 patchlevel='0',
171 patches=[
172 # The readline maintainers don't do actual micro releases, but
173 # just ship a set of patches.
174 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-001',
175 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002',
176 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003',
177 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004',
178 ]
179 ),
180 dict(
181 name="SQLite 3.6.11",
182 url="http://www.sqlite.org/sqlite-3.6.11.tar.gz",
183 checksum='7ebb099696ab76cc6ff65dd496d17858',
184 configure_pre=[
185 '--enable-threadsafe',
186 '--enable-tempstore',
187 '--enable-shared=no',
188 '--enable-static=yes',
189 '--disable-tcl',
190 ]
191 ),
192 dict(
193 name="NCurses 5.5",
194 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
195 checksum='e73c1ac10b4bfc46db43b2ddfd6244ef',
196 configure_pre=[
197 "--without-cxx",
198 "--without-ada",
199 "--without-progs",
200 "--without-curses-h",
201 "--enable-shared",
202 "--with-shared",
203 "--datadir=/usr/share",
204 "--sysconfdir=/etc",
205 "--sharedstatedir=/usr/com",
206 "--with-terminfo-dirs=/usr/share/terminfo",
207 "--with-default-terminfo-dir=/usr/share/terminfo",
208 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
209 "--enable-termcap",
210 ],
211 patches=[
212 "ncurses-5.5.patch",
213 ],
214 useLDFlags=False,
215 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
216 shellQuote(os.path.join(WORKDIR, 'libraries')),
217 shellQuote(os.path.join(WORKDIR, 'libraries')),
218 getVersion(),
219 ),
220 ),
221 ])
222
223 result.extend([
Ronald Oussoren508282e2009-03-30 19:34:51 +0000224 dict(
225 name="Sleepycat DB 4.7.25",
226 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
227 checksum='ec2b87e833779681a0c3a814aa71359e',
228 buildDir="build_unix",
229 configure="../dist/configure",
230 configure_pre=[
231 '--includedir=/usr/local/include/db4',
232 ]
233 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000234 ])
235
236 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000237
238
239# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000240def pkg_recipes():
241 result = [
242 dict(
243 name="PythonFramework",
244 long_name="Python Framework",
245 source="/Library/Frameworks/Python.framework",
246 readme="""\
247 This package installs Python.framework, that is the python
248 interpreter and the standard library. This also includes Python
249 wrappers for lots of Mac OS X API's.
250 """,
251 postflight="scripts/postflight.framework",
252 ),
253 dict(
254 name="PythonApplications",
255 long_name="GUI Applications",
256 source="/Applications/Python %(VER)s",
257 readme="""\
258 This package installs IDLE (an interactive Python IDE),
259 Python Launcher and Build Applet (create application bundles
260 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000261
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000262 It also installs a number of examples and demos.
263 """,
264 required=False,
265 ),
266 dict(
267 name="PythonUnixTools",
268 long_name="UNIX command-line tools",
269 source="/usr/local/bin",
270 readme="""\
271 This package installs the unix tools in /usr/local/bin for
272 compatibility with older releases of Python. This package
273 is not necessary to use Python.
274 """,
275 required=False,
276 ),
277 dict(
278 name="PythonDocumentation",
279 long_name="Python Documentation",
280 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
281 source="/pydocs",
282 readme="""\
283 This package installs the python documentation at a location
284 that is useable for pydoc and IDLE. If you have installed Xcode
285 it will also install a link to the documentation in
286 /Developer/Documentation/Python
287 """,
288 postflight="scripts/postflight.documentation",
289 required=False,
290 ),
291 dict(
292 name="PythonProfileChanges",
293 long_name="Shell profile updater",
294 readme="""\
295 This packages updates your shell profile to make sure that
296 the Python tools are found by your shell in preference of
297 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000298
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000299 If you don't install this package you'll have to add
300 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
301 to your PATH by hand.
302 """,
303 postflight="scripts/postflight.patch-profile",
304 topdir="/Library/Frameworks/Python.framework",
305 source="/empty-dir",
306 required=False,
307 ),
308 ]
309
310 if DEPTARGET < '10.4':
311 result.append(
312 dict(
313 name="PythonSystemFixes",
314 long_name="Fix system Python",
315 readme="""\
316 This package updates the system python installation on
317 Mac OS X 10.3 to ensure that you can build new python extensions
318 using that copy of python after installing this version.
319 """,
320 postflight="../Tools/fixapplepython23.py",
321 topdir="/Library/Frameworks/Python.framework",
322 source="/empty-dir",
323 required=False,
324 )
325 )
326 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000327
328def fatal(msg):
329 """
330 A fatal error, bail out.
331 """
332 sys.stderr.write('FATAL: ')
333 sys.stderr.write(msg)
334 sys.stderr.write('\n')
335 sys.exit(1)
336
337def fileContents(fn):
338 """
339 Return the contents of the named file
340 """
341 return open(fn, 'rb').read()
342
343def runCommand(commandline):
344 """
345 Run a command and raise RuntimeError if it fails. Output is surpressed
346 unless the command fails.
347 """
348 fd = os.popen(commandline, 'r')
349 data = fd.read()
350 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000351 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000352 sys.stdout.write(data)
353 raise RuntimeError, "command failed: %s"%(commandline,)
354
355 if VERBOSE:
356 sys.stdout.write(data); sys.stdout.flush()
357
358def captureCommand(commandline):
359 fd = os.popen(commandline, 'r')
360 data = fd.read()
361 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000362 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000363 sys.stdout.write(data)
364 raise RuntimeError, "command failed: %s"%(commandline,)
365
366 return data
367
368def checkEnvironment():
369 """
370 Check that we're running on a supported system.
371 """
372
373 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000374 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000375
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000376 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000377 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000378
379 if not os.path.exists(SDKPATH):
380 fatal("Please install the latest version of Xcode and the %s SDK"%(
381 os.path.basename(SDKPATH[:-4])))
382
383
384
Ronald Oussoren158ad592006-11-07 16:00:34 +0000385def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000386 """
387 Parse arguments and update global settings.
388 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000389 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000390 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000391
392 if args is None:
393 args = sys.argv[1:]
394
395 try:
396 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000397 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
398 'dep-target=', 'universal-archs=', 'help' ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000399 except getopt.error, msg:
400 print msg
401 sys.exit(1)
402
403 if args:
404 print "Additional arguments"
405 sys.exit(1)
406
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000407 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000408 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000409 if k in ('-h', '-?', '--help'):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000410 print USAGE
411 sys.exit(0)
412
413 elif k in ('-d', '--build-dir'):
414 WORKDIR=v
415
416 elif k in ('--third-party',):
417 DEPSRC=v
418
419 elif k in ('--sdk-path',):
420 SDKPATH=v
421
422 elif k in ('--src-dir',):
423 SRCDIR=v
424
Ronald Oussoren508282e2009-03-30 19:34:51 +0000425 elif k in ('--dep-target', ):
426 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000427 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000428
429 elif k in ('--universal-archs', ):
430 if v in UNIVERSALOPTS:
431 UNIVERSALARCHS = v
432 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000433 if deptarget is None:
434 # Select alternate default deployment
435 # target
436 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000437 else:
438 raise NotImplementedError, v
439
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000440 else:
441 raise NotImplementedError, k
442
443 SRCDIR=os.path.abspath(SRCDIR)
444 WORKDIR=os.path.abspath(WORKDIR)
445 SDKPATH=os.path.abspath(SDKPATH)
446 DEPSRC=os.path.abspath(DEPSRC)
447
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000448 CC=target_cc_map[DEPTARGET]
449
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000450 print "Settings:"
451 print " * Source directory:", SRCDIR
452 print " * Build directory: ", WORKDIR
453 print " * SDK location: ", SDKPATH
Ronald Oussoren508282e2009-03-30 19:34:51 +0000454 print " * Third-party source:", DEPSRC
455 print " * Deployment target:", DEPTARGET
456 print " * Universal architectures:", ARCHLIST
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000457 print " * C compiler:", CC
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000458 print ""
459
460
461
462
463def extractArchive(builddir, archiveName):
464 """
465 Extract a source archive into 'builddir'. Returns the path of the
466 extracted archive.
467
468 XXX: This function assumes that archives contain a toplevel directory
469 that is has the same name as the basename of the archive. This is
470 save enough for anything we use.
471 """
472 curdir = os.getcwd()
473 try:
474 os.chdir(builddir)
475 if archiveName.endswith('.tar.gz'):
476 retval = os.path.basename(archiveName[:-7])
477 if os.path.exists(retval):
478 shutil.rmtree(retval)
479 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
480
481 elif archiveName.endswith('.tar.bz2'):
482 retval = os.path.basename(archiveName[:-8])
483 if os.path.exists(retval):
484 shutil.rmtree(retval)
485 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
486
487 elif archiveName.endswith('.tar'):
488 retval = os.path.basename(archiveName[:-4])
489 if os.path.exists(retval):
490 shutil.rmtree(retval)
491 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
492
493 elif archiveName.endswith('.zip'):
494 retval = os.path.basename(archiveName[:-4])
495 if os.path.exists(retval):
496 shutil.rmtree(retval)
497 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
498
499 data = fp.read()
500 xit = fp.close()
501 if xit is not None:
502 sys.stdout.write(data)
503 raise RuntimeError, "Cannot extract %s"%(archiveName,)
504
505 return os.path.join(builddir, retval)
506
507 finally:
508 os.chdir(curdir)
509
510KNOWNSIZES = {
511 "http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz": 7952742,
512 "http://downloads.sleepycat.com/db-4.4.20.tar.gz": 2030276,
513}
514
515def downloadURL(url, fname):
516 """
517 Download the contents of the url into the file.
518 """
519 try:
520 size = os.path.getsize(fname)
521 except OSError:
522 pass
523 else:
524 if KNOWNSIZES.get(url) == size:
525 print "Using existing file for", url
526 return
527 fpIn = urllib2.urlopen(url)
528 fpOut = open(fname, 'wb')
529 block = fpIn.read(10240)
530 try:
531 while block:
532 fpOut.write(block)
533 block = fpIn.read(10240)
534 fpIn.close()
535 fpOut.close()
536 except:
537 try:
538 os.unlink(fname)
539 except:
540 pass
541
542def buildRecipe(recipe, basedir, archList):
543 """
544 Build software using a recipe. This function does the
545 'configure;make;make install' dance for C software, with a possibility
546 to customize this process, basically a poor-mans DarwinPorts.
547 """
548 curdir = os.getcwd()
549
550 name = recipe['name']
551 url = recipe['url']
552 configure = recipe.get('configure', './configure')
553 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
554 shellQuote(basedir)))
555
556 archiveName = os.path.split(url)[-1]
557 sourceArchive = os.path.join(DEPSRC, archiveName)
558
559 if not os.path.exists(DEPSRC):
560 os.mkdir(DEPSRC)
561
562
563 if os.path.exists(sourceArchive):
564 print "Using local copy of %s"%(name,)
565
566 else:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000567 print "Did not find local copy of %s"%(name,)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000568 print "Downloading %s"%(name,)
569 downloadURL(url, sourceArchive)
570 print "Archive for %s stored as %s"%(name, sourceArchive)
571
572 print "Extracting archive for %s"%(name,)
573 buildDir=os.path.join(WORKDIR, '_bld')
574 if not os.path.exists(buildDir):
575 os.mkdir(buildDir)
576
577 workDir = extractArchive(buildDir, sourceArchive)
578 os.chdir(workDir)
579 if 'buildDir' in recipe:
580 os.chdir(recipe['buildDir'])
581
582
583 for fn in recipe.get('patches', ()):
584 if fn.startswith('http://'):
585 # Download the patch before applying it.
586 path = os.path.join(DEPSRC, os.path.basename(fn))
587 downloadURL(fn, path)
588 fn = path
589
590 fn = os.path.join(curdir, fn)
591 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
592 shellQuote(fn),))
593
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000594 if configure is not None:
595 configure_args = [
596 "--prefix=/usr/local",
597 "--enable-static",
598 "--disable-shared",
599 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
600 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000601
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000602 if 'configure_pre' in recipe:
603 args = list(recipe['configure_pre'])
604 if '--disable-static' in args:
605 configure_args.remove('--enable-static')
606 if '--enable-shared' in args:
607 configure_args.remove('--disable-shared')
608 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000609
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000610 if recipe.get('useLDFlags', 1):
611 configure_args.extend([
612 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
613 ' -arch '.join(archList),
614 shellQuote(SDKPATH)[1:-1],
615 shellQuote(basedir)[1:-1],),
616 "LDFLAGS=-syslibroot,%s -L%s/usr/local/lib -arch %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000617 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000618 shellQuote(basedir)[1:-1],
619 ' -arch '.join(archList)),
620 ])
621 else:
622 configure_args.extend([
623 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
624 ' -arch '.join(archList),
625 shellQuote(SDKPATH)[1:-1],
626 shellQuote(basedir)[1:-1],),
627 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000628
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000629 if 'configure_post' in recipe:
630 configure_args = configure_args = list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000631
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000632 configure_args.insert(0, configure)
633 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000634
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000635 print "Running configure for %s"%(name,)
636 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000637
638 print "Running install for %s"%(name,)
639 runCommand('{ ' + install + ' ;} 2>&1')
640
641 print "Done %s"%(name,)
642 print ""
643
644 os.chdir(curdir)
645
646def buildLibraries():
647 """
648 Build our dependencies into $WORKDIR/libraries/usr/local
649 """
650 print ""
651 print "Building required libraries"
652 print ""
653 universal = os.path.join(WORKDIR, 'libraries')
654 os.mkdir(universal)
655 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
656 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
657
Ronald Oussoren508282e2009-03-30 19:34:51 +0000658 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000659 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000660
661
662
663def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000664 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000665 # inside the framwork. pydoc and IDLE will pick it up there.
666 print "Install python documentation"
667 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000668 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000669 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000670 curDir = os.getcwd()
671 os.chdir(buildDir)
672 runCommand('make update')
673 runCommand('make html')
674 os.chdir(curDir)
675 if not os.path.exists(docdir):
676 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000677 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000678
679
680def buildPython():
Ronald Oussoren508282e2009-03-30 19:34:51 +0000681 print "Building a universal python for %s architectures" % UNIVERSALARCHS
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000682
683 buildDir = os.path.join(WORKDIR, '_bld', 'python')
684 rootDir = os.path.join(WORKDIR, '_root')
685
686 if os.path.exists(buildDir):
687 shutil.rmtree(buildDir)
688 if os.path.exists(rootDir):
689 shutil.rmtree(rootDir)
690 os.mkdir(buildDir)
691 os.mkdir(rootDir)
692 os.mkdir(os.path.join(rootDir, 'empty-dir'))
693 curdir = os.getcwd()
694 os.chdir(buildDir)
695
696 # Not sure if this is still needed, the original build script
697 # claims that parts of the install assume python.exe exists.
698 os.symlink('python', os.path.join(buildDir, 'python.exe'))
699
700 # Extract the version from the configure file, needed to calculate
701 # several paths.
702 version = getVersion()
703
Ronald Oussoren008af852009-03-30 20:02:08 +0000704 # Since the extra libs are not in their installed framework location
705 # during the build, augment the library path so that the interpreter
706 # will find them during its extension import sanity checks.
707 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
708 'libraries', 'usr', 'local', 'lib')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000709 print "Running configure..."
Ronald Oussoren508282e2009-03-30 19:34:51 +0000710 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
711 "--with-universal-archs=%s "
712 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
713 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
714 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
715 UNIVERSALARCHS,
716 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000717 shellQuote(WORKDIR)[1:-1]))
718
719 print "Running make"
720 runCommand("make")
721
Ronald Oussorenaa560962006-11-07 15:53:38 +0000722 print "Running make frameworkinstall"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000723 runCommand("make frameworkinstall DESTDIR=%s"%(
724 shellQuote(rootDir)))
725
Ronald Oussorenaa560962006-11-07 15:53:38 +0000726 print "Running make frameworkinstallextras"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000727 runCommand("make frameworkinstallextras DESTDIR=%s"%(
728 shellQuote(rootDir)))
729
Ronald Oussoren008af852009-03-30 20:02:08 +0000730 del os.environ['DYLD_LIBRARY_PATH']
Ronald Oussorenaa560962006-11-07 15:53:38 +0000731 print "Copying required shared libraries"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000732 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
733 runCommand("mv %s/* %s"%(
734 shellQuote(os.path.join(
735 WORKDIR, 'libraries', 'Library', 'Frameworks',
736 'Python.framework', 'Versions', getVersion(),
737 'lib')),
738 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
739 'Python.framework', 'Versions', getVersion(),
740 'lib'))))
741
742 print "Fix file modes"
743 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000744 gid = grp.getgrnam('admin').gr_gid
745
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000746 for dirpath, dirnames, filenames in os.walk(frmDir):
747 for dn in dirnames:
748 os.chmod(os.path.join(dirpath, dn), 0775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000749 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +0000750
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000751
752 for fn in filenames:
753 if os.path.islink(fn):
754 continue
755
756 # "chmod g+w $fn"
757 p = os.path.join(dirpath, fn)
758 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000759 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
760 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000761
762 # We added some directories to the search path during the configure
763 # phase. Remove those because those directories won't be there on
764 # the end-users system.
765 path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
766 'Versions', version, 'lib', 'python%s'%(version,),
767 'config', 'Makefile')
768 fp = open(path, 'r')
769 data = fp.read()
770 fp.close()
771
772 data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
773 data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
774 fp = open(path, 'w')
775 fp.write(data)
776 fp.close()
777
778 # Add symlinks in /usr/local/bin, using relative links
779 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
780 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
781 'Python.framework', 'Versions', version, 'bin')
782 if os.path.exists(usr_local_bin):
783 shutil.rmtree(usr_local_bin)
784 os.makedirs(usr_local_bin)
785 for fn in os.listdir(
786 os.path.join(frmDir, 'Versions', version, 'bin')):
787 os.symlink(os.path.join(to_framework, fn),
788 os.path.join(usr_local_bin, fn))
789
790 os.chdir(curdir)
791
792
793
794def patchFile(inPath, outPath):
795 data = fileContents(inPath)
796 data = data.replace('$FULL_VERSION', getFullVersion())
797 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +0000798 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000799 data = data.replace('$ARCHITECTURES', "i386, ppc")
800 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +0000801
802 # This one is not handy as a template variable
803 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000804 fp = open(outPath, 'wb')
805 fp.write(data)
806 fp.close()
807
808def patchScript(inPath, outPath):
809 data = fileContents(inPath)
810 data = data.replace('@PYVER@', getVersion())
811 fp = open(outPath, 'wb')
812 fp.write(data)
813 fp.close()
814 os.chmod(outPath, 0755)
815
816
817
818def packageFromRecipe(targetDir, recipe):
819 curdir = os.getcwd()
820 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +0000821 # The major version (such as 2.5) is included in the package name
822 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +0000823 # common.
824 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000825 srcdir = recipe.get('source')
826 pkgroot = recipe.get('topdir', srcdir)
827 postflight = recipe.get('postflight')
828 readme = textwrap.dedent(recipe['readme'])
829 isRequired = recipe.get('required', True)
830
831 print "- building package %s"%(pkgname,)
832
833 # Substitute some variables
834 textvars = dict(
835 VER=getVersion(),
836 FULLVER=getFullVersion(),
837 )
838 readme = readme % textvars
839
840 if pkgroot is not None:
841 pkgroot = pkgroot % textvars
842 else:
843 pkgroot = '/'
844
845 if srcdir is not None:
846 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
847 srcdir = srcdir % textvars
848
849 if postflight is not None:
850 postflight = os.path.abspath(postflight)
851
852 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
853 os.makedirs(packageContents)
854
855 if srcdir is not None:
856 os.chdir(srcdir)
857 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
858 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
859 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
860
861 fn = os.path.join(packageContents, 'PkgInfo')
862 fp = open(fn, 'w')
863 fp.write('pmkrpkg1')
864 fp.close()
865
866 rsrcDir = os.path.join(packageContents, "Resources")
867 os.mkdir(rsrcDir)
868 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
869 fp.write(readme)
870 fp.close()
871
872 if postflight is not None:
873 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
874
875 vers = getFullVersion()
876 major, minor = map(int, getVersion().split('.', 2))
877 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000878 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
879 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
880 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000881 CFBundleShortVersionString=vers,
882 IFMajorVersion=major,
883 IFMinorVersion=minor,
884 IFPkgFormatVersion=0.10000000149011612,
885 IFPkgFlagAllowBackRev=False,
886 IFPkgFlagAuthorizationAction="RootAuthorization",
887 IFPkgFlagDefaultLocation=pkgroot,
888 IFPkgFlagFollowLinks=True,
889 IFPkgFlagInstallFat=True,
890 IFPkgFlagIsRequired=isRequired,
891 IFPkgFlagOverwritePermissions=False,
892 IFPkgFlagRelocatable=False,
893 IFPkgFlagRestartAction="NoRestart",
894 IFPkgFlagRootVolumeOnly=True,
895 IFPkgFlagUpdateInstalledLangauges=False,
896 )
897 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
898
899 pl = Plist(
900 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000901 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000902 IFPkgDescriptionVersion=vers,
903 )
904 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
905
906 finally:
907 os.chdir(curdir)
908
909
910def makeMpkgPlist(path):
911
912 vers = getFullVersion()
913 major, minor = map(int, getVersion().split('.', 2))
914
915 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000916 CFBundleGetInfoString="Python %s"%(vers,),
917 CFBundleIdentifier='org.python.Python',
918 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000919 CFBundleShortVersionString=vers,
920 IFMajorVersion=major,
921 IFMinorVersion=minor,
922 IFPkgFlagComponentDirectory="Contents/Packages",
923 IFPkgFlagPackageList=[
924 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +0000925 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000926 IFPkgFlagPackageSelection='selected'
927 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000928 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000929 ],
930 IFPkgFormatVersion=0.10000000149011612,
931 IFPkgFlagBackgroundScaling="proportional",
932 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +0000933 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000934 )
935
936 writePlist(pl, path)
937
938
939def buildInstaller():
940
941 # Zap all compiled files
942 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
943 for fn in filenames:
944 if fn.endswith('.pyc') or fn.endswith('.pyo'):
945 os.unlink(os.path.join(dirpath, fn))
946
947 outdir = os.path.join(WORKDIR, 'installer')
948 if os.path.exists(outdir):
949 shutil.rmtree(outdir)
950 os.mkdir(outdir)
951
Ronald Oussoren508282e2009-03-30 19:34:51 +0000952 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000953 pkgcontents = os.path.join(pkgroot, 'Packages')
954 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000955 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000956 packageFromRecipe(pkgcontents, recipe)
957
958 rsrcDir = os.path.join(pkgroot, 'Resources')
959
960 fn = os.path.join(pkgroot, 'PkgInfo')
961 fp = open(fn, 'w')
962 fp.write('pmkrpkg1')
963 fp.close()
964
965 os.mkdir(rsrcDir)
966
967 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
968 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000969 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000970 IFPkgDescriptionVersion=getVersion(),
971 )
972
973 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
974 for fn in os.listdir('resources'):
975 if fn == '.svn': continue
976 if fn.endswith('.jpg'):
977 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
978 else:
979 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
980
Ronald Oussorenc5555542006-06-11 20:24:45 +0000981 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000982
983
984def installSize(clear=False, _saved=[]):
985 if clear:
986 del _saved[:]
987 if not _saved:
988 data = captureCommand("du -ks %s"%(
989 shellQuote(os.path.join(WORKDIR, '_root'))))
990 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
991 return _saved[0]
992
993
994def buildDMG():
995 """
Ronald Oussorenaa560962006-11-07 15:53:38 +0000996 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000997 """
998 outdir = os.path.join(WORKDIR, 'diskimage')
999 if os.path.exists(outdir):
1000 shutil.rmtree(outdir)
1001
1002 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001003 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001004 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001005 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001006 imagepath = imagepath + '.dmg'
1007
1008 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001009 volname='Python %s'%(getFullVersion())
1010 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1011 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001012 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001013 shellQuote(imagepath + ".tmp.dmg" )))
1014
1015
1016 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1017 os.mkdir(os.path.join(WORKDIR, "mnt"))
1018 runCommand("hdiutil attach %s -mountroot %s"%(
1019 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1020
1021 # Custom icon for the DMG, shown when the DMG is mounted.
1022 shutil.copy("../Icons/Disk Image.icns",
1023 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
1024 runCommand("/Developer/Tools/SetFile -a C %s/"%(
1025 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1026
1027 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1028
1029 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1030 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1031 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1032 setIcon(imagepath, "../Icons/Disk Image.icns")
1033
1034 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001035
1036 return imagepath
1037
1038
1039def setIcon(filePath, icnsPath):
1040 """
1041 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001042 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001043
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001044 toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
1045 dirPath = os.path.dirname(__file__)
1046 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1047 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1048 # to connections to the window server.
1049 if not os.path.exists('seticon.app/Contents/MacOS'):
1050 os.makedirs('seticon.app/Contents/MacOS')
1051 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1052 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001053
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001054 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1055 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001056
1057def main():
1058 # First parse options and check if we can perform our work
1059 parseOptions()
1060 checkEnvironment()
1061
Ronald Oussoren508282e2009-03-30 19:34:51 +00001062 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001063 os.environ['CC'] = CC
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001064
1065 if os.path.exists(WORKDIR):
1066 shutil.rmtree(WORKDIR)
1067 os.mkdir(WORKDIR)
1068
1069 # Then build third-party libraries such as sleepycat DB4.
1070 buildLibraries()
1071
1072 # Now build python itself
1073 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001074
1075 # And then build the documentation
1076 # Remove the Deployment Target from the shell
1077 # environment, it's no longer needed and
1078 # an unexpected build target can cause problems
1079 # when Sphinx and its dependencies need to
1080 # be (re-)installed.
1081 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001082 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001083
1084
1085 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001086 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001087 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001088 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001089
Benjamin Petersonc3104762008-10-03 11:52:06 +00001090 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001091 getVersion(),))
1092 os.chmod(folder, 0755)
1093 setIcon(folder, "../Icons/Python Folder.icns")
1094
1095 # Create the installer
1096 buildInstaller()
1097
1098 # And copy the readme into the directory containing the installer
1099 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1100
1101 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001102 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001103
1104 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
1105 print >> fp, "# BUILD INFO"
1106 print >> fp, "# Date:", time.ctime()
1107 print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
1108 fp.close()
1109
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001110 # And copy it to a DMG
1111 buildDMG()
1112
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001113if __name__ == "__main__":
1114 main()