blob: 763eaf652736a48fd44d27e80a2a0011c7660595 [file] [log] [blame]
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001#!/usr/bin/python
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +00005work. 64-bit or four-way universal builds require at least OS X 10.5 and
6the 10.5 SDK.
Thomas Wouters477c8d52006-05-27 19:21:47 +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"""
Benjamin Peterson623918e2008-12-20 22:50:25 +000013import platform, os, sys, getopt, textwrap, shutil, urllib2, stat, time, pwd
Thomas Wouters89f507f2006-12-13 04:49:30 +000014import grp
Thomas Wouters477c8d52006-05-27 19:21:47 +000015
Thomas Wouters89f507f2006-12-13 04:49:30 +000016INCLUDE_TIMESTAMP = 1
17VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000018
19from plistlib import Plist
20
21import MacOS
Thomas Wouters477c8d52006-05-27 19:21:47 +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 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000032 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Benjamin Peterson623918e2008-12-20 22:50:25 +000053 raise RuntimeError, "Cannot find full version??"
Thomas Wouters477c8d52006-05-27 19:21:47 +000054
Thomas Wouters89f507f2006-12-13 04:49:30 +000055# The directory we'll use to create the build (will be erased and recreated)
56WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +000057
Thomas Wouters89f507f2006-12-13 04:49:30 +000058# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +000059# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +000060DEPSRC = os.path.join(WORKDIR, 'third-party')
61DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +000062
63# Location of the preferred SDK
Thomas Wouters89f507f2006-12-13 04:49:30 +000064SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
65#SDKPATH = "/"
Thomas Wouters477c8d52006-05-27 19:21:47 +000066
Ronald Oussoren1943f862009-03-30 19:39:14 +000067universal_opts_map = { '32-bit': ('i386', 'ppc',),
68 '64-bit': ('x86_64', 'ppc64',),
69 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
70
71UNIVERSALOPTS = tuple(universal_opts_map.keys())
72
73UNIVERSALARCHS = '32-bit'
74
75ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +000076
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000077# Source directory (asume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +000078SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +000079 os.path.dirname(
80 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000081 os.path.abspath(__file__
82 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +000083
Ronald Oussoren1943f862009-03-30 19:39:14 +000084# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
85DEPTARGET = '10.3'
86
Thomas Wouters89f507f2006-12-13 04:49:30 +000087USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +000088 Usage: build_python [options]
89
90 Options:
91 -? or -h: Show this message
92 -b DIR
93 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
94 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
95 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
96 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +000097 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
98 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +000099""")% globals()
100
101
102# Instructions for building libraries that are necessary for building a
103# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000104# [The recipes are defined here for convenience but instantiated later after
105# command line options have been processed.]
106def library_recipes():
107 return [
108 dict(
109 name="Bzip2 1.0.4",
110 url="http://www.bzip.org/1.0.4/bzip2-1.0.4.tar.gz",
111 checksum='fc310b254f6ba5fbb5da018f04533688',
112 configure=None,
113 install='make install PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
114 shellQuote(os.path.join(WORKDIR, 'libraries')),
115 ' -arch '.join(ARCHLIST),
116 SDKPATH,
117 ),
118 ),
119 dict(
120 name="ZLib 1.2.3",
121 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
122 checksum='debc62758716a169df9f62e6ab2bc634',
123 configure=None,
124 install='make install prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
125 shellQuote(os.path.join(WORKDIR, 'libraries')),
126 ' -arch '.join(ARCHLIST),
127 SDKPATH,
128 ),
129 ),
130 dict(
131 # Note that GNU readline is GPL'd software
132 name="GNU Readline 5.1.4",
133 url="http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz" ,
134 checksum='7ee5a692db88b30ca48927a13fd60e46',
135 patchlevel='0',
136 patches=[
137 # The readline maintainers don't do actual micro releases, but
138 # just ship a set of patches.
139 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-001',
140 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-002',
141 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-003',
142 'http://ftp.gnu.org/pub/gnu/readline/readline-5.1-patches/readline51-004',
143 ]
144 ),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000145
Ronald Oussoren1943f862009-03-30 19:39:14 +0000146 dict(
147 name="SQLite 3.6.11",
148 url="http://www.sqlite.org/sqlite-3.6.11.tar.gz",
149 checksum='7ebb099696ab76cc6ff65dd496d17858',
150 configure_pre=[
151 '--enable-threadsafe',
152 '--enable-tempstore',
153 '--enable-shared=no',
154 '--enable-static=yes',
155 '--disable-tcl',
156 ]
157 ),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000158
159 dict(
160 name="NCurses 5.5",
161 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.5.tar.gz",
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000162 checksum='e73c1ac10b4bfc46db43b2ddfd6244ef',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000163 configure_pre=[
164 "--without-cxx",
165 "--without-ada",
166 "--without-progs",
167 "--without-curses-h",
168 "--enable-shared",
169 "--with-shared",
170 "--datadir=/usr/share",
171 "--sysconfdir=/etc",
172 "--sharedstatedir=/usr/com",
173 "--with-terminfo-dirs=/usr/share/terminfo",
174 "--with-default-terminfo-dir=/usr/share/terminfo",
175 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
176 "--enable-termcap",
177 ],
178 patches=[
179 "ncurses-5.5.patch",
180 ],
181 useLDFlags=False,
182 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
183 shellQuote(os.path.join(WORKDIR, 'libraries')),
184 shellQuote(os.path.join(WORKDIR, 'libraries')),
185 getVersion(),
186 ),
187 ),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000188]
189
Thomas Wouters477c8d52006-05-27 19:21:47 +0000190# Instructions for building packages inside the .mpkg.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000191PKG_RECIPES = [
Thomas Wouters477c8d52006-05-27 19:21:47 +0000192 dict(
193 name="PythonFramework",
194 long_name="Python Framework",
195 source="/Library/Frameworks/Python.framework",
196 readme="""\
197 This package installs Python.framework, that is the python
198 interpreter and the standard library. This also includes Python
199 wrappers for lots of Mac OS X API's.
200 """,
201 postflight="scripts/postflight.framework",
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000202 selected='selected',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000203 ),
204 dict(
205 name="PythonApplications",
206 long_name="GUI Applications",
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000207 source="/Applications/Python %(VER)s",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000208 readme="""\
Thomas Wouters89f507f2006-12-13 04:49:30 +0000209 This package installs IDLE (an interactive Python IDE),
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000210 Python Launcher and Build Applet (create application bundles
211 from python scripts).
212
213 It also installs a number of examples and demos.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000214 """,
215 required=False,
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000216 selected='selected',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000217 ),
218 dict(
219 name="PythonUnixTools",
220 long_name="UNIX command-line tools",
221 source="/usr/local/bin",
222 readme="""\
223 This package installs the unix tools in /usr/local/bin for
Ronald Oussoren1943f862009-03-30 19:39:14 +0000224 compatibility with older releases of Python. This package
225 is not necessary to use Python.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000226 """,
227 required=False,
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000228 selected='unselected',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000229 ),
230 dict(
231 name="PythonDocumentation",
232 long_name="Python Documentation",
233 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
234 source="/pydocs",
235 readme="""\
236 This package installs the python documentation at a location
237 that is useable for pydoc and IDLE. If you have installed Xcode
238 it will also install a link to the documentation in
239 /Developer/Documentation/Python
240 """,
241 postflight="scripts/postflight.documentation",
242 required=False,
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000243 selected='selected',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000244 ),
245 dict(
246 name="PythonProfileChanges",
247 long_name="Shell profile updater",
248 readme="""\
249 This packages updates your shell profile to make sure that
Ronald Oussoren1943f862009-03-30 19:39:14 +0000250 the Python tools are found by your shell in preference of
Thomas Wouters477c8d52006-05-27 19:21:47 +0000251 the system provided Python tools.
252
253 If you don't install this package you'll have to add
254 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
255 to your PATH by hand.
256 """,
257 postflight="scripts/postflight.patch-profile",
258 topdir="/Library/Frameworks/Python.framework",
259 source="/empty-dir",
260 required=False,
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000261 selected='unselected',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000262 ),
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000263 dict(
264 name="PythonSystemFixes",
265 long_name="Fix system Python",
266 readme="""\
267 This package updates the system python installation on
268 Mac OS X 10.3 to ensure that you can build new python extensions
Thomas Wouters89f507f2006-12-13 04:49:30 +0000269 using that copy of python after installing this version.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000270 """,
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000271 postflight="../Tools/fixapplepython23.py",
272 topdir="/Library/Frameworks/Python.framework",
273 source="/empty-dir",
274 required=False,
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000275 selected='unselected',
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000276 )
Thomas Wouters477c8d52006-05-27 19:21:47 +0000277]
278
Thomas Wouters477c8d52006-05-27 19:21:47 +0000279def fatal(msg):
280 """
281 A fatal error, bail out.
282 """
283 sys.stderr.write('FATAL: ')
284 sys.stderr.write(msg)
285 sys.stderr.write('\n')
286 sys.exit(1)
287
288def fileContents(fn):
289 """
290 Return the contents of the named file
291 """
292 return open(fn, 'rb').read()
293
294def runCommand(commandline):
295 """
296 Run a command and raise RuntimeError if it fails. Output is surpressed
297 unless the command fails.
298 """
299 fd = os.popen(commandline, 'r')
300 data = fd.read()
301 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000302 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000303 sys.stdout.write(data)
Benjamin Peterson623918e2008-12-20 22:50:25 +0000304 raise RuntimeError, "command failed: %s"%(commandline,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000305
306 if VERBOSE:
307 sys.stdout.write(data); sys.stdout.flush()
308
309def captureCommand(commandline):
310 fd = os.popen(commandline, 'r')
311 data = fd.read()
312 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000313 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000314 sys.stdout.write(data)
Benjamin Peterson623918e2008-12-20 22:50:25 +0000315 raise RuntimeError, "command failed: %s"%(commandline,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000316
317 return data
318
319def checkEnvironment():
320 """
321 Check that we're running on a supported system.
322 """
323
324 if platform.system() != 'Darwin':
325 fatal("This script should be run on a Mac OS X 10.4 system")
326
327 if platform.release() <= '8.':
328 fatal("This script should be run on a Mac OS X 10.4 system")
329
330 if not os.path.exists(SDKPATH):
331 fatal("Please install the latest version of Xcode and the %s SDK"%(
332 os.path.basename(SDKPATH[:-4])))
333
334
335
Thomas Wouters89f507f2006-12-13 04:49:30 +0000336def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000337 """
338 Parse arguments and update global settings.
339 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000340 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
341 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST
Thomas Wouters477c8d52006-05-27 19:21:47 +0000342
343 if args is None:
344 args = sys.argv[1:]
345
346 try:
347 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000348 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
349 'dep-target=', 'universal-archs=', 'help' ])
Benjamin Peterson623918e2008-12-20 22:50:25 +0000350 except getopt.error, msg:
351 print msg
Thomas Wouters477c8d52006-05-27 19:21:47 +0000352 sys.exit(1)
353
354 if args:
Benjamin Peterson623918e2008-12-20 22:50:25 +0000355 print "Additional arguments"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000356 sys.exit(1)
357
358 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000359 if k in ('-h', '-?', '--help'):
Benjamin Peterson623918e2008-12-20 22:50:25 +0000360 print USAGE
Thomas Wouters477c8d52006-05-27 19:21:47 +0000361 sys.exit(0)
362
363 elif k in ('-d', '--build-dir'):
364 WORKDIR=v
365
366 elif k in ('--third-party',):
367 DEPSRC=v
368
369 elif k in ('--sdk-path',):
370 SDKPATH=v
371
372 elif k in ('--src-dir',):
373 SRCDIR=v
374
Ronald Oussoren1943f862009-03-30 19:39:14 +0000375 elif k in ('--dep-target', ):
376 DEPTARGET=v
377
378 elif k in ('--universal-archs', ):
379 if v in UNIVERSALOPTS:
380 UNIVERSALARCHS = v
381 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
382 else:
383 raise NotImplementedError, v
384
Thomas Wouters477c8d52006-05-27 19:21:47 +0000385 else:
Benjamin Peterson623918e2008-12-20 22:50:25 +0000386 raise NotImplementedError, k
Thomas Wouters477c8d52006-05-27 19:21:47 +0000387
388 SRCDIR=os.path.abspath(SRCDIR)
389 WORKDIR=os.path.abspath(WORKDIR)
390 SDKPATH=os.path.abspath(SDKPATH)
391 DEPSRC=os.path.abspath(DEPSRC)
392
Benjamin Peterson623918e2008-12-20 22:50:25 +0000393 print "Settings:"
394 print " * Source directory:", SRCDIR
395 print " * Build directory: ", WORKDIR
396 print " * SDK location: ", SDKPATH
Ronald Oussoren1943f862009-03-30 19:39:14 +0000397 print " * Third-party source:", DEPSRC
398 print " * Deployment target:", DEPTARGET
399 print " * Universal architectures:", ARCHLIST
Benjamin Peterson623918e2008-12-20 22:50:25 +0000400 print ""
Thomas Wouters477c8d52006-05-27 19:21:47 +0000401
402
403
404
405def extractArchive(builddir, archiveName):
406 """
407 Extract a source archive into 'builddir'. Returns the path of the
408 extracted archive.
409
410 XXX: This function assumes that archives contain a toplevel directory
411 that is has the same name as the basename of the archive. This is
412 save enough for anything we use.
413 """
414 curdir = os.getcwd()
415 try:
416 os.chdir(builddir)
417 if archiveName.endswith('.tar.gz'):
418 retval = os.path.basename(archiveName[:-7])
419 if os.path.exists(retval):
420 shutil.rmtree(retval)
421 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
422
423 elif archiveName.endswith('.tar.bz2'):
424 retval = os.path.basename(archiveName[:-8])
425 if os.path.exists(retval):
426 shutil.rmtree(retval)
427 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
428
429 elif archiveName.endswith('.tar'):
430 retval = os.path.basename(archiveName[:-4])
431 if os.path.exists(retval):
432 shutil.rmtree(retval)
433 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
434
435 elif archiveName.endswith('.zip'):
436 retval = os.path.basename(archiveName[:-4])
437 if os.path.exists(retval):
438 shutil.rmtree(retval)
439 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
440
441 data = fp.read()
442 xit = fp.close()
443 if xit is not None:
444 sys.stdout.write(data)
Benjamin Peterson623918e2008-12-20 22:50:25 +0000445 raise RuntimeError, "Cannot extract %s"%(archiveName,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000446
447 return os.path.join(builddir, retval)
448
449 finally:
450 os.chdir(curdir)
451
452KNOWNSIZES = {
453 "http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz": 7952742,
454 "http://downloads.sleepycat.com/db-4.4.20.tar.gz": 2030276,
455}
456
457def downloadURL(url, fname):
458 """
459 Download the contents of the url into the file.
460 """
461 try:
462 size = os.path.getsize(fname)
463 except OSError:
464 pass
465 else:
466 if KNOWNSIZES.get(url) == size:
Benjamin Peterson623918e2008-12-20 22:50:25 +0000467 print "Using existing file for", url
Thomas Wouters477c8d52006-05-27 19:21:47 +0000468 return
Benjamin Peterson623918e2008-12-20 22:50:25 +0000469 fpIn = urllib2.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000470 fpOut = open(fname, 'wb')
471 block = fpIn.read(10240)
472 try:
473 while block:
474 fpOut.write(block)
475 block = fpIn.read(10240)
476 fpIn.close()
477 fpOut.close()
478 except:
479 try:
480 os.unlink(fname)
481 except:
482 pass
483
484def buildRecipe(recipe, basedir, archList):
485 """
486 Build software using a recipe. This function does the
487 'configure;make;make install' dance for C software, with a possibility
488 to customize this process, basically a poor-mans DarwinPorts.
489 """
490 curdir = os.getcwd()
491
492 name = recipe['name']
493 url = recipe['url']
494 configure = recipe.get('configure', './configure')
495 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
496 shellQuote(basedir)))
497
498 archiveName = os.path.split(url)[-1]
499 sourceArchive = os.path.join(DEPSRC, archiveName)
500
501 if not os.path.exists(DEPSRC):
502 os.mkdir(DEPSRC)
503
504
505 if os.path.exists(sourceArchive):
Benjamin Peterson623918e2008-12-20 22:50:25 +0000506 print "Using local copy of %s"%(name,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000507
508 else:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000509 print "Did not find local copy of %s"%(name,)
Benjamin Peterson623918e2008-12-20 22:50:25 +0000510 print "Downloading %s"%(name,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000511 downloadURL(url, sourceArchive)
Benjamin Peterson623918e2008-12-20 22:50:25 +0000512 print "Archive for %s stored as %s"%(name, sourceArchive)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000513
Benjamin Peterson623918e2008-12-20 22:50:25 +0000514 print "Extracting archive for %s"%(name,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000515 buildDir=os.path.join(WORKDIR, '_bld')
516 if not os.path.exists(buildDir):
517 os.mkdir(buildDir)
518
519 workDir = extractArchive(buildDir, sourceArchive)
520 os.chdir(workDir)
521 if 'buildDir' in recipe:
522 os.chdir(recipe['buildDir'])
523
524
525 for fn in recipe.get('patches', ()):
526 if fn.startswith('http://'):
527 # Download the patch before applying it.
528 path = os.path.join(DEPSRC, os.path.basename(fn))
529 downloadURL(fn, path)
530 fn = path
531
532 fn = os.path.join(curdir, fn)
533 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
534 shellQuote(fn),))
535
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000536 if configure is not None:
537 configure_args = [
538 "--prefix=/usr/local",
539 "--enable-static",
540 "--disable-shared",
541 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
542 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000544 if 'configure_pre' in recipe:
545 args = list(recipe['configure_pre'])
546 if '--disable-static' in args:
547 configure_args.remove('--enable-static')
548 if '--enable-shared' in args:
549 configure_args.remove('--disable-shared')
550 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000551
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000552 if recipe.get('useLDFlags', 1):
553 configure_args.extend([
554 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
555 ' -arch '.join(archList),
556 shellQuote(SDKPATH)[1:-1],
557 shellQuote(basedir)[1:-1],),
558 "LDFLAGS=-syslibroot,%s -L%s/usr/local/lib -arch %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000559 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000560 shellQuote(basedir)[1:-1],
561 ' -arch '.join(archList)),
562 ])
563 else:
564 configure_args.extend([
565 "CFLAGS=-arch %s -isysroot %s -I%s/usr/local/include"%(
566 ' -arch '.join(archList),
567 shellQuote(SDKPATH)[1:-1],
568 shellQuote(basedir)[1:-1],),
569 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000570
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000571 if 'configure_post' in recipe:
572 configure_args = configure_args = list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000573
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000574 configure_args.insert(0, configure)
575 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000576
Benjamin Peterson623918e2008-12-20 22:50:25 +0000577 print "Running configure for %s"%(name,)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000578 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000579
Benjamin Peterson623918e2008-12-20 22:50:25 +0000580 print "Running install for %s"%(name,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000581 runCommand('{ ' + install + ' ;} 2>&1')
582
Benjamin Peterson623918e2008-12-20 22:50:25 +0000583 print "Done %s"%(name,)
584 print ""
Thomas Wouters477c8d52006-05-27 19:21:47 +0000585
586 os.chdir(curdir)
587
588def buildLibraries():
589 """
590 Build our dependencies into $WORKDIR/libraries/usr/local
591 """
Benjamin Peterson623918e2008-12-20 22:50:25 +0000592 print ""
593 print "Building required libraries"
594 print ""
Thomas Wouters477c8d52006-05-27 19:21:47 +0000595 universal = os.path.join(WORKDIR, 'libraries')
596 os.mkdir(universal)
597 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
598 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
599
Ronald Oussoren1943f862009-03-30 19:39:14 +0000600 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000601 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000602
603
604
605def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000606 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +0000607 # inside the framwork. pydoc and IDLE will pick it up there.
Benjamin Peterson623918e2008-12-20 22:50:25 +0000608 print "Install python documentation"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000609 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000610 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000611 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000612 curDir = os.getcwd()
613 os.chdir(buildDir)
614 runCommand('make update')
615 runCommand('make html')
616 os.chdir(curDir)
617 if not os.path.exists(docdir):
618 os.mkdir(docdir)
619 os.rename(os.path.join(buildDir, 'build', 'html'),
620 os.path.join(docdir, 'python-docs-html'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000621
622
623def buildPython():
Ronald Oussoren1943f862009-03-30 19:39:14 +0000624 print "Building a universal python for %s architectures" % UNIVERSALARCHS
Thomas Wouters477c8d52006-05-27 19:21:47 +0000625
626 buildDir = os.path.join(WORKDIR, '_bld', 'python')
627 rootDir = os.path.join(WORKDIR, '_root')
628
629 if os.path.exists(buildDir):
630 shutil.rmtree(buildDir)
631 if os.path.exists(rootDir):
632 shutil.rmtree(rootDir)
633 os.mkdir(buildDir)
634 os.mkdir(rootDir)
635 os.mkdir(os.path.join(rootDir, 'empty-dir'))
636 curdir = os.getcwd()
637 os.chdir(buildDir)
638
639 # Not sure if this is still needed, the original build script
640 # claims that parts of the install assume python.exe exists.
641 os.symlink('python', os.path.join(buildDir, 'python.exe'))
642
643 # Extract the version from the configure file, needed to calculate
644 # several paths.
645 version = getVersion()
646
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000647 # Since the extra libs are not in their installed framework location
648 # during the build, augment the library path so that the interpreter
649 # will find them during its extension import sanity checks.
650 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
651 'libraries', 'usr', 'local', 'lib')
Benjamin Peterson623918e2008-12-20 22:50:25 +0000652 print "Running configure..."
Ronald Oussoren1943f862009-03-30 19:39:14 +0000653 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Ronald Oussoren0e314542009-03-30 19:51:09 +0000654 "--with-universal-archs=%s --with-computed-gotos"
Ronald Oussoren1943f862009-03-30 19:39:14 +0000655 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
656 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
657 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
658 UNIVERSALARCHS,
659 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000660 shellQuote(WORKDIR)[1:-1]))
661
Benjamin Peterson623918e2008-12-20 22:50:25 +0000662 print "Running make"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000663 runCommand("make")
664
Benjamin Peterson623918e2008-12-20 22:50:25 +0000665 print "Running make frameworkinstall"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000666 runCommand("make frameworkinstall DESTDIR=%s"%(
667 shellQuote(rootDir)))
668
Benjamin Peterson623918e2008-12-20 22:50:25 +0000669 print "Running make frameworkinstallextras"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000670 runCommand("make frameworkinstallextras DESTDIR=%s"%(
671 shellQuote(rootDir)))
672
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000673 del os.environ['DYLD_LIBRARY_PATH']
Benjamin Peterson623918e2008-12-20 22:50:25 +0000674 print "Copying required shared libraries"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000675 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
676 runCommand("mv %s/* %s"%(
677 shellQuote(os.path.join(
678 WORKDIR, 'libraries', 'Library', 'Frameworks',
679 'Python.framework', 'Versions', getVersion(),
680 'lib')),
681 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
682 'Python.framework', 'Versions', getVersion(),
683 'lib'))))
684
Benjamin Peterson623918e2008-12-20 22:50:25 +0000685 print "Fix file modes"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000686 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000687 gid = grp.getgrnam('admin').gr_gid
688
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000689
690
Thomas Wouters477c8d52006-05-27 19:21:47 +0000691 for dirpath, dirnames, filenames in os.walk(frmDir):
692 for dn in dirnames:
693 os.chmod(os.path.join(dirpath, dn), 0775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000694 os.chown(os.path.join(dirpath, dn), -1, gid)
695
Thomas Wouters477c8d52006-05-27 19:21:47 +0000696
697 for fn in filenames:
698 if os.path.islink(fn):
699 continue
700
701 # "chmod g+w $fn"
702 p = os.path.join(dirpath, fn)
703 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000704 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
705 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000706
707 # We added some directories to the search path during the configure
708 # phase. Remove those because those directories won't be there on
709 # the end-users system.
710 path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
711 'Versions', version, 'lib', 'python%s'%(version,),
712 'config', 'Makefile')
713 fp = open(path, 'r')
714 data = fp.read()
715 fp.close()
716
717 data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
718 data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
719 fp = open(path, 'w')
720 fp.write(data)
721 fp.close()
722
723 # Add symlinks in /usr/local/bin, using relative links
724 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
725 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
726 'Python.framework', 'Versions', version, 'bin')
727 if os.path.exists(usr_local_bin):
728 shutil.rmtree(usr_local_bin)
729 os.makedirs(usr_local_bin)
730 for fn in os.listdir(
731 os.path.join(frmDir, 'Versions', version, 'bin')):
732 os.symlink(os.path.join(to_framework, fn),
733 os.path.join(usr_local_bin, fn))
734
735 os.chdir(curdir)
736
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000737 # Remove the 'Current' link, that way we don't accidently mess with an already installed
738 # version of python
739 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework', 'Versions', 'Current'))
740
741
Thomas Wouters477c8d52006-05-27 19:21:47 +0000742
743
744def patchFile(inPath, outPath):
745 data = fileContents(inPath)
746 data = data.replace('$FULL_VERSION', getFullVersion())
747 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +0000748 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000749 data = data.replace('$ARCHITECTURES', "i386, ppc")
750 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000751
752 # This one is not handy as a template variable
753 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000754 fp = open(outPath, 'wb')
755 fp.write(data)
756 fp.close()
757
758def patchScript(inPath, outPath):
759 data = fileContents(inPath)
760 data = data.replace('@PYVER@', getVersion())
761 fp = open(outPath, 'wb')
762 fp.write(data)
763 fp.close()
764 os.chmod(outPath, 0755)
765
766
767
768def packageFromRecipe(targetDir, recipe):
769 curdir = os.getcwd()
770 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000771 # The major version (such as 2.5) is included in the package name
772 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000773 # common.
774 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +0000775 srcdir = recipe.get('source')
776 pkgroot = recipe.get('topdir', srcdir)
777 postflight = recipe.get('postflight')
778 readme = textwrap.dedent(recipe['readme'])
779 isRequired = recipe.get('required', True)
780
Benjamin Peterson623918e2008-12-20 22:50:25 +0000781 print "- building package %s"%(pkgname,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000782
783 # Substitute some variables
784 textvars = dict(
785 VER=getVersion(),
786 FULLVER=getFullVersion(),
787 )
788 readme = readme % textvars
789
790 if pkgroot is not None:
791 pkgroot = pkgroot % textvars
792 else:
793 pkgroot = '/'
794
795 if srcdir is not None:
796 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
797 srcdir = srcdir % textvars
798
799 if postflight is not None:
800 postflight = os.path.abspath(postflight)
801
802 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
803 os.makedirs(packageContents)
804
805 if srcdir is not None:
806 os.chdir(srcdir)
807 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
808 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
809 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
810
811 fn = os.path.join(packageContents, 'PkgInfo')
812 fp = open(fn, 'w')
813 fp.write('pmkrpkg1')
814 fp.close()
815
816 rsrcDir = os.path.join(packageContents, "Resources")
817 os.mkdir(rsrcDir)
818 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
819 fp.write(readme)
820 fp.close()
821
822 if postflight is not None:
823 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
824
825 vers = getFullVersion()
826 major, minor = map(int, getVersion().split('.', 2))
827 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000828 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
829 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
830 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000831 CFBundleShortVersionString=vers,
832 IFMajorVersion=major,
833 IFMinorVersion=minor,
834 IFPkgFormatVersion=0.10000000149011612,
835 IFPkgFlagAllowBackRev=False,
836 IFPkgFlagAuthorizationAction="RootAuthorization",
837 IFPkgFlagDefaultLocation=pkgroot,
838 IFPkgFlagFollowLinks=True,
839 IFPkgFlagInstallFat=True,
840 IFPkgFlagIsRequired=isRequired,
841 IFPkgFlagOverwritePermissions=False,
842 IFPkgFlagRelocatable=False,
843 IFPkgFlagRestartAction="NoRestart",
844 IFPkgFlagRootVolumeOnly=True,
845 IFPkgFlagUpdateInstalledLangauges=False,
846 )
847 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
848
849 pl = Plist(
850 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +0000851 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000852 IFPkgDescriptionVersion=vers,
853 )
854 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
855
856 finally:
857 os.chdir(curdir)
858
859
860def makeMpkgPlist(path):
861
862 vers = getFullVersion()
863 major, minor = map(int, getVersion().split('.', 2))
864
865 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000866 CFBundleGetInfoString="Python %s"%(vers,),
867 CFBundleIdentifier='org.python.Python',
868 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000869 CFBundleShortVersionString=vers,
870 IFMajorVersion=major,
871 IFMinorVersion=minor,
872 IFPkgFlagComponentDirectory="Contents/Packages",
873 IFPkgFlagPackageList=[
874 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000875 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000876 IFPkgFlagPackageSelection=item['selected'],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000877 )
878 for item in PKG_RECIPES
879 ],
880 IFPkgFormatVersion=0.10000000149011612,
881 IFPkgFlagBackgroundScaling="proportional",
882 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000883 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000884 )
885
886 writePlist(pl, path)
887
888
889def buildInstaller():
890
891 # Zap all compiled files
892 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
893 for fn in filenames:
894 if fn.endswith('.pyc') or fn.endswith('.pyo'):
895 os.unlink(os.path.join(dirpath, fn))
896
897 outdir = os.path.join(WORKDIR, 'installer')
898 if os.path.exists(outdir):
899 shutil.rmtree(outdir)
900 os.mkdir(outdir)
901
Ronald Oussoren1943f862009-03-30 19:39:14 +0000902 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000903 pkgcontents = os.path.join(pkgroot, 'Packages')
904 os.makedirs(pkgcontents)
905 for recipe in PKG_RECIPES:
906 packageFromRecipe(pkgcontents, recipe)
907
908 rsrcDir = os.path.join(pkgroot, 'Resources')
909
910 fn = os.path.join(pkgroot, 'PkgInfo')
911 fp = open(fn, 'w')
912 fp.write('pmkrpkg1')
913 fp.close()
914
915 os.mkdir(rsrcDir)
916
917 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
918 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000919 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000920 IFPkgDescriptionVersion=getVersion(),
921 )
922
923 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
924 for fn in os.listdir('resources'):
925 if fn == '.svn': continue
926 if fn.endswith('.jpg'):
927 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
928 else:
929 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
930
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000931 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000932
933
934def installSize(clear=False, _saved=[]):
935 if clear:
936 del _saved[:]
937 if not _saved:
938 data = captureCommand("du -ks %s"%(
939 shellQuote(os.path.join(WORKDIR, '_root'))))
940 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
941 return _saved[0]
942
943
944def buildDMG():
945 """
Thomas Wouters89f507f2006-12-13 04:49:30 +0000946 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000947 """
948 outdir = os.path.join(WORKDIR, 'diskimage')
949 if os.path.exists(outdir):
950 shutil.rmtree(outdir)
951
952 imagepath = os.path.join(outdir,
953 'python-%s-macosx'%(getFullVersion(),))
954 if INCLUDE_TIMESTAMP:
955 imagepath = imagepath + '%04d-%02d-%02d'%(time.localtime()[:3])
956 imagepath = imagepath + '.dmg'
957
958 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000959 volname='Python %s'%(getFullVersion())
960 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
961 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000962 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +0000963 shellQuote(imagepath + ".tmp.dmg" )))
964
965
966 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
967 os.mkdir(os.path.join(WORKDIR, "mnt"))
968 runCommand("hdiutil attach %s -mountroot %s"%(
969 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
970
971 # Custom icon for the DMG, shown when the DMG is mounted.
972 shutil.copy("../Icons/Disk Image.icns",
973 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
974 runCommand("/Developer/Tools/SetFile -a C %s/"%(
975 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
976
977 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
978
979 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
980 runCommand("hdiutil convert %s -format UDZO -o %s"%(
981 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
982 setIcon(imagepath, "../Icons/Disk Image.icns")
983
984 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000985
986 return imagepath
987
988
989def setIcon(filePath, icnsPath):
990 """
991 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000992 """
Thomas Wouters477c8d52006-05-27 19:21:47 +0000993
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000994 toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
995 dirPath = os.path.dirname(__file__)
996 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
997 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
998 # to connections to the window server.
999 if not os.path.exists('seticon.app/Contents/MacOS'):
1000 os.makedirs('seticon.app/Contents/MacOS')
1001 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1002 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001003
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001004 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1005 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001006
1007def main():
1008 # First parse options and check if we can perform our work
1009 parseOptions()
1010 checkEnvironment()
1011
Ronald Oussoren1943f862009-03-30 19:39:14 +00001012 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Thomas Wouters477c8d52006-05-27 19:21:47 +00001013
1014 if os.path.exists(WORKDIR):
1015 shutil.rmtree(WORKDIR)
1016 os.mkdir(WORKDIR)
1017
1018 # Then build third-party libraries such as sleepycat DB4.
1019 buildLibraries()
1020
1021 # Now build python itself
1022 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001023
1024 # And then build the documentation
1025 # Remove the Deployment Target from the shell
1026 # environment, it's no longer needed and
1027 # an unexpected build target can cause problems
1028 # when Sphinx and its dependencies need to
1029 # be (re-)installed.
1030 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001031 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001032
1033
1034 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001035 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001036 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001037 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001038
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001039 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001040 getVersion(),))
1041 os.chmod(folder, 0755)
1042 setIcon(folder, "../Icons/Python Folder.icns")
1043
1044 # Create the installer
1045 buildInstaller()
1046
1047 # And copy the readme into the directory containing the installer
1048 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1049
1050 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001051 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001052
1053 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Benjamin Peterson623918e2008-12-20 22:50:25 +00001054 print >> fp, "# BUILD INFO"
1055 print >> fp, "# Date:", time.ctime()
1056 print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
Thomas Wouters477c8d52006-05-27 19:21:47 +00001057 fp.close()
1058
Thomas Wouters477c8d52006-05-27 19:21:47 +00001059
1060
1061 # And copy it to a DMG
1062 buildDMG()
1063
1064
1065if __name__ == "__main__":
1066 main()