blob: 174bf687d43e924f608cdb84c28ce4e807c1dd7c [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
Benjamin Peterson623918e2008-12-20 22:50:25 +0000647 print "Running configure..."
Ronald Oussoren1943f862009-03-30 19:39:14 +0000648 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
649 "--with-universal-archs=%s "
650 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
651 "OPT='-g -O3 -I%s/libraries/usr/local/include' 2>&1"%(
652 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
653 UNIVERSALARCHS,
654 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000655 shellQuote(WORKDIR)[1:-1]))
656
Benjamin Peterson623918e2008-12-20 22:50:25 +0000657 print "Running make"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000658 runCommand("make")
659
Benjamin Peterson623918e2008-12-20 22:50:25 +0000660 print "Running make frameworkinstall"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000661 runCommand("make frameworkinstall DESTDIR=%s"%(
662 shellQuote(rootDir)))
663
Benjamin Peterson623918e2008-12-20 22:50:25 +0000664 print "Running make frameworkinstallextras"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665 runCommand("make frameworkinstallextras DESTDIR=%s"%(
666 shellQuote(rootDir)))
667
Benjamin Peterson623918e2008-12-20 22:50:25 +0000668 print "Copying required shared libraries"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000669 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
670 runCommand("mv %s/* %s"%(
671 shellQuote(os.path.join(
672 WORKDIR, 'libraries', 'Library', 'Frameworks',
673 'Python.framework', 'Versions', getVersion(),
674 'lib')),
675 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
676 'Python.framework', 'Versions', getVersion(),
677 'lib'))))
678
Benjamin Peterson623918e2008-12-20 22:50:25 +0000679 print "Fix file modes"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000680 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000681 gid = grp.getgrnam('admin').gr_gid
682
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000683
684
Thomas Wouters477c8d52006-05-27 19:21:47 +0000685 for dirpath, dirnames, filenames in os.walk(frmDir):
686 for dn in dirnames:
687 os.chmod(os.path.join(dirpath, dn), 0775)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000688 os.chown(os.path.join(dirpath, dn), -1, gid)
689
Thomas Wouters477c8d52006-05-27 19:21:47 +0000690
691 for fn in filenames:
692 if os.path.islink(fn):
693 continue
694
695 # "chmod g+w $fn"
696 p = os.path.join(dirpath, fn)
697 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000698 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
699 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000700
701 # We added some directories to the search path during the configure
702 # phase. Remove those because those directories won't be there on
703 # the end-users system.
704 path =os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework',
705 'Versions', version, 'lib', 'python%s'%(version,),
706 'config', 'Makefile')
707 fp = open(path, 'r')
708 data = fp.read()
709 fp.close()
710
711 data = data.replace('-L%s/libraries/usr/local/lib'%(WORKDIR,), '')
712 data = data.replace('-I%s/libraries/usr/local/include'%(WORKDIR,), '')
713 fp = open(path, 'w')
714 fp.write(data)
715 fp.close()
716
717 # Add symlinks in /usr/local/bin, using relative links
718 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
719 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
720 'Python.framework', 'Versions', version, 'bin')
721 if os.path.exists(usr_local_bin):
722 shutil.rmtree(usr_local_bin)
723 os.makedirs(usr_local_bin)
724 for fn in os.listdir(
725 os.path.join(frmDir, 'Versions', version, 'bin')):
726 os.symlink(os.path.join(to_framework, fn),
727 os.path.join(usr_local_bin, fn))
728
729 os.chdir(curdir)
730
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000731 # Remove the 'Current' link, that way we don't accidently mess with an already installed
732 # version of python
733 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework', 'Versions', 'Current'))
734
735
Thomas Wouters477c8d52006-05-27 19:21:47 +0000736
737
738def patchFile(inPath, outPath):
739 data = fileContents(inPath)
740 data = data.replace('$FULL_VERSION', getFullVersion())
741 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +0000742 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000743 data = data.replace('$ARCHITECTURES', "i386, ppc")
744 data = data.replace('$INSTALL_SIZE', installSize())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000745
746 # This one is not handy as a template variable
747 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000748 fp = open(outPath, 'wb')
749 fp.write(data)
750 fp.close()
751
752def patchScript(inPath, outPath):
753 data = fileContents(inPath)
754 data = data.replace('@PYVER@', getVersion())
755 fp = open(outPath, 'wb')
756 fp.write(data)
757 fp.close()
758 os.chmod(outPath, 0755)
759
760
761
762def packageFromRecipe(targetDir, recipe):
763 curdir = os.getcwd()
764 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000765 # The major version (such as 2.5) is included in the package name
766 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000767 # common.
768 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +0000769 srcdir = recipe.get('source')
770 pkgroot = recipe.get('topdir', srcdir)
771 postflight = recipe.get('postflight')
772 readme = textwrap.dedent(recipe['readme'])
773 isRequired = recipe.get('required', True)
774
Benjamin Peterson623918e2008-12-20 22:50:25 +0000775 print "- building package %s"%(pkgname,)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000776
777 # Substitute some variables
778 textvars = dict(
779 VER=getVersion(),
780 FULLVER=getFullVersion(),
781 )
782 readme = readme % textvars
783
784 if pkgroot is not None:
785 pkgroot = pkgroot % textvars
786 else:
787 pkgroot = '/'
788
789 if srcdir is not None:
790 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
791 srcdir = srcdir % textvars
792
793 if postflight is not None:
794 postflight = os.path.abspath(postflight)
795
796 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
797 os.makedirs(packageContents)
798
799 if srcdir is not None:
800 os.chdir(srcdir)
801 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
802 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
803 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
804
805 fn = os.path.join(packageContents, 'PkgInfo')
806 fp = open(fn, 'w')
807 fp.write('pmkrpkg1')
808 fp.close()
809
810 rsrcDir = os.path.join(packageContents, "Resources")
811 os.mkdir(rsrcDir)
812 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
813 fp.write(readme)
814 fp.close()
815
816 if postflight is not None:
817 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
818
819 vers = getFullVersion()
820 major, minor = map(int, getVersion().split('.', 2))
821 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000822 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
823 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
824 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000825 CFBundleShortVersionString=vers,
826 IFMajorVersion=major,
827 IFMinorVersion=minor,
828 IFPkgFormatVersion=0.10000000149011612,
829 IFPkgFlagAllowBackRev=False,
830 IFPkgFlagAuthorizationAction="RootAuthorization",
831 IFPkgFlagDefaultLocation=pkgroot,
832 IFPkgFlagFollowLinks=True,
833 IFPkgFlagInstallFat=True,
834 IFPkgFlagIsRequired=isRequired,
835 IFPkgFlagOverwritePermissions=False,
836 IFPkgFlagRelocatable=False,
837 IFPkgFlagRestartAction="NoRestart",
838 IFPkgFlagRootVolumeOnly=True,
839 IFPkgFlagUpdateInstalledLangauges=False,
840 )
841 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
842
843 pl = Plist(
844 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +0000845 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000846 IFPkgDescriptionVersion=vers,
847 )
848 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
849
850 finally:
851 os.chdir(curdir)
852
853
854def makeMpkgPlist(path):
855
856 vers = getFullVersion()
857 major, minor = map(int, getVersion().split('.', 2))
858
859 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000860 CFBundleGetInfoString="Python %s"%(vers,),
861 CFBundleIdentifier='org.python.Python',
862 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +0000863 CFBundleShortVersionString=vers,
864 IFMajorVersion=major,
865 IFMinorVersion=minor,
866 IFPkgFlagComponentDirectory="Contents/Packages",
867 IFPkgFlagPackageList=[
868 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000869 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren360e98c2008-12-30 14:16:51 +0000870 IFPkgFlagPackageSelection=item['selected'],
Thomas Wouters477c8d52006-05-27 19:21:47 +0000871 )
872 for item in PKG_RECIPES
873 ],
874 IFPkgFormatVersion=0.10000000149011612,
875 IFPkgFlagBackgroundScaling="proportional",
876 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000877 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000878 )
879
880 writePlist(pl, path)
881
882
883def buildInstaller():
884
885 # Zap all compiled files
886 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
887 for fn in filenames:
888 if fn.endswith('.pyc') or fn.endswith('.pyo'):
889 os.unlink(os.path.join(dirpath, fn))
890
891 outdir = os.path.join(WORKDIR, 'installer')
892 if os.path.exists(outdir):
893 shutil.rmtree(outdir)
894 os.mkdir(outdir)
895
Ronald Oussoren1943f862009-03-30 19:39:14 +0000896 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000897 pkgcontents = os.path.join(pkgroot, 'Packages')
898 os.makedirs(pkgcontents)
899 for recipe in PKG_RECIPES:
900 packageFromRecipe(pkgcontents, recipe)
901
902 rsrcDir = os.path.join(pkgroot, 'Resources')
903
904 fn = os.path.join(pkgroot, 'PkgInfo')
905 fp = open(fn, 'w')
906 fp.write('pmkrpkg1')
907 fp.close()
908
909 os.mkdir(rsrcDir)
910
911 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
912 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +0000913 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000914 IFPkgDescriptionVersion=getVersion(),
915 )
916
917 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
918 for fn in os.listdir('resources'):
919 if fn == '.svn': continue
920 if fn.endswith('.jpg'):
921 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
922 else:
923 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
924
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000925 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000926
927
928def installSize(clear=False, _saved=[]):
929 if clear:
930 del _saved[:]
931 if not _saved:
932 data = captureCommand("du -ks %s"%(
933 shellQuote(os.path.join(WORKDIR, '_root'))))
934 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
935 return _saved[0]
936
937
938def buildDMG():
939 """
Thomas Wouters89f507f2006-12-13 04:49:30 +0000940 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000941 """
942 outdir = os.path.join(WORKDIR, 'diskimage')
943 if os.path.exists(outdir):
944 shutil.rmtree(outdir)
945
946 imagepath = os.path.join(outdir,
947 'python-%s-macosx'%(getFullVersion(),))
948 if INCLUDE_TIMESTAMP:
949 imagepath = imagepath + '%04d-%02d-%02d'%(time.localtime()[:3])
950 imagepath = imagepath + '.dmg'
951
952 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000953 volname='Python %s'%(getFullVersion())
954 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
955 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +0000956 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +0000957 shellQuote(imagepath + ".tmp.dmg" )))
958
959
960 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
961 os.mkdir(os.path.join(WORKDIR, "mnt"))
962 runCommand("hdiutil attach %s -mountroot %s"%(
963 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
964
965 # Custom icon for the DMG, shown when the DMG is mounted.
966 shutil.copy("../Icons/Disk Image.icns",
967 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
968 runCommand("/Developer/Tools/SetFile -a C %s/"%(
969 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
970
971 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
972
973 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
974 runCommand("hdiutil convert %s -format UDZO -o %s"%(
975 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
976 setIcon(imagepath, "../Icons/Disk Image.icns")
977
978 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000979
980 return imagepath
981
982
983def setIcon(filePath, icnsPath):
984 """
985 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000986 """
Thomas Wouters477c8d52006-05-27 19:21:47 +0000987
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000988 toolPath = os.path.join(os.path.dirname(__file__), "seticon.app/Contents/MacOS/seticon")
989 dirPath = os.path.dirname(__file__)
990 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
991 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
992 # to connections to the window server.
993 if not os.path.exists('seticon.app/Contents/MacOS'):
994 os.makedirs('seticon.app/Contents/MacOS')
995 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
996 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000997
Ronald Oussoren207b4c22009-03-30 17:20:30 +0000998 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
999 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001000
1001def main():
1002 # First parse options and check if we can perform our work
1003 parseOptions()
1004 checkEnvironment()
1005
Ronald Oussoren1943f862009-03-30 19:39:14 +00001006 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Thomas Wouters477c8d52006-05-27 19:21:47 +00001007
1008 if os.path.exists(WORKDIR):
1009 shutil.rmtree(WORKDIR)
1010 os.mkdir(WORKDIR)
1011
1012 # Then build third-party libraries such as sleepycat DB4.
1013 buildLibraries()
1014
1015 # Now build python itself
1016 buildPython()
1017 buildPythonDocs()
1018 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001019 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussorenbc448662009-02-12 16:08:14 +00001020 patchScript("scripts/postflight.patch-profile", fn)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001021
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001022 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001023 getVersion(),))
1024 os.chmod(folder, 0755)
1025 setIcon(folder, "../Icons/Python Folder.icns")
1026
1027 # Create the installer
1028 buildInstaller()
1029
1030 # And copy the readme into the directory containing the installer
1031 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1032
1033 # Ditto for the license file.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001034 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001035
1036 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Benjamin Peterson623918e2008-12-20 22:50:25 +00001037 print >> fp, "# BUILD INFO"
1038 print >> fp, "# Date:", time.ctime()
1039 print >> fp, "# By:", pwd.getpwuid(os.getuid()).pw_gecos
Thomas Wouters477c8d52006-05-27 19:21:47 +00001040 fp.close()
1041
Thomas Wouters477c8d52006-05-27 19:21:47 +00001042
1043
1044 # And copy it to a DMG
1045 buildDMG()
1046
1047
1048if __name__ == "__main__":
1049 main()