blob: 38f20f41226e5c1346a7b5ded3c271f5e5f80405 [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 Oussorenc66ced32009-09-20 20:16:11 +000064if int(os.uname()[2].split('.')[0]) == 8:
65 # Explicitly use the 10.4u (universal) SDK when
66 # building on 10.4, the system headers are not
67 # useable for a universal build
68 SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
69else:
70 SDKPATH = "/"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000071
Ronald Oussoren508282e2009-03-30 19:34:51 +000072universal_opts_map = { '32-bit': ('i386', 'ppc',),
73 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +000074 'intel': ('i386', 'x86_64'),
75 '3-way': ('ppc', 'i386', 'x86_64'),
76 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
77default_target_map = {
78 '64-bit': '10.5',
79 '3-way': '10.5',
80 'intel': '10.5',
81 'all': '10.5',
82}
Ronald Oussoren508282e2009-03-30 19:34:51 +000083
84UNIVERSALOPTS = tuple(universal_opts_map.keys())
85
86UNIVERSALARCHS = '32-bit'
87
88ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +000089
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000090# Source directory (asume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +000091SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000092 os.path.dirname(
93 os.path.dirname(
94 os.path.abspath(__file__
95 ))))
96
Ronald Oussoren508282e2009-03-30 19:34:51 +000097# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
98DEPTARGET = '10.3'
99
Ronald Oussoren158ad592006-11-07 16:00:34 +0000100USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000101 Usage: build_python [options]
102
103 Options:
104 -? or -h: Show this message
105 -b DIR
106 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
107 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
108 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
109 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000110 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
111 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000112""")% globals()
113
114
115# Instructions for building libraries that are necessary for building a
116# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000117# [The recipes are defined here for convenience but instantiated later after
118# command line options have been processed.]
119def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000120 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000121
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000122 if DEPTARGET < '10.5':
123 result.extend([
124 dict(
125 name="Bzip2 1.0.5",
126 url="http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz",
127 checksum='3c15a0c8d1d3ee1c46a1634d00617b1a',
128 configure=None,
129 install='make install PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
130 shellQuote(os.path.join(WORKDIR, 'libraries')),
131 ' -arch '.join(ARCHLIST),
132 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000133 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000134 ),
135 dict(
136 name="ZLib 1.2.3",
137 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
138 checksum='debc62758716a169df9f62e6ab2bc634',
139 configure=None,
140 install='make install prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
141 shellQuote(os.path.join(WORKDIR, 'libraries')),
142 ' -arch '.join(ARCHLIST),
143 SDKPATH,
144 ),
145 ),
146 dict(
147 # Note that GNU readline is GPL'd software
148 name="GNU Readline 5.1.4",
149 url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" ,
150 checksum='7ee5a692db88b30ca48927a13fd60e46',
151 patchlevel='0',
152 patches=[
153 # The readline maintainers don't do actual micro releases, but
154 # just ship a set of patches.
155 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-001',
156 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002',
157 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003',
158 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004',
159 ]
160 ),
161 dict(
162 name="SQLite 3.6.11",
163 url="http://www.sqlite.org/sqlite-3.6.11.tar.gz",
164 checksum='7ebb099696ab76cc6ff65dd496d17858',
165 configure_pre=[
166 '--enable-threadsafe',
167 '--enable-tempstore',
168 '--enable-shared=no',
169 '--enable-static=yes',
170 '--disable-tcl',
171 ]
172 ),
173 dict(
174 name="NCurses 5.5",
175 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
176 checksum='e73c1ac10b4bfc46db43b2ddfd6244ef',
177 configure_pre=[
178 "--without-cxx",
179 "--without-ada",
180 "--without-progs",
181 "--without-curses-h",
182 "--enable-shared",
183 "--with-shared",
184 "--datadir=/usr/share",
185 "--sysconfdir=/etc",
186 "--sharedstatedir=/usr/com",
187 "--with-terminfo-dirs=/usr/share/terminfo",
188 "--with-default-terminfo-dir=/usr/share/terminfo",
189 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
190 "--enable-termcap",
191 ],
192 patches=[
193 "ncurses-5.5.patch",
194 ],
195 useLDFlags=False,
196 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
197 shellQuote(os.path.join(WORKDIR, 'libraries')),
198 shellQuote(os.path.join(WORKDIR, 'libraries')),
199 getVersion(),
200 ),
201 ),
202 ])
203
204 result.extend([
Ronald Oussoren508282e2009-03-30 19:34:51 +0000205 dict(
206 name="Sleepycat DB 4.7.25",
207 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
208 checksum='ec2b87e833779681a0c3a814aa71359e',
209 buildDir="build_unix",
210 configure="../dist/configure",
211 configure_pre=[
212 '--includedir=/usr/local/include/db4',
213 ]
214 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000215 ])
216
217 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000218
219
220# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000221def pkg_recipes():
222 result = [
223 dict(
224 name="PythonFramework",
225 long_name="Python Framework",
226 source="/Library/Frameworks/Python.framework",
227 readme="""\
228 This package installs Python.framework, that is the python
229 interpreter and the standard library. This also includes Python
230 wrappers for lots of Mac OS X API's.
231 """,
232 postflight="scripts/postflight.framework",
233 ),
234 dict(
235 name="PythonApplications",
236 long_name="GUI Applications",
237 source="/Applications/Python %(VER)s",
238 readme="""\
239 This package installs IDLE (an interactive Python IDE),
240 Python Launcher and Build Applet (create application bundles
241 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000242
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000243 It also installs a number of examples and demos.
244 """,
245 required=False,
246 ),
247 dict(
248 name="PythonUnixTools",
249 long_name="UNIX command-line tools",
250 source="/usr/local/bin",
251 readme="""\
252 This package installs the unix tools in /usr/local/bin for
253 compatibility with older releases of Python. This package
254 is not necessary to use Python.
255 """,
256 required=False,
257 ),
258 dict(
259 name="PythonDocumentation",
260 long_name="Python Documentation",
261 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
262 source="/pydocs",
263 readme="""\
264 This package installs the python documentation at a location
265 that is useable for pydoc and IDLE. If you have installed Xcode
266 it will also install a link to the documentation in
267 /Developer/Documentation/Python
268 """,
269 postflight="scripts/postflight.documentation",
270 required=False,
271 ),
272 dict(
273 name="PythonProfileChanges",
274 long_name="Shell profile updater",
275 readme="""\
276 This packages updates your shell profile to make sure that
277 the Python tools are found by your shell in preference of
278 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000279
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000280 If you don't install this package you'll have to add
281 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
282 to your PATH by hand.
283 """,
284 postflight="scripts/postflight.patch-profile",
285 topdir="/Library/Frameworks/Python.framework",
286 source="/empty-dir",
287 required=False,
288 ),
289 ]
290
291 if DEPTARGET < '10.4':
292 result.append(
293 dict(
294 name="PythonSystemFixes",
295 long_name="Fix system Python",
296 readme="""\
297 This package updates the system python installation on
298 Mac OS X 10.3 to ensure that you can build new python extensions
299 using that copy of python after installing this version.
300 """,
301 postflight="../Tools/fixapplepython23.py",
302 topdir="/Library/Frameworks/Python.framework",
303 source="/empty-dir",
304 required=False,
305 )
306 )
307 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000308
309def fatal(msg):
310 """
311 A fatal error, bail out.
312 """
313 sys.stderr.write('FATAL: ')
314 sys.stderr.write(msg)
315 sys.stderr.write('\n')
316 sys.exit(1)
317
318def fileContents(fn):
319 """
320 Return the contents of the named file
321 """
322 return open(fn, 'rb').read()
323
324def runCommand(commandline):
325 """
326 Run a command and raise RuntimeError if it fails. Output is surpressed
327 unless the command fails.
328 """
329 fd = os.popen(commandline, 'r')
330 data = fd.read()
331 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000332 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000333 sys.stdout.write(data)
334 raise RuntimeError, "command failed: %s"%(commandline,)
335
336 if VERBOSE:
337 sys.stdout.write(data); sys.stdout.flush()
338
339def captureCommand(commandline):
340 fd = os.popen(commandline, 'r')
341 data = fd.read()
342 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000343 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000344 sys.stdout.write(data)
345 raise RuntimeError, "command failed: %s"%(commandline,)
346
347 return data
348
349def checkEnvironment():
350 """
351 Check that we're running on a supported system.
352 """
353
354 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000355 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000356
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000357 if int(platform.release().split('.')[0]) <= 8:
358 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000359
360 if not os.path.exists(SDKPATH):
361 fatal("Please install the latest version of Xcode and the %s SDK"%(
362 os.path.basename(SDKPATH[:-4])))
363
364
365
Ronald Oussoren158ad592006-11-07 16:00:34 +0000366def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000367 """
368 Parse arguments and update global settings.
369 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000370 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
371 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000372
373 if args is None:
374 args = sys.argv[1:]
375
376 try:
377 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000378 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
379 'dep-target=', 'universal-archs=', 'help' ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000380 except getopt.error, msg:
381 print msg
382 sys.exit(1)
383
384 if args:
385 print "Additional arguments"
386 sys.exit(1)
387
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000388 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000389 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000390 if k in ('-h', '-?', '--help'):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000391 print USAGE
392 sys.exit(0)
393
394 elif k in ('-d', '--build-dir'):
395 WORKDIR=v
396
397 elif k in ('--third-party',):
398 DEPSRC=v
399
400 elif k in ('--sdk-path',):
401 SDKPATH=v
402
403 elif k in ('--src-dir',):
404 SRCDIR=v
405
Ronald Oussoren508282e2009-03-30 19:34:51 +0000406 elif k in ('--dep-target', ):
407 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000408 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000409
410 elif k in ('--universal-archs', ):
411 if v in UNIVERSALOPTS:
412 UNIVERSALARCHS = v
413 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000414 if deptarget is None:
415 # Select alternate default deployment
416 # target
417 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000418 else:
419 raise NotImplementedError, v
420
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000421 else:
422 raise NotImplementedError, k
423
424 SRCDIR=os.path.abspath(SRCDIR)
425 WORKDIR=os.path.abspath(WORKDIR)
426 SDKPATH=os.path.abspath(SDKPATH)
427 DEPSRC=os.path.abspath(DEPSRC)
428
429 print "Settings:"
430 print " * Source directory:", SRCDIR
431 print " * Build directory: ", WORKDIR
432 print " * SDK location: ", SDKPATH
Ronald Oussoren508282e2009-03-30 19:34:51 +0000433 print " * Third-party source:", DEPSRC
434 print " * Deployment target:", DEPTARGET
435 print " * Universal architectures:", ARCHLIST
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000436 print ""
437
438
439
440
441def extractArchive(builddir, archiveName):
442 """
443 Extract a source archive into 'builddir'. Returns the path of the
444 extracted archive.
445
446 XXX: This function assumes that archives contain a toplevel directory
447 that is has the same name as the basename of the archive. This is
448 save enough for anything we use.
449 """
450 curdir = os.getcwd()
451 try:
452 os.chdir(builddir)
453 if archiveName.endswith('.tar.gz'):
454 retval = os.path.basename(archiveName[:-7])
455 if os.path.exists(retval):
456 shutil.rmtree(retval)
457 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
458
459 elif archiveName.endswith('.tar.bz2'):
460 retval = os.path.basename(archiveName[:-8])
461 if os.path.exists(retval):
462 shutil.rmtree(retval)
463 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
464
465 elif archiveName.endswith('.tar'):
466 retval = os.path.basename(archiveName[:-4])
467 if os.path.exists(retval):
468 shutil.rmtree(retval)
469 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
470
471 elif archiveName.endswith('.zip'):
472 retval = os.path.basename(archiveName[:-4])
473 if os.path.exists(retval):
474 shutil.rmtree(retval)
475 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
476
477 data = fp.read()
478 xit = fp.close()
479 if xit is not None:
480 sys.stdout.write(data)
481 raise RuntimeError, "Cannot extract %s"%(archiveName,)
482
483 return os.path.join(builddir, retval)
484
485 finally:
486 os.chdir(curdir)
487
488KNOWNSIZES = {
489 "http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz": 7952742,
490 "http://downloads.sleepycat.com/db-4.4.20.tar.gz": 2030276,
491}
492
493def downloadURL(url, fname):
494 """
495 Download the contents of the url into the file.
496 """
497 try:
498 size = os.path.getsize(fname)
499 except OSError:
500 pass
501 else:
502 if KNOWNSIZES.get(url) == size:
503 print "Using existing file for", url
504 return
505 fpIn = urllib2.urlopen(url)
506 fpOut = open(fname, 'wb')
507 block = fpIn.read(10240)
508 try:
509 while block:
510 fpOut.write(block)
511 block = fpIn.read(10240)
512 fpIn.close()
513 fpOut.close()
514 except:
515 try:
516 os.unlink(fname)
517 except:
518 pass
519
520def buildRecipe(recipe, basedir, archList):
521 """
522 Build software using a recipe. This function does the
523 'configure;make;make install' dance for C software, with a possibility
524 to customize this process, basically a poor-mans DarwinPorts.
525 """
526 curdir = os.getcwd()
527
528 name = recipe['name']
529 url = recipe['url']
530 configure = recipe.get('configure', './configure')
531 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
532 shellQuote(basedir)))
533
534 archiveName = os.path.split(url)[-1]
535 sourceArchive = os.path.join(DEPSRC, archiveName)
536
537 if not os.path.exists(DEPSRC):
538 os.mkdir(DEPSRC)
539
540
541 if os.path.exists(sourceArchive):
542 print "Using local copy of %s"%(name,)
543
544 else:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000545 print "Did not find local copy of %s"%(name,)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000546 print "Downloading %s"%(name,)
547 downloadURL(url, sourceArchive)
548 print "Archive for %s stored as %s"%(name, sourceArchive)
549
550 print "Extracting archive for %s"%(name,)
551 buildDir=os.path.join(WORKDIR, '_bld')
552 if not os.path.exists(buildDir):
553 os.mkdir(buildDir)
554
555 workDir = extractArchive(buildDir, sourceArchive)
556 os.chdir(workDir)
557 if 'buildDir' in recipe:
558 os.chdir(recipe['buildDir'])
559
560
561 for fn in recipe.get('patches', ()):
562 if fn.startswith('http://'):
563 # Download the patch before applying it.
564 path = os.path.join(DEPSRC, os.path.basename(fn))
565 downloadURL(fn, path)
566 fn = path
567
568 fn = os.path.join(curdir, fn)
569 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
570 shellQuote(fn),))
571
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000572 if configure is not None:
573 configure_args = [
574 "--prefix=/usr/local",
575 "--enable-static",
576 "--disable-shared",
577 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
578 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000579
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000580 if 'configure_pre' in recipe:
581 args = list(recipe['configure_pre'])
582 if '--disable-static' in args:
583 configure_args.remove('--enable-static')
584 if '--enable-shared' in args:
585 configure_args.remove('--disable-shared')
586 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000587
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000588 if recipe.get('useLDFlags', 1):
589 configure_args.extend([
590 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
591 ' -arch '.join(archList),
592 shellQuote(SDKPATH)[1:-1],
593 shellQuote(basedir)[1:-1],),
594 "LDFLAGS=-syslibroot,%s -L%s/usr/local/lib -arch %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000595 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000596 shellQuote(basedir)[1:-1],
597 ' -arch '.join(archList)),
598 ])
599 else:
600 configure_args.extend([
601 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
602 ' -arch '.join(archList),
603 shellQuote(SDKPATH)[1:-1],
604 shellQuote(basedir)[1:-1],),
605 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000606
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000607 if 'configure_post' in recipe:
608 configure_args = configure_args = list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000609
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000610 configure_args.insert(0, configure)
611 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000612
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000613 print "Running configure for %s"%(name,)
614 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000615
616 print "Running install for %s"%(name,)
617 runCommand('{ ' + install + ' ;} 2>&1')
618
619 print "Done %s"%(name,)
620 print ""
621
622 os.chdir(curdir)
623
624def buildLibraries():
625 """
626 Build our dependencies into $WORKDIR/libraries/usr/local
627 """
628 print ""
629 print "Building required libraries"
630 print ""
631 universal = os.path.join(WORKDIR, 'libraries')
632 os.mkdir(universal)
633 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
634 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
635
Ronald Oussoren508282e2009-03-30 19:34:51 +0000636 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000637 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000638
639
640
641def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000642 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000643 # inside the framwork. pydoc and IDLE will pick it up there.
644 print "Install python documentation"
645 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000646 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000647 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000648 curDir = os.getcwd()
649 os.chdir(buildDir)
650 runCommand('make update')
651 runCommand('make html')
652 os.chdir(curDir)
653 if not os.path.exists(docdir):
654 os.mkdir(docdir)
655 os.rename(os.path.join(buildDir, 'build', 'html'),
656 os.path.join(docdir, 'python-docs-html'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000657
658
659def buildPython():
Ronald Oussoren508282e2009-03-30 19:34:51 +0000660 print "Building a universal python for %s architectures" % UNIVERSALARCHS
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000661
662 buildDir = os.path.join(WORKDIR, '_bld', 'python')
663 rootDir = os.path.join(WORKDIR, '_root')
664
665 if os.path.exists(buildDir):
666 shutil.rmtree(buildDir)
667 if os.path.exists(rootDir):
668 shutil.rmtree(rootDir)
669 os.mkdir(buildDir)
670 os.mkdir(rootDir)
671 os.mkdir(os.path.join(rootDir, 'empty-dir'))
672 curdir = os.getcwd()
673 os.chdir(buildDir)
674
675 # Not sure if this is still needed, the original build script
676 # claims that parts of the install assume python.exe exists.
677 os.symlink('python', os.path.join(buildDir, 'python.exe'))
678
679 # Extract the version from the configure file, needed to calculate
680 # several paths.
681 version = getVersion()
682
Ronald Oussoren008af852009-03-30 20:02:08 +0000683 # Since the extra libs are not in their installed framework location
684 # during the build, augment the library path so that the interpreter
685 # will find them during its extension import sanity checks.
686 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
687 'libraries', 'usr', 'local', 'lib')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000688 print "Running configure..."
Ronald Oussoren508282e2009-03-30 19:34:51 +0000689 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
690 "--with-universal-archs=%s "
691 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
692 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
693 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
694 UNIVERSALARCHS,
695 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000696 shellQuote(WORKDIR)[1:-1]))
697
698 print "Running make"
699 runCommand("make")
700
Ronald Oussorenaa560962006-11-07 15:53:38 +0000701 print "Running make frameworkinstall"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000702 runCommand("make frameworkinstall DESTDIR=%s"%(
703 shellQuote(rootDir)))
704
Ronald Oussorenaa560962006-11-07 15:53:38 +0000705 print "Running make frameworkinstallextras"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000706 runCommand("make frameworkinstallextras DESTDIR=%s"%(
707 shellQuote(rootDir)))
708
Ronald Oussoren008af852009-03-30 20:02:08 +0000709 del os.environ['DYLD_LIBRARY_PATH']
Ronald Oussorenaa560962006-11-07 15:53:38 +0000710 print "Copying required shared libraries"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000711 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
712 runCommand("mv %s/* %s"%(
713 shellQuote(os.path.join(
714 WORKDIR, 'libraries', 'Library', 'Frameworks',
715 'Python.framework', 'Versions', getVersion(),
716 'lib')),
717 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
718 'Python.framework', 'Versions', getVersion(),
719 'lib'))))
720
721 print "Fix file modes"
722 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000723 gid = grp.getgrnam('admin').gr_gid
724
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000725 for dirpath, dirnames, filenames in os.walk(frmDir):
726 for dn in dirnames:
727 os.chmod(os.path.join(dirpath, dn), 0775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000728 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +0000729
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000730
731 for fn in filenames:
732 if os.path.islink(fn):
733 continue
734
735 # "chmod g+w $fn"
736 p = os.path.join(dirpath, fn)
737 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +0000738 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
739 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000740
741 # We added some directories to the search path during the configure
742 # phase. Remove those because those directories won't be there on
743 # the end-users system.
744 path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
745 'Versions', version, 'lib', 'python%s'%(version,),
746 'config', 'Makefile')
747 fp = open(path, 'r')
748 data = fp.read()
749 fp.close()
750
751 data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
752 data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
753 fp = open(path, 'w')
754 fp.write(data)
755 fp.close()
756
757 # Add symlinks in /usr/local/bin, using relative links
758 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
759 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
760 'Python.framework', 'Versions', version, 'bin')
761 if os.path.exists(usr_local_bin):
762 shutil.rmtree(usr_local_bin)
763 os.makedirs(usr_local_bin)
764 for fn in os.listdir(
765 os.path.join(frmDir, 'Versions', version, 'bin')):
766 os.symlink(os.path.join(to_framework, fn),
767 os.path.join(usr_local_bin, fn))
768
769 os.chdir(curdir)
770
771
772
773def patchFile(inPath, outPath):
774 data = fileContents(inPath)
775 data = data.replace('$FULL_VERSION', getFullVersion())
776 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +0000777 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000778 data = data.replace('$ARCHITECTURES', "i386, ppc")
779 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +0000780
781 # This one is not handy as a template variable
782 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000783 fp = open(outPath, 'wb')
784 fp.write(data)
785 fp.close()
786
787def patchScript(inPath, outPath):
788 data = fileContents(inPath)
789 data = data.replace('@PYVER@', getVersion())
790 fp = open(outPath, 'wb')
791 fp.write(data)
792 fp.close()
793 os.chmod(outPath, 0755)
794
795
796
797def packageFromRecipe(targetDir, recipe):
798 curdir = os.getcwd()
799 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +0000800 # The major version (such as 2.5) is included in the package name
801 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +0000802 # common.
803 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000804 srcdir = recipe.get('source')
805 pkgroot = recipe.get('topdir', srcdir)
806 postflight = recipe.get('postflight')
807 readme = textwrap.dedent(recipe['readme'])
808 isRequired = recipe.get('required', True)
809
810 print "- building package %s"%(pkgname,)
811
812 # Substitute some variables
813 textvars = dict(
814 VER=getVersion(),
815 FULLVER=getFullVersion(),
816 )
817 readme = readme % textvars
818
819 if pkgroot is not None:
820 pkgroot = pkgroot % textvars
821 else:
822 pkgroot = '/'
823
824 if srcdir is not None:
825 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
826 srcdir = srcdir % textvars
827
828 if postflight is not None:
829 postflight = os.path.abspath(postflight)
830
831 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
832 os.makedirs(packageContents)
833
834 if srcdir is not None:
835 os.chdir(srcdir)
836 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
837 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
838 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
839
840 fn = os.path.join(packageContents, 'PkgInfo')
841 fp = open(fn, 'w')
842 fp.write('pmkrpkg1')
843 fp.close()
844
845 rsrcDir = os.path.join(packageContents, "Resources")
846 os.mkdir(rsrcDir)
847 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
848 fp.write(readme)
849 fp.close()
850
851 if postflight is not None:
852 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
853
854 vers = getFullVersion()
855 major, minor = map(int, getVersion().split('.', 2))
856 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000857 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
858 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
859 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000860 CFBundleShortVersionString=vers,
861 IFMajorVersion=major,
862 IFMinorVersion=minor,
863 IFPkgFormatVersion=0.10000000149011612,
864 IFPkgFlagAllowBackRev=False,
865 IFPkgFlagAuthorizationAction="RootAuthorization",
866 IFPkgFlagDefaultLocation=pkgroot,
867 IFPkgFlagFollowLinks=True,
868 IFPkgFlagInstallFat=True,
869 IFPkgFlagIsRequired=isRequired,
870 IFPkgFlagOverwritePermissions=False,
871 IFPkgFlagRelocatable=False,
872 IFPkgFlagRestartAction="NoRestart",
873 IFPkgFlagRootVolumeOnly=True,
874 IFPkgFlagUpdateInstalledLangauges=False,
875 )
876 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
877
878 pl = Plist(
879 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000880 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000881 IFPkgDescriptionVersion=vers,
882 )
883 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
884
885 finally:
886 os.chdir(curdir)
887
888
889def makeMpkgPlist(path):
890
891 vers = getFullVersion()
892 major, minor = map(int, getVersion().split('.', 2))
893
894 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000895 CFBundleGetInfoString="Python %s"%(vers,),
896 CFBundleIdentifier='org.python.Python',
897 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000898 CFBundleShortVersionString=vers,
899 IFMajorVersion=major,
900 IFMinorVersion=minor,
901 IFPkgFlagComponentDirectory="Contents/Packages",
902 IFPkgFlagPackageList=[
903 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +0000904 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000905 IFPkgFlagPackageSelection='selected'
906 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000907 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000908 ],
909 IFPkgFormatVersion=0.10000000149011612,
910 IFPkgFlagBackgroundScaling="proportional",
911 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +0000912 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000913 )
914
915 writePlist(pl, path)
916
917
918def buildInstaller():
919
920 # Zap all compiled files
921 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
922 for fn in filenames:
923 if fn.endswith('.pyc') or fn.endswith('.pyo'):
924 os.unlink(os.path.join(dirpath, fn))
925
926 outdir = os.path.join(WORKDIR, 'installer')
927 if os.path.exists(outdir):
928 shutil.rmtree(outdir)
929 os.mkdir(outdir)
930
Ronald Oussoren508282e2009-03-30 19:34:51 +0000931 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000932 pkgcontents = os.path.join(pkgroot, 'Packages')
933 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000934 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000935 packageFromRecipe(pkgcontents, recipe)
936
937 rsrcDir = os.path.join(pkgroot, 'Resources')
938
939 fn = os.path.join(pkgroot, 'PkgInfo')
940 fp = open(fn, 'w')
941 fp.write('pmkrpkg1')
942 fp.close()
943
944 os.mkdir(rsrcDir)
945
946 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
947 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000948 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000949 IFPkgDescriptionVersion=getVersion(),
950 )
951
952 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
953 for fn in os.listdir('resources'):
954 if fn == '.svn': continue
955 if fn.endswith('.jpg'):
956 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
957 else:
958 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
959
Ronald Oussorenc5555542006-06-11 20:24:45 +0000960 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000961
962
963def installSize(clear=False, _saved=[]):
964 if clear:
965 del _saved[:]
966 if not _saved:
967 data = captureCommand("du -ks %s"%(
968 shellQuote(os.path.join(WORKDIR, '_root'))))
969 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
970 return _saved[0]
971
972
973def buildDMG():
974 """
Ronald Oussorenaa560962006-11-07 15:53:38 +0000975 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000976 """
977 outdir = os.path.join(WORKDIR, 'diskimage')
978 if os.path.exists(outdir):
979 shutil.rmtree(outdir)
980
981 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000982 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000983 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000984 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000985 imagepath = imagepath + '.dmg'
986
987 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000988 volname='Python %s'%(getFullVersion())
989 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
990 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000991 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +0000992 shellQuote(imagepath + ".tmp.dmg" )))
993
994
995 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
996 os.mkdir(os.path.join(WORKDIR, "mnt"))
997 runCommand("hdiutil attach %s -mountroot %s"%(
998 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
999
1000 # Custom icon for the DMG, shown when the DMG is mounted.
1001 shutil.copy("../Icons/Disk Image.icns",
1002 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
1003 runCommand("/Developer/Tools/SetFile -a C %s/"%(
1004 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1005
1006 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1007
1008 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1009 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1010 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1011 setIcon(imagepath, "../Icons/Disk Image.icns")
1012
1013 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001014
1015 return imagepath
1016
1017
1018def setIcon(filePath, icnsPath):
1019 """
1020 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001021 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001022
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001023 toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
1024 dirPath = os.path.dirname(__file__)
1025 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1026 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1027 # to connections to the window server.
1028 if not os.path.exists('seticon.app/Contents/MacOS'):
1029 os.makedirs('seticon.app/Contents/MacOS')
1030 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1031 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001032
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001033 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1034 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001035
1036def main():
1037 # First parse options and check if we can perform our work
1038 parseOptions()
1039 checkEnvironment()
1040
Ronald Oussoren508282e2009-03-30 19:34:51 +00001041 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001042
1043 if os.path.exists(WORKDIR):
1044 shutil.rmtree(WORKDIR)
1045 os.mkdir(WORKDIR)
1046
1047 # Then build third-party libraries such as sleepycat DB4.
1048 buildLibraries()
1049
1050 # Now build python itself
1051 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001052
1053 # And then build the documentation
1054 # Remove the Deployment Target from the shell
1055 # environment, it's no longer needed and
1056 # an unexpected build target can cause problems
1057 # when Sphinx and its dependencies need to
1058 # be (re-)installed.
1059 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001060 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001061
1062
1063 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001064 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001065 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001066 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001067
Benjamin Petersonc3104762008-10-03 11:52:06 +00001068 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001069 getVersion(),))
1070 os.chmod(folder, 0755)
1071 setIcon(folder, "../Icons/Python Folder.icns")
1072
1073 # Create the installer
1074 buildInstaller()
1075
1076 # And copy the readme into the directory containing the installer
1077 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1078
1079 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001080 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001081
1082 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
1083 print >> fp, "# BUILD INFO"
1084 print >> fp, "# Date:", time.ctime()
1085 print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
1086 fp.close()
1087
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001088 # And copy it to a DMG
1089 buildDMG()
1090
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001091if __name__ == "__main__":
1092 main()