blob: d6a8618e9743f3c11e72a3f1e5416aafe2ddc643 [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 Oussorenafbca492009-05-26 21:18:59 +0000228 selected='selected',
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)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000617 if os.path.exists(docdir):
618 os.rmdir(docdir)
619 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000620
621
622def buildPython():
Ronald Oussoren1943f862009-03-30 19:39:14 +0000623 print "Building a universal python for %s architectures" % UNIVERSALARCHS
Thomas Wouters477c8d52006-05-27 19:21:47 +0000624
625 buildDir = os.path.join(WORKDIR, '_bld', 'python')
626 rootDir = os.path.join(WORKDIR, '_root')
627
628 if os.path.exists(buildDir):
629 shutil.rmtree(buildDir)
630 if os.path.exists(rootDir):
631 shutil.rmtree(rootDir)
632 os.mkdir(buildDir)
633 os.mkdir(rootDir)
634 os.mkdir(os.path.join(rootDir, 'empty-dir'))
635 curdir = os.getcwd()
636 os.chdir(buildDir)
637
638 # Not sure if this is still needed, the original build script
639 # claims that parts of the install assume python.exe exists.
640 os.symlink('python', os.path.join(buildDir, 'python.exe'))
641
642 # Extract the version from the configure file, needed to calculate
643 # several paths.
644 version = getVersion()
645
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000646 # Since the extra libs are not in their installed framework location
647 # during the build, augment the library path so that the interpreter
648 # will find them during its extension import sanity checks.
649 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
650 'libraries', 'usr', 'local', 'lib')
Benjamin Peterson623918e2008-12-20 22:50:25 +0000651 print "Running configure..."
Ronald Oussoren1943f862009-03-30 19:39:14 +0000652 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Ronald Oussoren9ae36402009-04-01 14:59:59 +0000653 "--with-universal-archs=%s --with-computed-gotos "
Ronald Oussoren1943f862009-03-30 19:39:14 +0000654 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
655 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
656 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
657 UNIVERSALARCHS,
658 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000659 shellQuote(WORKDIR)[1:-1]))
660
Benjamin Peterson623918e2008-12-20 22:50:25 +0000661 print "Running make"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000662 runCommand("make")
663
Benjamin Peterson623918e2008-12-20 22:50:25 +0000664 print "Running make frameworkinstall"
Ronald Oussorenf84d7e92009-05-19 11:27:25 +0000665 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000666 shellQuote(rootDir)))
667
Benjamin Peterson623918e2008-12-20 22:50:25 +0000668 print "Running make frameworkinstallextras"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000669 runCommand("make frameworkinstallextras DESTDIR=%s"%(
670 shellQuote(rootDir)))
671
Ronald Oussorenac4b39f2009-03-30 20:05:35 +0000672 del os.environ['DYLD_LIBRARY_PATH']
Benjamin Peterson623918e2008-12-20 22:50:25 +0000673 print "Copying required shared libraries"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000674 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
675 runCommand("mv %s/* %s"%(
676 shellQuote(os.path.join(
677 WORKDIR, 'libraries', 'Library', 'Frameworks',
678 'Python.framework', 'Versions', getVersion(),
679 'lib')),
680 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
681 'Python.framework', 'Versions', getVersion(),
682 'lib'))))
683
Benjamin Peterson623918e2008-12-20 22:50:25 +0000684 print "Fix file modes"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000685 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000686 gid = grp.getgrnam('admin').gr_gid
687
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000688
689
Thomas Wouters477c8d52006-05-27 19:21:47 +0000690 for dirpath, dirnames, filenames in os.walk(frmDir):
691 for dn in dirnames:
692 os.chmod(os.path.join(dirpath, dn), 0775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000693 os.chown(os.path.join(dirpath, dn), -1, gid)
694
Thomas Wouters477c8d52006-05-27 19:21:47 +0000695
696 for fn in filenames:
697 if os.path.islink(fn):
698 continue
699
700 # "chmod g+w $fn"
701 p = os.path.join(dirpath, fn)
702 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000703 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
704 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000705
706 # We added some directories to the search path during the configure
707 # phase. Remove those because those directories won't be there on
708 # the end-users system.
709 path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
710 'Versions', version, 'lib', 'python%s'%(version,),
711 'config', 'Makefile')
712 fp = open(path, 'r')
713 data = fp.read()
714 fp.close()
715
716 data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
717 data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
718 fp = open(path, 'w')
719 fp.write(data)
720 fp.close()
721
722 # Add symlinks in /usr/local/bin, using relative links
723 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
724 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
725 'Python.framework', 'Versions', version, 'bin')
726 if os.path.exists(usr_local_bin):
727 shutil.rmtree(usr_local_bin)
728 os.makedirs(usr_local_bin)
729 for fn in os.listdir(
730 os.path.join(frmDir, 'Versions', version, 'bin')):
731 os.symlink(os.path.join(to_framework, fn),
732 os.path.join(usr_local_bin, fn))
733
734 os.chdir(curdir)
735
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000736 # Remove the 'Current' link, that way we don't accidently mess with an already installed
737 # version of python
738 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework', 'Versions', 'Current'))
739
740
Thomas Wouters477c8d52006-05-27 19:21:47 +0000741
742
743def patchFile(inPath, outPath):
744 data = fileContents(inPath)
745 data = data.replace('$FULL_VERSION', getFullVersion())
746 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +0000747 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000748 data = data.replace('$ARCHITECTURES', "i386, ppc")
749 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000750
751 # This one is not handy as a template variable
752 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000753 fp = open(outPath, 'wb')
754 fp.write(data)
755 fp.close()
756
757def patchScript(inPath, outPath):
758 data = fileContents(inPath)
759 data = data.replace('@PYVER@', getVersion())
760 fp = open(outPath, 'wb')
761 fp.write(data)
762 fp.close()
763 os.chmod(outPath, 0755)
764
765
766
767def packageFromRecipe(targetDir, recipe):
768 curdir = os.getcwd()
769 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000770 # The major version (such as 2.5) is included in the package name
771 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000772 # common.
773 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +0000774 srcdir = recipe.get('source')
775 pkgroot = recipe.get('topdir', srcdir)
776 postflight = recipe.get('postflight')
777 readme = textwrap.dedent(recipe['readme'])
778 isRequired = recipe.get('required', True)
779
Benjamin Peterson623918e2008-12-20 22:50:25 +0000780 print "- building package %s"%(pkgname,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000781
782 # Substitute some variables
783 textvars = dict(
784 VER=getVersion(),
785 FULLVER=getFullVersion(),
786 )
787 readme = readme % textvars
788
789 if pkgroot is not None:
790 pkgroot = pkgroot % textvars
791 else:
792 pkgroot = '/'
793
794 if srcdir is not None:
795 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
796 srcdir = srcdir % textvars
797
798 if postflight is not None:
799 postflight = os.path.abspath(postflight)
800
801 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
802 os.makedirs(packageContents)
803
804 if srcdir is not None:
805 os.chdir(srcdir)
806 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
807 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
808 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
809
810 fn = os.path.join(packageContents, 'PkgInfo')
811 fp = open(fn, 'w')
812 fp.write('pmkrpkg1')
813 fp.close()
814
815 rsrcDir = os.path.join(packageContents, "Resources")
816 os.mkdir(rsrcDir)
817 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
818 fp.write(readme)
819 fp.close()
820
821 if postflight is not None:
822 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
823
824 vers = getFullVersion()
825 major, minor = map(int, getVersion().split('.', 2))
826 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000827 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
828 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
829 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000830 CFBundleShortVersionString=vers,
831 IFMajorVersion=major,
832 IFMinorVersion=minor,
833 IFPkgFormatVersion=0.10000000149011612,
834 IFPkgFlagAllowBackRev=False,
835 IFPkgFlagAuthorizationAction="RootAuthorization",
836 IFPkgFlagDefaultLocation=pkgroot,
837 IFPkgFlagFollowLinks=True,
838 IFPkgFlagInstallFat=True,
839 IFPkgFlagIsRequired=isRequired,
840 IFPkgFlagOverwritePermissions=False,
841 IFPkgFlagRelocatable=False,
842 IFPkgFlagRestartAction="NoRestart",
843 IFPkgFlagRootVolumeOnly=True,
844 IFPkgFlagUpdateInstalledLangauges=False,
845 )
846 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
847
848 pl = Plist(
849 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +0000850 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000851 IFPkgDescriptionVersion=vers,
852 )
853 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
854
855 finally:
856 os.chdir(curdir)
857
858
859def makeMpkgPlist(path):
860
861 vers = getFullVersion()
862 major, minor = map(int, getVersion().split('.', 2))
863
864 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000865 CFBundleGetInfoString="Python %s"%(vers,),
866 CFBundleIdentifier='org.python.Python',
867 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000868 CFBundleShortVersionString=vers,
869 IFMajorVersion=major,
870 IFMinorVersion=minor,
871 IFPkgFlagComponentDirectory="Contents/Packages",
872 IFPkgFlagPackageList=[
873 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000874 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000875 IFPkgFlagPackageSelection=item['selected'],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000876 )
877 for item in PKG_RECIPES
878 ],
879 IFPkgFormatVersion=0.10000000149011612,
880 IFPkgFlagBackgroundScaling="proportional",
881 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000882 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000883 )
884
885 writePlist(pl, path)
886
887
888def buildInstaller():
889
890 # Zap all compiled files
891 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
892 for fn in filenames:
893 if fn.endswith('.pyc') or fn.endswith('.pyo'):
894 os.unlink(os.path.join(dirpath, fn))
895
896 outdir = os.path.join(WORKDIR, 'installer')
897 if os.path.exists(outdir):
898 shutil.rmtree(outdir)
899 os.mkdir(outdir)
900
Ronald Oussoren1943f862009-03-30 19:39:14 +0000901 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000902 pkgcontents = os.path.join(pkgroot, 'Packages')
903 os.makedirs(pkgcontents)
904 for recipe in PKG_RECIPES:
905 packageFromRecipe(pkgcontents, recipe)
906
907 rsrcDir = os.path.join(pkgroot, 'Resources')
908
909 fn = os.path.join(pkgroot, 'PkgInfo')
910 fp = open(fn, 'w')
911 fp.write('pmkrpkg1')
912 fp.close()
913
914 os.mkdir(rsrcDir)
915
916 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
917 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000918 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000919 IFPkgDescriptionVersion=getVersion(),
920 )
921
922 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
923 for fn in os.listdir('resources'):
924 if fn == '.svn': continue
925 if fn.endswith('.jpg'):
926 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
927 else:
928 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
929
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000930 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000931
932
933def installSize(clear=False, _saved=[]):
934 if clear:
935 del _saved[:]
936 if not _saved:
937 data = captureCommand("du -ks %s"%(
938 shellQuote(os.path.join(WORKDIR, '_root'))))
939 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
940 return _saved[0]
941
942
943def buildDMG():
944 """
Thomas Wouters89f507f2006-12-13 04:49:30 +0000945 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000946 """
947 outdir = os.path.join(WORKDIR, 'diskimage')
948 if os.path.exists(outdir):
949 shutil.rmtree(outdir)
950
951 imagepath = os.path.join(outdir,
952 'python-%s-macosx'%(getFullVersion(),))
953 if INCLUDE_TIMESTAMP:
954 imagepath = imagepath + '%04d-%02d-%02d'%(time.localtime()[:3])
955 imagepath = imagepath + '.dmg'
956
957 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000958 volname='Python %s'%(getFullVersion())
959 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
960 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000961 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +0000962 shellQuote(imagepath + ".tmp.dmg" )))
963
964
965 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
966 os.mkdir(os.path.join(WORKDIR, "mnt"))
967 runCommand("hdiutil attach %s -mountroot %s"%(
968 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
969
970 # Custom icon for the DMG, shown when the DMG is mounted.
971 shutil.copy("../Icons/Disk Image.icns",
972 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
973 runCommand("/Developer/Tools/SetFile -a C %s/"%(
974 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
975
976 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
977
978 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
979 runCommand("hdiutil convert %s -format UDZO -o %s"%(
980 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
981 setIcon(imagepath, "../Icons/Disk Image.icns")
982
983 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000984
985 return imagepath
986
987
988def setIcon(filePath, icnsPath):
989 """
990 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000991 """
Thomas Wouters477c8d52006-05-27 19:21:47 +0000992
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000993 toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
994 dirPath = os.path.dirname(__file__)
995 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
996 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
997 # to connections to the window server.
998 if not os.path.exists('seticon.app/Contents/MacOS'):
999 os.makedirs('seticon.app/Contents/MacOS')
1000 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1001 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001002
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001003 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1004 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001005
1006def main():
1007 # First parse options and check if we can perform our work
1008 parseOptions()
1009 checkEnvironment()
1010
Ronald Oussoren1943f862009-03-30 19:39:14 +00001011 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Thomas Wouters477c8d52006-05-27 19:21:47 +00001012
1013 if os.path.exists(WORKDIR):
1014 shutil.rmtree(WORKDIR)
1015 os.mkdir(WORKDIR)
1016
1017 # Then build third-party libraries such as sleepycat DB4.
1018 buildLibraries()
1019
1020 # Now build python itself
1021 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001022
1023 # And then build the documentation
1024 # Remove the Deployment Target from the shell
1025 # environment, it's no longer needed and
1026 # an unexpected build target can cause problems
1027 # when Sphinx and its dependencies need to
1028 # be (re-)installed.
1029 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001030 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001031
1032
1033 # Prepare the applications folder
Thomas Wouters477c8d52006-05-27 19:21:47 +00001034 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001035 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001036 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001037
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001038 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001039 getVersion(),))
1040 os.chmod(folder, 0755)
1041 setIcon(folder, "../Icons/Python Folder.icns")
1042
1043 # Create the installer
1044 buildInstaller()
1045
1046 # And copy the readme into the directory containing the installer
1047 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1048
1049 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001050 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051
1052 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Benjamin Peterson623918e2008-12-20 22:50:25 +00001053 print >> fp, "# BUILD INFO"
1054 print >> fp, "# Date:", time.ctime()
1055 print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
Thomas Wouters477c8d52006-05-27 19:21:47 +00001056 fp.close()
1057
Thomas Wouters477c8d52006-05-27 19:21:47 +00001058
1059
1060 # And copy it to a DMG
1061 buildDMG()
1062
1063
1064if __name__ == "__main__":
1065 main()