blob: 46e715a62eb6fa5a81742caf336f906dbe6cc156 [file] [log] [blame]
Ned Deilye1c9794952013-01-29 00:07:46 -08001#!/usr/bin/env python
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00002"""
Ned Deily53c460d2011-01-30 01:43:40 +00003This script is used to build "official" universal installers on Mac OS X.
Ned Deilye1c9794952013-01-29 00:07:46 -08004It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
Ned Deily53c460d2011-01-30 01:43:40 +0000532-bit builds. 64-bit or four-way universal builds require at least
6OS X 10.5 and the 10.5 SDK.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00007
Ned Deily53c460d2011-01-30 01:43:40 +00008Please ensure that this script keeps working with Python 2.5, to avoid
9bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Sphinx,
10which is used to build the documentation, currently requires at least
Ned Deilyebd63dc2014-04-09 16:12:11 -070011Python 2.4. However, as of Python 3.4.1, Doc builds require an external
12sphinx-build and the current versions of Sphinx now require at least
13Python 2.6.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000014
Ned Deilye1c9794952013-01-29 00:07:46 -080015In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script
16requires an installed version of hg and a third-party version of
17Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5
18(for 10.6 or later) installed in /Library/Frameworks. When installed,
19the Python built by this script will attempt to dynamically link first to
20Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
21back to the ones in /System/Library/Framework. For the build, we recommend
22installing the most recent ActiveTcl 8.4 or 8.5 version.
23
2432-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5
25and the installation of additional components, such as a newer Python
Ned Deilyebd63dc2014-04-09 16:12:11 -070026(2.5 is needed for Python parser updates), hg, and for the documentation
27build either svn (pre-3.4.1) or sphinx-build (3.4.1 and later).
Ned Deilye1c9794952013-01-29 00:07:46 -080028
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000029Usage: see USAGE variable in the script.
30"""
Ned Deilye1c9794952013-01-29 00:07:46 -080031import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
32try:
33 import urllib2 as urllib_request
34except ImportError:
35 import urllib.request as urllib_request
36
37STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38 | stat.S_IRGRP | stat.S_IXGRP
39 | stat.S_IROTH | stat.S_IXOTH )
40
41STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
42 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
43 | stat.S_IROTH | stat.S_IXOTH )
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000044
Ronald Oussoren158ad592006-11-07 16:00:34 +000045INCLUDE_TIMESTAMP = 1
46VERBOSE = 1
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000047
48from plistlib import Plist
49
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000050try:
51 from plistlib import writePlist
52except ImportError:
53 # We're run using python2.3
54 def writePlist(plist, path):
55 plist.write(path)
56
57def shellQuote(value):
58 """
Ronald Oussorenaa560962006-11-07 15:53:38 +000059 Return the string value in a form that can safely be inserted into
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000060 a shell command.
61 """
62 return "'%s'"%(value.replace("'", "'\"'\"'"))
63
64def grepValue(fn, variable):
65 variable = variable + '='
66 for ln in open(fn, 'r'):
67 if ln.startswith(variable):
68 value = ln[len(variable):].strip()
69 return value[1:-1]
Ned Deilye1c9794952013-01-29 00:07:46 -080070 raise RuntimeError("Cannot find variable %s" % variable[:-1])
71
72_cache_getVersion = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000073
74def getVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080075 global _cache_getVersion
76 if _cache_getVersion is None:
77 _cache_getVersion = grepValue(
78 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
79 return _cache_getVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000080
Ned Deily53c460d2011-01-30 01:43:40 +000081def getVersionTuple():
82 return tuple([int(n) for n in getVersion().split('.')])
83
Ned Deilye1c9794952013-01-29 00:07:46 -080084def getVersionMajorMinor():
85 return tuple([int(n) for n in getVersion().split('.', 2)])
86
87_cache_getFullVersion = None
88
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000089def getFullVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080090 global _cache_getFullVersion
91 if _cache_getFullVersion is not None:
92 return _cache_getFullVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000093 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
94 for ln in open(fn):
95 if 'PY_VERSION' in ln:
Ned Deilye1c9794952013-01-29 00:07:46 -080096 _cache_getFullVersion = ln.split()[-1][1:-1]
97 return _cache_getFullVersion
98 raise RuntimeError("Cannot find full version??")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000099
Ronald Oussorenaa560962006-11-07 15:53:38 +0000100# The directory we'll use to create the build (will be erased and recreated)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000101WORKDIR = "/tmp/_py"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000102
Ronald Oussorenaa560962006-11-07 15:53:38 +0000103# The directory we'll use to store third-party sources. Set this to something
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000104# else if you don't want to re-fetch required libraries every time.
Ronald Oussoren158ad592006-11-07 16:00:34 +0000105DEPSRC = os.path.join(WORKDIR, 'third-party')
106DEPSRC = os.path.expanduser('~/Universal/other-sources')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000107
108# Location of the preferred SDK
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000109
110### There are some issues with the SDK selection below here,
111### The resulting binary doesn't work on all platforms that
112### it should. Always default to the 10.4u SDK until that
Ezio Melotti6d0f0f22013-08-26 01:31:30 +0300113### issue is resolved.
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000114###
115##if int(os.uname()[2].split('.')[0]) == 8:
116## # Explicitly use the 10.4u (universal) SDK when
117## # building on 10.4, the system headers are not
118## # useable for a universal build
119## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
120##else:
121## SDKPATH = "/"
122
123SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000124
Ronald Oussoren508282e2009-03-30 19:34:51 +0000125universal_opts_map = { '32-bit': ('i386', 'ppc',),
126 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000127 'intel': ('i386', 'x86_64'),
128 '3-way': ('ppc', 'i386', 'x86_64'),
129 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
130default_target_map = {
131 '64-bit': '10.5',
132 '3-way': '10.5',
133 'intel': '10.5',
134 'all': '10.5',
135}
Ronald Oussoren508282e2009-03-30 19:34:51 +0000136
137UNIVERSALOPTS = tuple(universal_opts_map.keys())
138
139UNIVERSALARCHS = '32-bit'
140
141ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000142
Ezio Melotti24b07bc2011-03-15 18:55:01 +0200143# Source directory (assume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000144SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000145 os.path.dirname(
146 os.path.dirname(
147 os.path.abspath(__file__
148 ))))
149
Ronald Oussoren508282e2009-03-30 19:34:51 +0000150# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
151DEPTARGET = '10.3'
152
Ned Deily1f70b872014-06-25 13:33:57 -0700153def getDeptargetTuple():
154 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
155
156def getTargetCompilers():
157 target_cc_map = {
Ned Deilye1c9794952013-01-29 00:07:46 -0800158 '10.3': ('gcc-4.0', 'g++-4.0'),
159 '10.4': ('gcc-4.0', 'g++-4.0'),
160 '10.5': ('gcc-4.2', 'g++-4.2'),
161 '10.6': ('gcc-4.2', 'g++-4.2'),
Ned Deily1f70b872014-06-25 13:33:57 -0700162 }
163 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000164
Ned Deily1f70b872014-06-25 13:33:57 -0700165CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000166
Ned Deily53c460d2011-01-30 01:43:40 +0000167PYTHON_3 = getVersionTuple() >= (3, 0)
168
Ronald Oussoren158ad592006-11-07 16:00:34 +0000169USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000170 Usage: build_python [options]
171
172 Options:
173 -? or -h: Show this message
174 -b DIR
175 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
176 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
177 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
178 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000179 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
180 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000181""")% globals()
182
Ned Deilye1c9794952013-01-29 00:07:46 -0800183# Dict of object file names with shared library names to check after building.
184# This is to ensure that we ended up dynamically linking with the shared
185# library paths and versions we expected. For example:
186# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
187# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
188# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
189EXPECTED_SHARED_LIBS = {}
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000190
191# Instructions for building libraries that are necessary for building a
192# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000193# [The recipes are defined here for convenience but instantiated later after
194# command line options have been processed.]
195def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000196 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000197
Ned Deily1f70b872014-06-25 13:33:57 -0700198 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deilye1c9794952013-01-29 00:07:46 -0800199
Ned Deilyebd63dc2014-04-09 16:12:11 -0700200# Disable for now
Ned Deily30101822014-11-14 18:53:59 -0800201 if False: # if getDeptargetTuple() > (10, 5):
Ned Deily0203a802013-10-25 00:40:07 -0700202 result.extend([
203 dict(
204 name="Tcl 8.5.15",
205 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tcl8.5.15-src.tar.gz",
206 checksum='f3df162f92c69b254079c4d0af7a690f',
207 buildDir="unix",
208 configure_pre=[
209 '--enable-shared',
210 '--enable-threads',
211 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
212 ],
213 useLDFlags=False,
214 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
215 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
216 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
217 },
218 ),
219 dict(
220 name="Tk 8.5.15",
221 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tk8.5.15-src.tar.gz",
222 checksum='55b8e33f903210a4e1c8bce0f820657f',
Ned Deilya6cbff02013-10-27 19:47:23 -0700223 patches=[
224 "issue19373_tk_8_5_15_source.patch",
225 ],
Ned Deily0203a802013-10-25 00:40:07 -0700226 buildDir="unix",
227 configure_pre=[
228 '--enable-aqua',
229 '--enable-shared',
230 '--enable-threads',
231 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
232 ],
233 useLDFlags=False,
234 install='make TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s TK_LIBRARY=%(TK_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
235 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
236 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
237 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.5'%(getVersion())),
238 },
239 ),
240 ])
241
Ned Deily30101822014-11-14 18:53:59 -0800242 if PYTHON_3:
Ned Deilye1c9794952013-01-29 00:07:46 -0800243 result.extend([
244 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700245 name="XZ 5.0.5",
246 url="http://tukaani.org/xz/xz-5.0.5.tar.gz",
247 checksum='19d924e066b6fff0bc9d1981b4e53196',
Ned Deilye1c9794952013-01-29 00:07:46 -0800248 configure_pre=[
249 '--disable-dependency-tracking',
250 ]
251 ),
252 ])
253
254 result.extend([
255 dict(
256 name="NCurses 5.9",
257 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
258 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
259 configure_pre=[
260 "--enable-widec",
261 "--without-cxx",
262 "--without-cxx-binding",
263 "--without-ada",
264 "--without-curses-h",
265 "--enable-shared",
266 "--with-shared",
267 "--without-debug",
268 "--without-normal",
269 "--without-tests",
270 "--without-manpages",
271 "--datadir=/usr/share",
272 "--sysconfdir=/etc",
273 "--sharedstatedir=/usr/com",
274 "--with-terminfo-dirs=/usr/share/terminfo",
275 "--with-default-terminfo-dir=/usr/share/terminfo",
276 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
277 ],
278 patchscripts=[
279 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
280 "f54bf02a349f96a7c4f0d00922f3a0d4"),
281 ],
282 useLDFlags=False,
283 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
284 shellQuote(os.path.join(WORKDIR, 'libraries')),
285 shellQuote(os.path.join(WORKDIR, 'libraries')),
286 getVersion(),
287 ),
288 ),
289 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700290 name="SQLite 3.8.3.1",
291 url="http://www.sqlite.org/2014/sqlite-autoconf-3080301.tar.gz",
292 checksum='509ff98d8dc9729b618b7e96612079c6',
Ned Deilye1c9794952013-01-29 00:07:46 -0800293 extra_cflags=('-Os '
294 '-DSQLITE_ENABLE_FTS4 '
295 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
296 '-DSQLITE_ENABLE_RTREE '
297 '-DSQLITE_TCL=0 '
298 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
299 configure_pre=[
300 '--enable-threadsafe',
301 '--enable-shared=no',
302 '--enable-static=yes',
303 '--disable-readline',
304 '--disable-dependency-tracking',
305 ]
306 ),
307 ])
308
Ned Deily1f70b872014-06-25 13:33:57 -0700309 if getDeptargetTuple() < (10, 5):
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000310 result.extend([
311 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000312 name="Bzip2 1.0.6",
313 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
314 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000315 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800316 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
317 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000318 shellQuote(os.path.join(WORKDIR, 'libraries')),
319 ' -arch '.join(ARCHLIST),
320 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000321 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000322 ),
323 dict(
324 name="ZLib 1.2.3",
325 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
326 checksum='debc62758716a169df9f62e6ab2bc634',
327 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800328 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
329 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000330 shellQuote(os.path.join(WORKDIR, 'libraries')),
331 ' -arch '.join(ARCHLIST),
332 SDKPATH,
333 ),
334 ),
335 dict(
336 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000337 name="GNU Readline 6.1.2",
338 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
339 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000340 patchlevel='0',
341 patches=[
342 # The readline maintainers don't do actual micro releases, but
343 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800344 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
345 'c642f2e84d820884b0bf9fd176bc6c3f'),
346 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
347 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000348 ]
349 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000350 ])
351
Ned Deily53c460d2011-01-30 01:43:40 +0000352 if not PYTHON_3:
353 result.extend([
354 dict(
355 name="Sleepycat DB 4.7.25",
356 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
357 checksum='ec2b87e833779681a0c3a814aa71359e',
358 buildDir="build_unix",
359 configure="../dist/configure",
360 configure_pre=[
361 '--includedir=/usr/local/include/db4',
362 ]
363 ),
364 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000365
366 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000367
368
369# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000370def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000371 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000372 result = [
373 dict(
374 name="PythonFramework",
375 long_name="Python Framework",
376 source="/Library/Frameworks/Python.framework",
377 readme="""\
378 This package installs Python.framework, that is the python
379 interpreter and the standard library. This also includes Python
380 wrappers for lots of Mac OS X API's.
381 """,
382 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000383 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000384 ),
385 dict(
386 name="PythonApplications",
387 long_name="GUI Applications",
388 source="/Applications/Python %(VER)s",
389 readme="""\
390 This package installs IDLE (an interactive Python IDE),
391 Python Launcher and Build Applet (create application bundles
392 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000393
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000394 It also installs a number of examples and demos.
395 """,
396 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000397 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000398 ),
399 dict(
400 name="PythonUnixTools",
401 long_name="UNIX command-line tools",
402 source="/usr/local/bin",
403 readme="""\
404 This package installs the unix tools in /usr/local/bin for
405 compatibility with older releases of Python. This package
406 is not necessary to use Python.
407 """,
408 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000409 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000410 ),
411 dict(
412 name="PythonDocumentation",
413 long_name="Python Documentation",
414 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
415 source="/pydocs",
416 readme="""\
417 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800418 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000419 """,
420 postflight="scripts/postflight.documentation",
421 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000422 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000423 ),
424 dict(
425 name="PythonProfileChanges",
426 long_name="Shell profile updater",
427 readme="""\
428 This packages updates your shell profile to make sure that
429 the Python tools are found by your shell in preference of
430 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000431
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000432 If you don't install this package you'll have to add
433 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
434 to your PATH by hand.
435 """,
436 postflight="scripts/postflight.patch-profile",
437 topdir="/Library/Frameworks/Python.framework",
438 source="/empty-dir",
439 required=False,
Ned Deily30101822014-11-14 18:53:59 -0800440 selected='selected',
441 ),
442 dict(
443 name="PythonInstallPip",
444 long_name="Install or upgrade pip",
445 readme="""\
446 This package installs (or upgrades from an earlier version)
447 pip, a tool for installing and managing Python packages.
448 """,
449 postflight="scripts/postflight.ensurepip",
450 topdir="/Library/Frameworks/Python.framework",
451 source="/empty-dir",
452 required=False,
453 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000454 ),
455 ]
456
Ned Deily1f70b872014-06-25 13:33:57 -0700457 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000458 result.append(
459 dict(
460 name="PythonSystemFixes",
461 long_name="Fix system Python",
462 readme="""\
463 This package updates the system python installation on
464 Mac OS X 10.3 to ensure that you can build new python extensions
465 using that copy of python after installing this version.
466 """,
467 postflight="../Tools/fixapplepython23.py",
468 topdir="/Library/Frameworks/Python.framework",
469 source="/empty-dir",
470 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000471 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000472 )
473 )
Ned Deilyebd63dc2014-04-09 16:12:11 -0700474
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000475 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000476
477def fatal(msg):
478 """
479 A fatal error, bail out.
480 """
481 sys.stderr.write('FATAL: ')
482 sys.stderr.write(msg)
483 sys.stderr.write('\n')
484 sys.exit(1)
485
486def fileContents(fn):
487 """
488 Return the contents of the named file
489 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800490 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000491
492def runCommand(commandline):
493 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200494 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000495 unless the command fails.
496 """
497 fd = os.popen(commandline, 'r')
498 data = fd.read()
499 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000500 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000501 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800502 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000503
504 if VERBOSE:
505 sys.stdout.write(data); sys.stdout.flush()
506
507def captureCommand(commandline):
508 fd = os.popen(commandline, 'r')
509 data = fd.read()
510 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000511 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000512 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800513 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000514
515 return data
516
Ronald Oussoren287128a2010-04-18 14:01:05 +0000517def getTclTkVersion(configfile, versionline):
518 """
519 search Tcl or Tk configuration file for version line
520 """
521 try:
522 f = open(configfile, "r")
523 except:
524 fatal("Framework configuration file not found: %s" % configfile)
525
526 for l in f:
527 if l.startswith(versionline):
528 f.close()
529 return l
530
531 fatal("Version variable %s not found in framework configuration file: %s"
532 % (versionline, configfile))
533
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000534def checkEnvironment():
535 """
536 Check that we're running on a supported system.
537 """
538
Ned Deily53c460d2011-01-30 01:43:40 +0000539 if sys.version_info[0:2] < (2, 4):
540 fatal("This script must be run with Python 2.4 or later")
541
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000542 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000543 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000544
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000545 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000546 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000547
548 if not os.path.exists(SDKPATH):
549 fatal("Please install the latest version of Xcode and the %s SDK"%(
550 os.path.basename(SDKPATH[:-4])))
551
Ronald Oussoren287128a2010-04-18 14:01:05 +0000552 # Because we only support dynamic load of only one major/minor version of
553 # Tcl/Tk, ensure:
554 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800555 # higher than the Apple-supplied system version in
556 # SDKROOT/System/Library/Frameworks
557 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
558 # in) SDKROOT/Library/Frameworks with the same version as the system
559 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000560
Ned Deilye1c9794952013-01-29 00:07:46 -0800561 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000562 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800563 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000564 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800565 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000566 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800567 frameworks[framework] = os.readlink(sysfw)
568 if not os.path.exists(libfw):
569 fatal("Please install a link to a current %s %s as %s so "
570 "the user can override the system framework."
571 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000572 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000573 fatal("Version of %s must match %s" % (libfw, sysfw) )
574 if os.path.exists(usrfw):
575 fatal("Please rename %s to avoid possible dynamic load issues."
576 % usrfw)
577
Ned Deilye1c9794952013-01-29 00:07:46 -0800578 if frameworks['Tcl'] != frameworks['Tk']:
579 fatal("The Tcl and Tk frameworks are not the same version.")
580
581 # add files to check after build
582 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
583 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
584 % frameworks['Tcl'],
585 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
586 % frameworks['Tk'],
587 ]
588
Ronald Oussoren287128a2010-04-18 14:01:05 +0000589 # Remove inherited environment variables which might influence build
590 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
591 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
592 for ev in list(os.environ):
593 for prefix in environ_var_prefixes:
594 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800595 print("INFO: deleting environment variable %s=%s" % (
596 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000597 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000598
Ned Deilye1c9794952013-01-29 00:07:46 -0800599 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
600 if 'SDK_TOOLS_BIN' in os.environ:
601 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
602 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
603 # add its fixed location here if it exists
604 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
605 if os.path.isdir(OLD_DEVELOPER_TOOLS):
606 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
607 os.environ['PATH'] = base_path
608 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyebd63dc2014-04-09 16:12:11 -0700609 # Ensure ws have access to hg and to sphinx-build.
610 # You may have to create links in /usr/bin for them.
611 runCommand('hg --version')
Ned Deily5ceffa12014-09-05 15:51:54 -0700612 runCommand('sphinx-build --version')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000613
Ronald Oussoren158ad592006-11-07 16:00:34 +0000614def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000615 """
616 Parse arguments and update global settings.
617 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000618 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800619 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000620
621 if args is None:
622 args = sys.argv[1:]
623
624 try:
625 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000626 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
627 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800628 except getopt.GetoptError:
629 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000630 sys.exit(1)
631
632 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800633 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000634 sys.exit(1)
635
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000636 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000637 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000638 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800639 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000640 sys.exit(0)
641
642 elif k in ('-d', '--build-dir'):
643 WORKDIR=v
644
645 elif k in ('--third-party',):
646 DEPSRC=v
647
648 elif k in ('--sdk-path',):
649 SDKPATH=v
650
651 elif k in ('--src-dir',):
652 SRCDIR=v
653
Ronald Oussoren508282e2009-03-30 19:34:51 +0000654 elif k in ('--dep-target', ):
655 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000656 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000657
658 elif k in ('--universal-archs', ):
659 if v in UNIVERSALOPTS:
660 UNIVERSALARCHS = v
661 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000662 if deptarget is None:
663 # Select alternate default deployment
664 # target
665 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000666 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800667 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000668
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000669 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800670 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000671
672 SRCDIR=os.path.abspath(SRCDIR)
673 WORKDIR=os.path.abspath(WORKDIR)
674 SDKPATH=os.path.abspath(SDKPATH)
675 DEPSRC=os.path.abspath(DEPSRC)
676
Ned Deily1f70b872014-06-25 13:33:57 -0700677 CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000678
Ned Deilye1c9794952013-01-29 00:07:46 -0800679 print("Settings:")
680 print(" * Source directory:", SRCDIR)
681 print(" * Build directory: ", WORKDIR)
682 print(" * SDK location: ", SDKPATH)
683 print(" * Third-party source:", DEPSRC)
684 print(" * Deployment target:", DEPTARGET)
685 print(" * Universal architectures:", ARCHLIST)
686 print(" * C compiler:", CC)
687 print(" * C++ compiler:", CXX)
688 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000689
690
691
692
693def extractArchive(builddir, archiveName):
694 """
695 Extract a source archive into 'builddir'. Returns the path of the
696 extracted archive.
697
698 XXX: This function assumes that archives contain a toplevel directory
699 that is has the same name as the basename of the archive. This is
Ned Deily0203a802013-10-25 00:40:07 -0700700 safe enough for almost anything we use. Unfortunately, it does not
701 work for current Tcl and Tk source releases where the basename of
702 the archive ends with "-src" but the uncompressed directory does not.
703 For now, just special case Tcl and Tk tar.gz downloads.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000704 """
705 curdir = os.getcwd()
706 try:
707 os.chdir(builddir)
708 if archiveName.endswith('.tar.gz'):
709 retval = os.path.basename(archiveName[:-7])
Ned Deily0203a802013-10-25 00:40:07 -0700710 if ((retval.startswith('tcl') or retval.startswith('tk'))
711 and retval.endswith('-src')):
712 retval = retval[:-4]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000713 if os.path.exists(retval):
714 shutil.rmtree(retval)
715 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
716
717 elif archiveName.endswith('.tar.bz2'):
718 retval = os.path.basename(archiveName[:-8])
719 if os.path.exists(retval):
720 shutil.rmtree(retval)
721 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
722
723 elif archiveName.endswith('.tar'):
724 retval = os.path.basename(archiveName[:-4])
725 if os.path.exists(retval):
726 shutil.rmtree(retval)
727 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
728
729 elif archiveName.endswith('.zip'):
730 retval = os.path.basename(archiveName[:-4])
731 if os.path.exists(retval):
732 shutil.rmtree(retval)
733 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
734
735 data = fp.read()
736 xit = fp.close()
737 if xit is not None:
738 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800739 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000740
741 return os.path.join(builddir, retval)
742
743 finally:
744 os.chdir(curdir)
745
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000746def downloadURL(url, fname):
747 """
748 Download the contents of the url into the file.
749 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800750 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000751 fpOut = open(fname, 'wb')
752 block = fpIn.read(10240)
753 try:
754 while block:
755 fpOut.write(block)
756 block = fpIn.read(10240)
757 fpIn.close()
758 fpOut.close()
759 except:
760 try:
761 os.unlink(fname)
762 except:
763 pass
764
Ned Deilye1c9794952013-01-29 00:07:46 -0800765def verifyThirdPartyFile(url, checksum, fname):
766 """
767 Download file from url to filename fname if it does not already exist.
768 Abort if file contents does not match supplied md5 checksum.
769 """
770 name = os.path.basename(fname)
771 if os.path.exists(fname):
772 print("Using local copy of %s"%(name,))
773 else:
774 print("Did not find local copy of %s"%(name,))
775 print("Downloading %s"%(name,))
776 downloadURL(url, fname)
777 print("Archive for %s stored as %s"%(name, fname))
778 if os.system(
779 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
780 % (shellQuote(fname), checksum) ):
781 fatal('MD5 checksum mismatch for file %s' % fname)
782
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000783def buildRecipe(recipe, basedir, archList):
784 """
785 Build software using a recipe. This function does the
786 'configure;make;make install' dance for C software, with a possibility
787 to customize this process, basically a poor-mans DarwinPorts.
788 """
789 curdir = os.getcwd()
790
791 name = recipe['name']
792 url = recipe['url']
793 configure = recipe.get('configure', './configure')
794 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
795 shellQuote(basedir)))
796
797 archiveName = os.path.split(url)[-1]
798 sourceArchive = os.path.join(DEPSRC, archiveName)
799
800 if not os.path.exists(DEPSRC):
801 os.mkdir(DEPSRC)
802
Ned Deilye1c9794952013-01-29 00:07:46 -0800803 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
804 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000805 buildDir=os.path.join(WORKDIR, '_bld')
806 if not os.path.exists(buildDir):
807 os.mkdir(buildDir)
808
809 workDir = extractArchive(buildDir, sourceArchive)
810 os.chdir(workDir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000811
Ned Deilye1c9794952013-01-29 00:07:46 -0800812 for patch in recipe.get('patches', ()):
813 if isinstance(patch, tuple):
814 url, checksum = patch
815 fn = os.path.join(DEPSRC, os.path.basename(url))
816 verifyThirdPartyFile(url, checksum, fn)
817 else:
818 # patch is a file in the source directory
819 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000820 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
821 shellQuote(fn),))
822
Ned Deilye1c9794952013-01-29 00:07:46 -0800823 for patchscript in recipe.get('patchscripts', ()):
824 if isinstance(patchscript, tuple):
825 url, checksum = patchscript
826 fn = os.path.join(DEPSRC, os.path.basename(url))
827 verifyThirdPartyFile(url, checksum, fn)
828 else:
829 # patch is a file in the source directory
830 fn = os.path.join(curdir, patchscript)
831 if fn.endswith('.bz2'):
832 runCommand('bunzip2 -fk %s' % shellQuote(fn))
833 fn = fn[:-4]
834 runCommand('sh %s' % shellQuote(fn))
835 os.unlink(fn)
836
Ned Deilya6cbff02013-10-27 19:47:23 -0700837 if 'buildDir' in recipe:
838 os.chdir(recipe['buildDir'])
839
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000840 if configure is not None:
841 configure_args = [
842 "--prefix=/usr/local",
843 "--enable-static",
844 "--disable-shared",
845 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
846 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000847
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000848 if 'configure_pre' in recipe:
849 args = list(recipe['configure_pre'])
850 if '--disable-static' in args:
851 configure_args.remove('--enable-static')
852 if '--enable-shared' in args:
853 configure_args.remove('--disable-shared')
854 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000855
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000856 if recipe.get('useLDFlags', 1):
857 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800858 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
859 "-I%s/usr/local/include"%(
860 recipe.get('extra_cflags', ''),
861 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000862 ' -arch '.join(archList),
863 shellQuote(SDKPATH)[1:-1],
864 shellQuote(basedir)[1:-1],),
Ned Deilyc177b1c2014-04-09 16:13:46 -0700865 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deilye1c9794952013-01-29 00:07:46 -0800866 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000867 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000868 shellQuote(basedir)[1:-1],
869 ' -arch '.join(archList)),
870 ])
871 else:
872 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -0800873 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
874 "-I%s/usr/local/include"%(
875 recipe.get('extra_cflags', ''),
876 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000877 ' -arch '.join(archList),
878 shellQuote(SDKPATH)[1:-1],
879 shellQuote(basedir)[1:-1],),
880 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000881
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000882 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -0800883 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000884
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000885 configure_args.insert(0, configure)
886 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000887
Ned Deilye1c9794952013-01-29 00:07:46 -0800888 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000889 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000890
Ned Deilye1c9794952013-01-29 00:07:46 -0800891 print("Running install for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000892 runCommand('{ ' + install + ' ;} 2>&1')
893
Ned Deilye1c9794952013-01-29 00:07:46 -0800894 print("Done %s"%(name,))
895 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000896
897 os.chdir(curdir)
898
899def buildLibraries():
900 """
901 Build our dependencies into $WORKDIR/libraries/usr/local
902 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800903 print("")
904 print("Building required libraries")
905 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000906 universal = os.path.join(WORKDIR, 'libraries')
907 os.mkdir(universal)
908 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
909 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
910
Ronald Oussoren508282e2009-03-30 19:34:51 +0000911 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000912 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000913
914
915
916def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000917 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000918 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -0800919 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000920 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000921 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000922 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000923 curDir = os.getcwd()
924 os.chdir(buildDir)
Ned Deily5ceffa12014-09-05 15:51:54 -0700925 # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9
926 runCommand('make clean')
927 # Assume sphinx-build is on our PATH, checked in checkEnvironment
928 runCommand('make html')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +0000929 os.chdir(curDir)
930 if not os.path.exists(docdir):
931 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000932 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000933
934
935def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -0800936 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000937
938 buildDir = os.path.join(WORKDIR, '_bld', 'python')
939 rootDir = os.path.join(WORKDIR, '_root')
940
941 if os.path.exists(buildDir):
942 shutil.rmtree(buildDir)
943 if os.path.exists(rootDir):
944 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +0000945 os.makedirs(buildDir)
946 os.makedirs(rootDir)
947 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000948 curdir = os.getcwd()
949 os.chdir(buildDir)
950
951 # Not sure if this is still needed, the original build script
952 # claims that parts of the install assume python.exe exists.
953 os.symlink('python', os.path.join(buildDir, 'python.exe'))
954
955 # Extract the version from the configure file, needed to calculate
956 # several paths.
957 version = getVersion()
958
Ronald Oussoren008af852009-03-30 20:02:08 +0000959 # Since the extra libs are not in their installed framework location
960 # during the build, augment the library path so that the interpreter
961 # will find them during its extension import sanity checks.
962 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
963 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -0800964 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +0000965 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
966 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +0000967 "%s "
Ned Deilyebd63dc2014-04-09 16:12:11 -0700968 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +0000969 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deilyf84b5312013-10-25 00:44:46 -0700970 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +0000971 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
972 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +0000973 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deily30101822014-11-14 18:53:59 -0800974 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +0000975 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000976 shellQuote(WORKDIR)[1:-1]))
977
Ned Deilyfbb60d52014-05-22 15:27:01 -0700978 print("Running make touch")
979 runCommand("make touch")
980
Ned Deilye1c9794952013-01-29 00:07:46 -0800981 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000982 runCommand("make")
983
Ned Deilye1c9794952013-01-29 00:07:46 -0800984 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +0000985 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000986 shellQuote(rootDir)))
987
Ned Deilye1c9794952013-01-29 00:07:46 -0800988 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000989 runCommand("make frameworkinstallextras DESTDIR=%s"%(
990 shellQuote(rootDir)))
991
Ronald Oussoren008af852009-03-30 20:02:08 +0000992 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -0800993 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000994 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
995 runCommand("mv %s/* %s"%(
996 shellQuote(os.path.join(
997 WORKDIR, 'libraries', 'Library', 'Frameworks',
998 'Python.framework', 'Versions', getVersion(),
999 'lib')),
1000 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1001 'Python.framework', 'Versions', getVersion(),
1002 'lib'))))
1003
Ned Deily70f213a2013-10-26 03:16:06 -07001004 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1005 'Python.framework', 'Versions',
1006 version, 'lib', 'python%s'%(version,))
1007
Ned Deilye1c9794952013-01-29 00:07:46 -08001008 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001009 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001010 gid = grp.getgrnam('admin').gr_gid
1011
Ned Deilye1c9794952013-01-29 00:07:46 -08001012 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001013 for dirpath, dirnames, filenames in os.walk(frmDir):
1014 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -08001015 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001016 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +00001017
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001018 for fn in filenames:
1019 if os.path.islink(fn):
1020 continue
1021
1022 # "chmod g+w $fn"
1023 p = os.path.join(dirpath, fn)
1024 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001025 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1026 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001027
Ned Deilye1c9794952013-01-29 00:07:46 -08001028 if fn in EXPECTED_SHARED_LIBS:
1029 # check to see that this file was linked with the
1030 # expected library path and version
1031 data = captureCommand("otool -L %s" % shellQuote(p))
1032 for sl in EXPECTED_SHARED_LIBS[fn]:
1033 if ("\t%s " % sl) not in data:
1034 print("Expected shared lib %s was not linked with %s"
1035 % (sl, p))
1036 shared_lib_error = True
1037
1038 if shared_lib_error:
1039 fatal("Unexpected shared library errors.")
1040
Ned Deily53c460d2011-01-30 01:43:40 +00001041 if PYTHON_3:
1042 LDVERSION=None
1043 VERSION=None
1044 ABIFLAGS=None
1045
1046 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
1047 for ln in fp:
1048 if ln.startswith('VERSION='):
1049 VERSION=ln.split()[1]
1050 if ln.startswith('ABIFLAGS='):
1051 ABIFLAGS=ln.split()[1]
1052 if ln.startswith('LDVERSION='):
1053 LDVERSION=ln.split()[1]
1054 fp.close()
1055
1056 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1057 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1058 config_suffix = '-' + LDVERSION
1059 else:
1060 config_suffix = '' # Python 2.x
1061
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001062 # We added some directories to the search path during the configure
1063 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -08001064 # the end-users system. Also remove the directories from _sysconfigdata.py
1065 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001066
Ned Deilye6ef7022013-10-25 00:46:59 -07001067 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1068 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1069
Ned Deilye6ef7022013-10-25 00:46:59 -07001070 # fix Makefile
1071 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1072 fp = open(path, 'r')
1073 data = fp.read()
1074 fp.close()
1075
1076 for p in (include_path, lib_path):
1077 data = data.replace(" " + p, '')
1078 data = data.replace(p + " ", '')
1079
1080 fp = open(path, 'w')
1081 fp.write(data)
1082 fp.close()
1083
1084 # fix _sysconfigdata if it exists
1085 #
1086 # TODO: make this more robust! test_sysconfig_module of
1087 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1088 # the output from get_config_var in both sysconfig and
1089 # distutils.sysconfig is exactly the same for both CFLAGS and
1090 # LDFLAGS. The fixing up is now complicated by the pretty
1091 # printing in _sysconfigdata.py. Also, we are using the
1092 # pprint from the Python running the installer build which
1093 # may not cosmetically format the same as the pprint in the Python
1094 # being built (and which is used to originally generate
1095 # _sysconfigdata.py).
1096
1097 import pprint
1098 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1099 if os.path.exists(path):
Ned Deilye1c9794952013-01-29 00:07:46 -08001100 fp = open(path, 'r')
1101 data = fp.read()
1102 fp.close()
Ned Deilye6ef7022013-10-25 00:46:59 -07001103 # create build_time_vars dict
1104 exec(data)
1105 vars = {}
1106 for k, v in build_time_vars.items():
1107 if type(v) == type(''):
1108 for p in (include_path, lib_path):
1109 v = v.replace(' ' + p, '')
1110 v = v.replace(p + ' ', '')
1111 vars[k] = v
Ned Deilye1c9794952013-01-29 00:07:46 -08001112
Ned Deilye1c9794952013-01-29 00:07:46 -08001113 fp = open(path, 'w')
Ned Deilye6ef7022013-10-25 00:46:59 -07001114 # duplicated from sysconfig._generate_posix_vars()
1115 fp.write('# system configuration generated and used by'
1116 ' the sysconfig module\n')
1117 fp.write('build_time_vars = ')
1118 pprint.pprint(vars, stream=fp)
Ned Deilye1c9794952013-01-29 00:07:46 -08001119 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001120
1121 # Add symlinks in /usr/local/bin, using relative links
1122 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1123 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1124 'Python.framework', 'Versions', version, 'bin')
1125 if os.path.exists(usr_local_bin):
1126 shutil.rmtree(usr_local_bin)
1127 os.makedirs(usr_local_bin)
1128 for fn in os.listdir(
1129 os.path.join(frmDir, 'Versions', version, 'bin')):
1130 os.symlink(os.path.join(to_framework, fn),
1131 os.path.join(usr_local_bin, fn))
1132
1133 os.chdir(curdir)
1134
Ned Deily53c460d2011-01-30 01:43:40 +00001135 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001136 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001137 # with an already installed version of python 2
1138 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1139 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001140
1141def patchFile(inPath, outPath):
1142 data = fileContents(inPath)
1143 data = data.replace('$FULL_VERSION', getFullVersion())
1144 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001145 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001146 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001147 data = data.replace('$INSTALL_SIZE', installSize())
Ronald Oussorenc5555542006-06-11 20:24:45 +00001148
1149 # This one is not handy as a template variable
1150 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001151 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001152 fp.write(data)
1153 fp.close()
1154
1155def patchScript(inPath, outPath):
Ned Deily30101822014-11-14 18:53:59 -08001156 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001157 data = fileContents(inPath)
Ned Deily30101822014-11-14 18:53:59 -08001158 data = data.replace('@PYMAJOR@', str(major))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001159 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001160 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001161 fp.write(data)
1162 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001163 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001164
1165
1166
1167def packageFromRecipe(targetDir, recipe):
1168 curdir = os.getcwd()
1169 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001170 # The major version (such as 2.5) is included in the package name
1171 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001172 # common.
1173 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001174 srcdir = recipe.get('source')
1175 pkgroot = recipe.get('topdir', srcdir)
1176 postflight = recipe.get('postflight')
1177 readme = textwrap.dedent(recipe['readme'])
1178 isRequired = recipe.get('required', True)
1179
Ned Deilye1c9794952013-01-29 00:07:46 -08001180 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001181
1182 # Substitute some variables
1183 textvars = dict(
1184 VER=getVersion(),
1185 FULLVER=getFullVersion(),
1186 )
1187 readme = readme % textvars
1188
1189 if pkgroot is not None:
1190 pkgroot = pkgroot % textvars
1191 else:
1192 pkgroot = '/'
1193
1194 if srcdir is not None:
1195 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1196 srcdir = srcdir % textvars
1197
1198 if postflight is not None:
1199 postflight = os.path.abspath(postflight)
1200
1201 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1202 os.makedirs(packageContents)
1203
1204 if srcdir is not None:
1205 os.chdir(srcdir)
1206 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1207 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1208 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1209
1210 fn = os.path.join(packageContents, 'PkgInfo')
1211 fp = open(fn, 'w')
1212 fp.write('pmkrpkg1')
1213 fp.close()
1214
1215 rsrcDir = os.path.join(packageContents, "Resources")
1216 os.mkdir(rsrcDir)
1217 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1218 fp.write(readme)
1219 fp.close()
1220
1221 if postflight is not None:
1222 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1223
1224 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001225 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001226 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001227 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1228 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1229 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001230 CFBundleShortVersionString=vers,
1231 IFMajorVersion=major,
1232 IFMinorVersion=minor,
1233 IFPkgFormatVersion=0.10000000149011612,
1234 IFPkgFlagAllowBackRev=False,
1235 IFPkgFlagAuthorizationAction="RootAuthorization",
1236 IFPkgFlagDefaultLocation=pkgroot,
1237 IFPkgFlagFollowLinks=True,
1238 IFPkgFlagInstallFat=True,
1239 IFPkgFlagIsRequired=isRequired,
1240 IFPkgFlagOverwritePermissions=False,
1241 IFPkgFlagRelocatable=False,
1242 IFPkgFlagRestartAction="NoRestart",
1243 IFPkgFlagRootVolumeOnly=True,
1244 IFPkgFlagUpdateInstalledLangauges=False,
1245 )
1246 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1247
1248 pl = Plist(
1249 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001250 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001251 IFPkgDescriptionVersion=vers,
1252 )
1253 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1254
1255 finally:
1256 os.chdir(curdir)
1257
1258
1259def makeMpkgPlist(path):
1260
1261 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001262 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001263
1264 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001265 CFBundleGetInfoString="Python %s"%(vers,),
1266 CFBundleIdentifier='org.python.Python',
1267 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001268 CFBundleShortVersionString=vers,
1269 IFMajorVersion=major,
1270 IFMinorVersion=minor,
1271 IFPkgFlagComponentDirectory="Contents/Packages",
1272 IFPkgFlagPackageList=[
1273 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001274 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001275 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001276 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001277 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001278 ],
1279 IFPkgFormatVersion=0.10000000149011612,
1280 IFPkgFlagBackgroundScaling="proportional",
1281 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001282 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001283 )
1284
1285 writePlist(pl, path)
1286
1287
1288def buildInstaller():
1289
1290 # Zap all compiled files
1291 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1292 for fn in filenames:
1293 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1294 os.unlink(os.path.join(dirpath, fn))
1295
1296 outdir = os.path.join(WORKDIR, 'installer')
1297 if os.path.exists(outdir):
1298 shutil.rmtree(outdir)
1299 os.mkdir(outdir)
1300
Ronald Oussoren508282e2009-03-30 19:34:51 +00001301 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001302 pkgcontents = os.path.join(pkgroot, 'Packages')
1303 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001304 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001305 packageFromRecipe(pkgcontents, recipe)
1306
1307 rsrcDir = os.path.join(pkgroot, 'Resources')
1308
1309 fn = os.path.join(pkgroot, 'PkgInfo')
1310 fp = open(fn, 'w')
1311 fp.write('pmkrpkg1')
1312 fp.close()
1313
1314 os.mkdir(rsrcDir)
1315
1316 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1317 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001318 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001319 IFPkgDescriptionVersion=getVersion(),
1320 )
1321
1322 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1323 for fn in os.listdir('resources'):
1324 if fn == '.svn': continue
1325 if fn.endswith('.jpg'):
1326 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1327 else:
1328 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1329
Ronald Oussorenc5555542006-06-11 20:24:45 +00001330 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001331
1332
1333def installSize(clear=False, _saved=[]):
1334 if clear:
1335 del _saved[:]
1336 if not _saved:
1337 data = captureCommand("du -ks %s"%(
1338 shellQuote(os.path.join(WORKDIR, '_root'))))
1339 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1340 return _saved[0]
1341
1342
1343def buildDMG():
1344 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001345 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001346 """
1347 outdir = os.path.join(WORKDIR, 'diskimage')
1348 if os.path.exists(outdir):
1349 shutil.rmtree(outdir)
1350
1351 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001352 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001353 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001354 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001355 imagepath = imagepath + '.dmg'
1356
1357 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001358 volname='Python %s'%(getFullVersion())
1359 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1360 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001361 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001362 shellQuote(imagepath + ".tmp.dmg" )))
1363
1364
1365 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1366 os.mkdir(os.path.join(WORKDIR, "mnt"))
1367 runCommand("hdiutil attach %s -mountroot %s"%(
1368 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1369
1370 # Custom icon for the DMG, shown when the DMG is mounted.
1371 shutil.copy("../Icons/Disk Image.icns",
1372 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001373 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001374 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1375
1376 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1377
1378 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1379 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1380 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1381 setIcon(imagepath, "../Icons/Disk Image.icns")
1382
1383 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001384
1385 return imagepath
1386
1387
1388def setIcon(filePath, icnsPath):
1389 """
1390 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001391 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001392
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001393 dirPath = os.path.normpath(os.path.dirname(__file__))
1394 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001395 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1396 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1397 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001398 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1399 if not os.path.exists(appPath):
1400 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001401 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1402 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001403
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001404 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1405 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001406
1407def main():
1408 # First parse options and check if we can perform our work
1409 parseOptions()
1410 checkEnvironment()
1411
Ronald Oussoren508282e2009-03-30 19:34:51 +00001412 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001413 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001414 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001415
1416 if os.path.exists(WORKDIR):
1417 shutil.rmtree(WORKDIR)
1418 os.mkdir(WORKDIR)
1419
Ronald Oussoren287128a2010-04-18 14:01:05 +00001420 os.environ['LC_ALL'] = 'C'
1421
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001422 # Then build third-party libraries such as sleepycat DB4.
1423 buildLibraries()
1424
1425 # Now build python itself
1426 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001427
1428 # And then build the documentation
1429 # Remove the Deployment Target from the shell
1430 # environment, it's no longer needed and
1431 # an unexpected build target can cause problems
1432 # when Sphinx and its dependencies need to
1433 # be (re-)installed.
1434 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001435 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001436
1437
1438 # Prepare the applications folder
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001439 fn = os.path.join(WORKDIR, "_root", "Applications",
Benjamin Petersonc3104762008-10-03 11:52:06 +00001440 "Python %s"%(getVersion(),), "Update Shell Profile.command")
Ronald Oussoren799868e2009-03-04 21:07:19 +00001441 patchScript("scripts/postflight.patch-profile", fn)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001442
Benjamin Petersonc3104762008-10-03 11:52:06 +00001443 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001444 getVersion(),))
Ned Deilye1c9794952013-01-29 00:07:46 -08001445 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001446 setIcon(folder, "../Icons/Python Folder.icns")
1447
1448 # Create the installer
1449 buildInstaller()
1450
1451 # And copy the readme into the directory containing the installer
1452 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1453
1454 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001455 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001456
1457 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001458 fp.write("# BUILD INFO\n")
1459 fp.write("# Date: %s\n" % time.ctime())
1460 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001461 fp.close()
1462
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001463 # And copy it to a DMG
1464 buildDMG()
1465
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001466if __name__ == "__main__":
1467 main()