blob: 6bcdcd8e7e9c51cfa707edd6fe93d08a97e86ad2 [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):
Ned Deily62a86602014-12-09 23:45:13 -080065 """
66 Return the unquoted value of a variable from a file..
67 QUOTED_VALUE='quotes' -> str('quotes')
68 UNQUOTED_VALUE=noquotes -> str('noquotes')
69 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000070 variable = variable + '='
71 for ln in open(fn, 'r'):
72 if ln.startswith(variable):
73 value = ln[len(variable):].strip()
Ned Deily62a86602014-12-09 23:45:13 -080074 return value.strip("\"'")
Ned Deilye1c9794952013-01-29 00:07:46 -080075 raise RuntimeError("Cannot find variable %s" % variable[:-1])
76
77_cache_getVersion = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000078
79def getVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080080 global _cache_getVersion
81 if _cache_getVersion is None:
82 _cache_getVersion = grepValue(
83 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
84 return _cache_getVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000085
Ned Deilye1c9794952013-01-29 00:07:46 -080086def getVersionMajorMinor():
87 return tuple([int(n) for n in getVersion().split('.', 2)])
88
89_cache_getFullVersion = None
90
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000091def getFullVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080092 global _cache_getFullVersion
93 if _cache_getFullVersion is not None:
94 return _cache_getFullVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000095 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
96 for ln in open(fn):
97 if 'PY_VERSION' in ln:
Ned Deilye1c9794952013-01-29 00:07:46 -080098 _cache_getFullVersion = ln.split()[-1][1:-1]
99 return _cache_getFullVersion
100 raise RuntimeError("Cannot find full version??")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000101
Ned Deily62a86602014-12-09 23:45:13 -0800102FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
103FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
104
Ronald Oussorenaa560962006-11-07 15:53:38 +0000105# The directory we'll use to create the build (will be erased and recreated)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000106WORKDIR = "/tmp/_py"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000107
Ronald Oussorenaa560962006-11-07 15:53:38 +0000108# The directory we'll use to store third-party sources. Set this to something
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000109# else if you don't want to re-fetch required libraries every time.
Ronald Oussoren158ad592006-11-07 16:00:34 +0000110DEPSRC = os.path.join(WORKDIR, 'third-party')
111DEPSRC = os.path.expanduser('~/Universal/other-sources')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000112
113# Location of the preferred SDK
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000114
115### There are some issues with the SDK selection below here,
116### The resulting binary doesn't work on all platforms that
117### it should. Always default to the 10.4u SDK until that
Ezio Melotti6d0f0f22013-08-26 01:31:30 +0300118### issue is resolved.
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000119###
120##if int(os.uname()[2].split('.')[0]) == 8:
121## # Explicitly use the 10.4u (universal) SDK when
122## # building on 10.4, the system headers are not
123## # useable for a universal build
124## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
125##else:
126## SDKPATH = "/"
127
128SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000129
Ronald Oussoren508282e2009-03-30 19:34:51 +0000130universal_opts_map = { '32-bit': ('i386', 'ppc',),
131 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000132 'intel': ('i386', 'x86_64'),
133 '3-way': ('ppc', 'i386', 'x86_64'),
134 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
135default_target_map = {
136 '64-bit': '10.5',
137 '3-way': '10.5',
138 'intel': '10.5',
139 'all': '10.5',
140}
Ronald Oussoren508282e2009-03-30 19:34:51 +0000141
142UNIVERSALOPTS = tuple(universal_opts_map.keys())
143
144UNIVERSALARCHS = '32-bit'
145
146ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000147
Ezio Melotti24b07bc2011-03-15 18:55:01 +0200148# Source directory (assume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000149SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000150 os.path.dirname(
151 os.path.dirname(
152 os.path.abspath(__file__
153 ))))
154
Ronald Oussoren508282e2009-03-30 19:34:51 +0000155# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
156DEPTARGET = '10.3'
157
Ned Deily1f70b872014-06-25 13:33:57 -0700158def getDeptargetTuple():
159 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
160
161def getTargetCompilers():
162 target_cc_map = {
Ned Deilye1c9794952013-01-29 00:07:46 -0800163 '10.3': ('gcc-4.0', 'g++-4.0'),
164 '10.4': ('gcc-4.0', 'g++-4.0'),
165 '10.5': ('gcc-4.2', 'g++-4.2'),
166 '10.6': ('gcc-4.2', 'g++-4.2'),
Ned Deily1f70b872014-06-25 13:33:57 -0700167 }
168 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000169
Ned Deily1f70b872014-06-25 13:33:57 -0700170CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000171
Ned Deily62a86602014-12-09 23:45:13 -0800172PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Ned Deily53c460d2011-01-30 01:43:40 +0000173
Ronald Oussoren158ad592006-11-07 16:00:34 +0000174USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000175 Usage: build_python [options]
176
177 Options:
178 -? or -h: Show this message
179 -b DIR
180 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
181 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
182 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
183 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000184 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
185 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000186""")% globals()
187
Ned Deilye1c9794952013-01-29 00:07:46 -0800188# Dict of object file names with shared library names to check after building.
189# This is to ensure that we ended up dynamically linking with the shared
190# library paths and versions we expected. For example:
191# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
192# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
193# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
194EXPECTED_SHARED_LIBS = {}
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000195
Ned Deily62a86602014-12-09 23:45:13 -0800196# List of names of third party software built with this installer.
197# The names will be inserted into the rtf version of the License.
198THIRD_PARTY_LIBS = []
199
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000200# Instructions for building libraries that are necessary for building a
201# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000202# [The recipes are defined here for convenience but instantiated later after
203# command line options have been processed.]
204def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000205 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000206
Ned Deily1f70b872014-06-25 13:33:57 -0700207 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deilye1c9794952013-01-29 00:07:46 -0800208
Ned Deily62a86602014-12-09 23:45:13 -0800209 if getDeptargetTuple() < (10, 6):
210 # The OpenSSL libs shipped with OS X 10.5 and earlier are
211 # hopelessly out-of-date and do not include Apple's tie-in to
212 # the root certificates in the user and system keychains via TEA
213 # that was introduced in OS X 10.6. Note that this applies to
214 # programs built and linked with a 10.5 SDK even when run on
215 # newer versions of OS X.
216 #
217 # Dealing with CAs is messy. For now, just supply a
218 # local libssl and libcrypto for the older installer variants
219 # (e.g. the python.org 10.5+ 32-bit-only installer) that use the
220 # same default ssl certfile location as the system libs do:
221 # /System/Library/OpenSSL/cert.pem
222 # Then at least TLS connections can be negotiated with sites that
223 # use sha-256 certs like python.org, assuming the proper CA certs
224 # have been supplied. The default CA cert management issues for
225 # 10.5 and earlier builds are the same as before, other than it is
226 # now more obvious with cert checking enabled by default in the
227 # standard library.
228 #
229 # For builds with 10.6+ SDKs, continue to use the deprecated but
230 # less out-of-date Apple 0.9.8 libs for now. While they are less
231 # secure than using an up-to-date 1.0.1 version, doing so
232 # avoids the big problems of forcing users to have to manage
233 # default CAs themselves, thanks to the Apple libs using private TEA
234 # APIs for cert validation from keychains if validation using the
235 # standard OpenSSL locations (/System/Library/OpenSSL, normally empty)
236 # fails.
237
238 result.extend([
239 dict(
240 name="OpenSSL 1.0.1j",
241 url="https://www.openssl.org/source/openssl-1.0.1j.tar.gz",
242 checksum='f7175c9cd3c39bb1907ac8bba9df8ed3',
243 patches=[
244 "openssl_sdk_makedepend.patch",
245 ],
246 buildrecipe=build_universal_openssl,
247 configure=None,
248 install=None,
249 ),
250 ])
251
Ned Deilyebd63dc2014-04-09 16:12:11 -0700252# Disable for now
Ned Deily30101822014-11-14 18:53:59 -0800253 if False: # if getDeptargetTuple() > (10, 5):
Ned Deily0203a802013-10-25 00:40:07 -0700254 result.extend([
255 dict(
256 name="Tcl 8.5.15",
257 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tcl8.5.15-src.tar.gz",
258 checksum='f3df162f92c69b254079c4d0af7a690f',
259 buildDir="unix",
260 configure_pre=[
261 '--enable-shared',
262 '--enable-threads',
263 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
264 ],
265 useLDFlags=False,
266 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
267 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
268 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
269 },
270 ),
271 dict(
272 name="Tk 8.5.15",
273 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tk8.5.15-src.tar.gz",
274 checksum='55b8e33f903210a4e1c8bce0f820657f',
Ned Deilya6cbff02013-10-27 19:47:23 -0700275 patches=[
276 "issue19373_tk_8_5_15_source.patch",
277 ],
Ned Deily0203a802013-10-25 00:40:07 -0700278 buildDir="unix",
279 configure_pre=[
280 '--enable-aqua',
281 '--enable-shared',
282 '--enable-threads',
283 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
284 ],
285 useLDFlags=False,
286 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'%{
287 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
288 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
289 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.5'%(getVersion())),
290 },
291 ),
292 ])
293
Ned Deily30101822014-11-14 18:53:59 -0800294 if PYTHON_3:
Ned Deilye1c9794952013-01-29 00:07:46 -0800295 result.extend([
296 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700297 name="XZ 5.0.5",
298 url="http://tukaani.org/xz/xz-5.0.5.tar.gz",
299 checksum='19d924e066b6fff0bc9d1981b4e53196',
Ned Deilye1c9794952013-01-29 00:07:46 -0800300 configure_pre=[
301 '--disable-dependency-tracking',
302 ]
303 ),
304 ])
305
306 result.extend([
307 dict(
308 name="NCurses 5.9",
309 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
310 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
311 configure_pre=[
312 "--enable-widec",
313 "--without-cxx",
314 "--without-cxx-binding",
315 "--without-ada",
316 "--without-curses-h",
317 "--enable-shared",
318 "--with-shared",
319 "--without-debug",
320 "--without-normal",
321 "--without-tests",
322 "--without-manpages",
323 "--datadir=/usr/share",
324 "--sysconfdir=/etc",
325 "--sharedstatedir=/usr/com",
326 "--with-terminfo-dirs=/usr/share/terminfo",
327 "--with-default-terminfo-dir=/usr/share/terminfo",
328 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
329 ],
330 patchscripts=[
331 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
332 "f54bf02a349f96a7c4f0d00922f3a0d4"),
333 ],
334 useLDFlags=False,
335 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
336 shellQuote(os.path.join(WORKDIR, 'libraries')),
337 shellQuote(os.path.join(WORKDIR, 'libraries')),
338 getVersion(),
339 ),
340 ),
341 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700342 name="SQLite 3.8.3.1",
343 url="http://www.sqlite.org/2014/sqlite-autoconf-3080301.tar.gz",
344 checksum='509ff98d8dc9729b618b7e96612079c6',
Ned Deilye1c9794952013-01-29 00:07:46 -0800345 extra_cflags=('-Os '
346 '-DSQLITE_ENABLE_FTS4 '
347 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
348 '-DSQLITE_ENABLE_RTREE '
349 '-DSQLITE_TCL=0 '
350 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
351 configure_pre=[
352 '--enable-threadsafe',
353 '--enable-shared=no',
354 '--enable-static=yes',
355 '--disable-readline',
356 '--disable-dependency-tracking',
357 ]
358 ),
359 ])
360
Ned Deily1f70b872014-06-25 13:33:57 -0700361 if getDeptargetTuple() < (10, 5):
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000362 result.extend([
363 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000364 name="Bzip2 1.0.6",
365 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
366 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000367 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800368 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
369 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000370 shellQuote(os.path.join(WORKDIR, 'libraries')),
371 ' -arch '.join(ARCHLIST),
372 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000373 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000374 ),
375 dict(
376 name="ZLib 1.2.3",
377 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
378 checksum='debc62758716a169df9f62e6ab2bc634',
379 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800380 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
381 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000382 shellQuote(os.path.join(WORKDIR, 'libraries')),
383 ' -arch '.join(ARCHLIST),
384 SDKPATH,
385 ),
386 ),
387 dict(
388 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000389 name="GNU Readline 6.1.2",
390 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
391 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000392 patchlevel='0',
393 patches=[
394 # The readline maintainers don't do actual micro releases, but
395 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800396 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
397 'c642f2e84d820884b0bf9fd176bc6c3f'),
398 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
399 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000400 ]
401 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000402 ])
403
Ned Deily53c460d2011-01-30 01:43:40 +0000404 if not PYTHON_3:
405 result.extend([
406 dict(
407 name="Sleepycat DB 4.7.25",
408 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
409 checksum='ec2b87e833779681a0c3a814aa71359e',
410 buildDir="build_unix",
411 configure="../dist/configure",
412 configure_pre=[
413 '--includedir=/usr/local/include/db4',
414 ]
415 ),
416 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000417
418 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000419
420
421# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000422def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000423 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000424 result = [
425 dict(
426 name="PythonFramework",
427 long_name="Python Framework",
428 source="/Library/Frameworks/Python.framework",
429 readme="""\
430 This package installs Python.framework, that is the python
431 interpreter and the standard library. This also includes Python
432 wrappers for lots of Mac OS X API's.
433 """,
434 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000435 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000436 ),
437 dict(
438 name="PythonApplications",
439 long_name="GUI Applications",
440 source="/Applications/Python %(VER)s",
441 readme="""\
442 This package installs IDLE (an interactive Python IDE),
443 Python Launcher and Build Applet (create application bundles
444 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000445
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000446 It also installs a number of examples and demos.
447 """,
448 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000449 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000450 ),
451 dict(
452 name="PythonUnixTools",
453 long_name="UNIX command-line tools",
454 source="/usr/local/bin",
455 readme="""\
456 This package installs the unix tools in /usr/local/bin for
457 compatibility with older releases of Python. This package
458 is not necessary to use Python.
459 """,
460 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000461 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000462 ),
463 dict(
464 name="PythonDocumentation",
465 long_name="Python Documentation",
466 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
467 source="/pydocs",
468 readme="""\
469 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800470 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000471 """,
472 postflight="scripts/postflight.documentation",
473 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000474 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000475 ),
476 dict(
477 name="PythonProfileChanges",
478 long_name="Shell profile updater",
479 readme="""\
480 This packages updates your shell profile to make sure that
481 the Python tools are found by your shell in preference of
482 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000483
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000484 If you don't install this package you'll have to add
485 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
486 to your PATH by hand.
487 """,
488 postflight="scripts/postflight.patch-profile",
489 topdir="/Library/Frameworks/Python.framework",
490 source="/empty-dir",
491 required=False,
Ned Deily30101822014-11-14 18:53:59 -0800492 selected='selected',
493 ),
494 dict(
495 name="PythonInstallPip",
496 long_name="Install or upgrade pip",
497 readme="""\
498 This package installs (or upgrades from an earlier version)
499 pip, a tool for installing and managing Python packages.
500 """,
501 postflight="scripts/postflight.ensurepip",
502 topdir="/Library/Frameworks/Python.framework",
503 source="/empty-dir",
504 required=False,
505 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000506 ),
507 ]
508
Ned Deily1f70b872014-06-25 13:33:57 -0700509 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000510 result.append(
511 dict(
512 name="PythonSystemFixes",
513 long_name="Fix system Python",
514 readme="""\
515 This package updates the system python installation on
516 Mac OS X 10.3 to ensure that you can build new python extensions
517 using that copy of python after installing this version.
518 """,
519 postflight="../Tools/fixapplepython23.py",
520 topdir="/Library/Frameworks/Python.framework",
521 source="/empty-dir",
522 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000523 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000524 )
525 )
Ned Deilyebd63dc2014-04-09 16:12:11 -0700526
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000527 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000528
529def fatal(msg):
530 """
531 A fatal error, bail out.
532 """
533 sys.stderr.write('FATAL: ')
534 sys.stderr.write(msg)
535 sys.stderr.write('\n')
536 sys.exit(1)
537
538def fileContents(fn):
539 """
540 Return the contents of the named file
541 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800542 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000543
544def runCommand(commandline):
545 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200546 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000547 unless the command fails.
548 """
549 fd = os.popen(commandline, 'r')
550 data = fd.read()
551 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000552 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000553 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800554 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000555
556 if VERBOSE:
557 sys.stdout.write(data); sys.stdout.flush()
558
559def captureCommand(commandline):
560 fd = os.popen(commandline, 'r')
561 data = fd.read()
562 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000563 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000564 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800565 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000566
567 return data
568
Ronald Oussoren287128a2010-04-18 14:01:05 +0000569def getTclTkVersion(configfile, versionline):
570 """
571 search Tcl or Tk configuration file for version line
572 """
573 try:
574 f = open(configfile, "r")
575 except:
576 fatal("Framework configuration file not found: %s" % configfile)
577
578 for l in f:
579 if l.startswith(versionline):
580 f.close()
581 return l
582
583 fatal("Version variable %s not found in framework configuration file: %s"
584 % (versionline, configfile))
585
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000586def checkEnvironment():
587 """
588 Check that we're running on a supported system.
589 """
590
Ned Deily53c460d2011-01-30 01:43:40 +0000591 if sys.version_info[0:2] < (2, 4):
592 fatal("This script must be run with Python 2.4 or later")
593
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000594 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000595 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000596
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000597 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000598 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000599
600 if not os.path.exists(SDKPATH):
601 fatal("Please install the latest version of Xcode and the %s SDK"%(
602 os.path.basename(SDKPATH[:-4])))
603
Ronald Oussoren287128a2010-04-18 14:01:05 +0000604 # Because we only support dynamic load of only one major/minor version of
605 # Tcl/Tk, ensure:
606 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800607 # higher than the Apple-supplied system version in
608 # SDKROOT/System/Library/Frameworks
609 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
610 # in) SDKROOT/Library/Frameworks with the same version as the system
611 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000612
Ned Deilye1c9794952013-01-29 00:07:46 -0800613 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000614 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800615 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000616 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800617 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000618 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800619 frameworks[framework] = os.readlink(sysfw)
620 if not os.path.exists(libfw):
621 fatal("Please install a link to a current %s %s as %s so "
622 "the user can override the system framework."
623 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000624 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000625 fatal("Version of %s must match %s" % (libfw, sysfw) )
626 if os.path.exists(usrfw):
627 fatal("Please rename %s to avoid possible dynamic load issues."
628 % usrfw)
629
Ned Deilye1c9794952013-01-29 00:07:46 -0800630 if frameworks['Tcl'] != frameworks['Tk']:
631 fatal("The Tcl and Tk frameworks are not the same version.")
632
633 # add files to check after build
634 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
635 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
636 % frameworks['Tcl'],
637 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
638 % frameworks['Tk'],
639 ]
640
Ronald Oussoren287128a2010-04-18 14:01:05 +0000641 # Remove inherited environment variables which might influence build
642 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
643 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
644 for ev in list(os.environ):
645 for prefix in environ_var_prefixes:
646 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800647 print("INFO: deleting environment variable %s=%s" % (
648 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000649 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000650
Ned Deilye1c9794952013-01-29 00:07:46 -0800651 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
652 if 'SDK_TOOLS_BIN' in os.environ:
653 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
654 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
655 # add its fixed location here if it exists
656 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
657 if os.path.isdir(OLD_DEVELOPER_TOOLS):
658 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
659 os.environ['PATH'] = base_path
660 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyebd63dc2014-04-09 16:12:11 -0700661 # Ensure ws have access to hg and to sphinx-build.
662 # You may have to create links in /usr/bin for them.
663 runCommand('hg --version')
Ned Deily5ceffa12014-09-05 15:51:54 -0700664 runCommand('sphinx-build --version')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000665
Ronald Oussoren158ad592006-11-07 16:00:34 +0000666def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000667 """
668 Parse arguments and update global settings.
669 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000670 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800671 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily62a86602014-12-09 23:45:13 -0800672 global FW_VERSION_PREFIX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000673
674 if args is None:
675 args = sys.argv[1:]
676
677 try:
678 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000679 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
680 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800681 except getopt.GetoptError:
682 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000683 sys.exit(1)
684
685 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800686 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000687 sys.exit(1)
688
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000689 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000690 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000691 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800692 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000693 sys.exit(0)
694
695 elif k in ('-d', '--build-dir'):
696 WORKDIR=v
697
698 elif k in ('--third-party',):
699 DEPSRC=v
700
701 elif k in ('--sdk-path',):
702 SDKPATH=v
703
704 elif k in ('--src-dir',):
705 SRCDIR=v
706
Ronald Oussoren508282e2009-03-30 19:34:51 +0000707 elif k in ('--dep-target', ):
708 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000709 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000710
711 elif k in ('--universal-archs', ):
712 if v in UNIVERSALOPTS:
713 UNIVERSALARCHS = v
714 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000715 if deptarget is None:
716 # Select alternate default deployment
717 # target
718 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000719 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800720 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000721
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000722 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800723 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000724
725 SRCDIR=os.path.abspath(SRCDIR)
726 WORKDIR=os.path.abspath(WORKDIR)
727 SDKPATH=os.path.abspath(SDKPATH)
728 DEPSRC=os.path.abspath(DEPSRC)
729
Ned Deily1f70b872014-06-25 13:33:57 -0700730 CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000731
Ned Deily62a86602014-12-09 23:45:13 -0800732 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
733
734 print("-- Settings:")
735 print(" * Source directory: %s" % SRCDIR)
736 print(" * Build directory: %s" % WORKDIR)
737 print(" * SDK location: %s" % SDKPATH)
738 print(" * Third-party source: %s" % DEPSRC)
739 print(" * Deployment target: %s" % DEPTARGET)
740 print(" * Universal archs: %s" % str(ARCHLIST))
741 print(" * C compiler: %s" % CC)
742 print(" * C++ compiler: %s" % CXX)
Ned Deilye1c9794952013-01-29 00:07:46 -0800743 print("")
Ned Deily62a86602014-12-09 23:45:13 -0800744 print(" -- Building a Python %s framework at patch level %s"
745 % (getVersion(), getFullVersion()))
746 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000747
748def extractArchive(builddir, archiveName):
749 """
750 Extract a source archive into 'builddir'. Returns the path of the
751 extracted archive.
752
753 XXX: This function assumes that archives contain a toplevel directory
754 that is has the same name as the basename of the archive. This is
Ned Deily0203a802013-10-25 00:40:07 -0700755 safe enough for almost anything we use. Unfortunately, it does not
756 work for current Tcl and Tk source releases where the basename of
757 the archive ends with "-src" but the uncompressed directory does not.
758 For now, just special case Tcl and Tk tar.gz downloads.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000759 """
760 curdir = os.getcwd()
761 try:
762 os.chdir(builddir)
763 if archiveName.endswith('.tar.gz'):
764 retval = os.path.basename(archiveName[:-7])
Ned Deily0203a802013-10-25 00:40:07 -0700765 if ((retval.startswith('tcl') or retval.startswith('tk'))
766 and retval.endswith('-src')):
767 retval = retval[:-4]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000768 if os.path.exists(retval):
769 shutil.rmtree(retval)
770 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
771
772 elif archiveName.endswith('.tar.bz2'):
773 retval = os.path.basename(archiveName[:-8])
774 if os.path.exists(retval):
775 shutil.rmtree(retval)
776 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
777
778 elif archiveName.endswith('.tar'):
779 retval = os.path.basename(archiveName[:-4])
780 if os.path.exists(retval):
781 shutil.rmtree(retval)
782 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
783
784 elif archiveName.endswith('.zip'):
785 retval = os.path.basename(archiveName[:-4])
786 if os.path.exists(retval):
787 shutil.rmtree(retval)
788 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
789
790 data = fp.read()
791 xit = fp.close()
792 if xit is not None:
793 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800794 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000795
796 return os.path.join(builddir, retval)
797
798 finally:
799 os.chdir(curdir)
800
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000801def downloadURL(url, fname):
802 """
803 Download the contents of the url into the file.
804 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800805 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000806 fpOut = open(fname, 'wb')
807 block = fpIn.read(10240)
808 try:
809 while block:
810 fpOut.write(block)
811 block = fpIn.read(10240)
812 fpIn.close()
813 fpOut.close()
814 except:
815 try:
816 os.unlink(fname)
817 except:
818 pass
819
Ned Deilye1c9794952013-01-29 00:07:46 -0800820def verifyThirdPartyFile(url, checksum, fname):
821 """
822 Download file from url to filename fname if it does not already exist.
823 Abort if file contents does not match supplied md5 checksum.
824 """
825 name = os.path.basename(fname)
826 if os.path.exists(fname):
827 print("Using local copy of %s"%(name,))
828 else:
829 print("Did not find local copy of %s"%(name,))
830 print("Downloading %s"%(name,))
831 downloadURL(url, fname)
832 print("Archive for %s stored as %s"%(name, fname))
833 if os.system(
834 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
835 % (shellQuote(fname), checksum) ):
836 fatal('MD5 checksum mismatch for file %s' % fname)
837
Ned Deily62a86602014-12-09 23:45:13 -0800838def build_universal_openssl(basedir, archList):
839 """
840 Special case build recipe for universal build of openssl.
841
842 The upstream OpenSSL build system does not directly support
843 OS X universal builds. We need to build each architecture
844 separately then lipo them together into fat libraries.
845 """
846
Ned Deily0be4b1e2014-12-11 15:55:42 -0800847 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
848 # If we are building on a 10.4.x or earlier system,
849 # unilaterally disable assembly code building to avoid the problem.
850 no_asm = int(platform.release().split(".")[0]) < 9
851
Ned Deily62a86602014-12-09 23:45:13 -0800852 def build_openssl_arch(archbase, arch):
853 "Build one architecture of openssl"
854 arch_opts = {
855 "i386": ["darwin-i386-cc"],
856 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
857 "ppc": ["darwin-ppc-cc"],
858 "ppc64": ["darwin64-ppc-cc"],
859 }
860 configure_opts = [
861 "no-krb5",
862 "no-idea",
863 "no-mdc2",
864 "no-rc5",
865 "no-zlib",
866 "enable-tlsext",
867 "no-ssl2",
868 "no-ssl3",
869 "no-ssl3-method",
870 # "enable-unit-test",
871 "shared",
872 "--install_prefix=%s"%shellQuote(archbase),
873 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
874 "--openssldir=/System/Library/OpenSSL",
875 ]
Ned Deily0be4b1e2014-12-11 15:55:42 -0800876 if no_asm:
877 configure_opts.append("no-asm")
Ned Deily62a86602014-12-09 23:45:13 -0800878 runCommand(" ".join(["perl", "Configure"]
879 + arch_opts[arch] + configure_opts))
880 runCommand("make depend OSX_SDK=%s" % SDKPATH)
881 runCommand("make all OSX_SDK=%s" % SDKPATH)
882 runCommand("make install_sw OSX_SDK=%s" % SDKPATH)
883 # runCommand("make test")
884 return
885
886 srcdir = os.getcwd()
887 universalbase = os.path.join(srcdir, "..",
888 os.path.basename(srcdir) + "-universal")
889 os.mkdir(universalbase)
890 archbasefws = []
891 for arch in archList:
892 # fresh copy of the source tree
893 archsrc = os.path.join(universalbase, arch, "src")
894 shutil.copytree(srcdir, archsrc, symlinks=True)
895 # install base for this arch
896 archbase = os.path.join(universalbase, arch, "root")
897 os.mkdir(archbase)
898 # Python framework base within install_prefix:
899 # the build will install into this framework..
900 # This is to ensure that the resulting shared libs have
901 # the desired real install paths built into them.
902 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
903
904 # build one architecture
905 os.chdir(archsrc)
906 build_openssl_arch(archbase, arch)
907 os.chdir(srcdir)
908 archbasefws.append(archbasefw)
909
910 # copy arch-independent files from last build into the basedir framework
911 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
912 shutil.copytree(
913 os.path.join(archbasefw, "include", "openssl"),
914 os.path.join(basefw, "include", "openssl")
915 )
916
917 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
918 "SHLIB_VERSION_NUMBER")
919 # e.g. -> "1.0.0"
920 libcrypto = "libcrypto.dylib"
921 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
922 # e.g. -> "libcrypto.1.0.0.dylib"
923 libssl = "libssl.dylib"
924 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
925 # e.g. -> "libssl.1.0.0.dylib"
926
927 try:
928 os.mkdir(os.path.join(basefw, "lib"))
929 except OSError:
930 pass
931
932 # merge the individual arch-dependent shared libs into a fat shared lib
933 archbasefws.insert(0, basefw)
934 for (lib_unversioned, lib_versioned) in [
935 (libcrypto, libcrypto_versioned),
936 (libssl, libssl_versioned)
937 ]:
938 runCommand("lipo -create -output " +
939 " ".join(shellQuote(
940 os.path.join(fw, "lib", lib_versioned))
941 for fw in archbasefws))
942 # and create an unversioned symlink of it
943 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
944
945 # Create links in the temp include and lib dirs that will be injected
946 # into the Python build so that setup.py can find them while building
947 # and the versioned links so that the setup.py post-build import test
948 # does not fail.
949 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
950 for fn in [
951 ["include", "openssl"],
952 ["lib", libcrypto],
953 ["lib", libssl],
954 ["lib", libcrypto_versioned],
955 ["lib", libssl_versioned],
956 ]:
957 os.symlink(
958 os.path.join(relative_path, *fn),
959 os.path.join(basedir, "usr", "local", *fn)
960 )
961
962 return
963
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000964def buildRecipe(recipe, basedir, archList):
965 """
966 Build software using a recipe. This function does the
967 'configure;make;make install' dance for C software, with a possibility
968 to customize this process, basically a poor-mans DarwinPorts.
969 """
970 curdir = os.getcwd()
971
972 name = recipe['name']
Ned Deily62a86602014-12-09 23:45:13 -0800973 THIRD_PARTY_LIBS.append(name)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000974 url = recipe['url']
975 configure = recipe.get('configure', './configure')
Ned Deily62a86602014-12-09 23:45:13 -0800976 buildrecipe = recipe.get('buildrecipe', None)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000977 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
978 shellQuote(basedir)))
979
980 archiveName = os.path.split(url)[-1]
981 sourceArchive = os.path.join(DEPSRC, archiveName)
982
983 if not os.path.exists(DEPSRC):
984 os.mkdir(DEPSRC)
985
Ned Deilye1c9794952013-01-29 00:07:46 -0800986 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
987 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000988 buildDir=os.path.join(WORKDIR, '_bld')
989 if not os.path.exists(buildDir):
990 os.mkdir(buildDir)
991
992 workDir = extractArchive(buildDir, sourceArchive)
993 os.chdir(workDir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000994
Ned Deilye1c9794952013-01-29 00:07:46 -0800995 for patch in recipe.get('patches', ()):
996 if isinstance(patch, tuple):
997 url, checksum = patch
998 fn = os.path.join(DEPSRC, os.path.basename(url))
999 verifyThirdPartyFile(url, checksum, fn)
1000 else:
1001 # patch is a file in the source directory
1002 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001003 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1004 shellQuote(fn),))
1005
Ned Deilye1c9794952013-01-29 00:07:46 -08001006 for patchscript in recipe.get('patchscripts', ()):
1007 if isinstance(patchscript, tuple):
1008 url, checksum = patchscript
1009 fn = os.path.join(DEPSRC, os.path.basename(url))
1010 verifyThirdPartyFile(url, checksum, fn)
1011 else:
1012 # patch is a file in the source directory
1013 fn = os.path.join(curdir, patchscript)
1014 if fn.endswith('.bz2'):
1015 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1016 fn = fn[:-4]
1017 runCommand('sh %s' % shellQuote(fn))
1018 os.unlink(fn)
1019
Ned Deilya6cbff02013-10-27 19:47:23 -07001020 if 'buildDir' in recipe:
1021 os.chdir(recipe['buildDir'])
1022
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001023 if configure is not None:
1024 configure_args = [
1025 "--prefix=/usr/local",
1026 "--enable-static",
1027 "--disable-shared",
1028 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1029 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001030
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001031 if 'configure_pre' in recipe:
1032 args = list(recipe['configure_pre'])
1033 if '--disable-static' in args:
1034 configure_args.remove('--enable-static')
1035 if '--enable-shared' in args:
1036 configure_args.remove('--disable-shared')
1037 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001038
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001039 if recipe.get('useLDFlags', 1):
1040 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001041 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1042 "-I%s/usr/local/include"%(
1043 recipe.get('extra_cflags', ''),
1044 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001045 ' -arch '.join(archList),
1046 shellQuote(SDKPATH)[1:-1],
1047 shellQuote(basedir)[1:-1],),
Ned Deilyc177b1c2014-04-09 16:13:46 -07001048 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deilye1c9794952013-01-29 00:07:46 -08001049 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001050 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001051 shellQuote(basedir)[1:-1],
1052 ' -arch '.join(archList)),
1053 ])
1054 else:
1055 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001056 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1057 "-I%s/usr/local/include"%(
1058 recipe.get('extra_cflags', ''),
1059 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001060 ' -arch '.join(archList),
1061 shellQuote(SDKPATH)[1:-1],
1062 shellQuote(basedir)[1:-1],),
1063 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001064
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001065 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -08001066 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001067
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001068 configure_args.insert(0, configure)
1069 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001070
Ned Deilye1c9794952013-01-29 00:07:46 -08001071 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001072 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001073
Ned Deily62a86602014-12-09 23:45:13 -08001074 if buildrecipe is not None:
1075 # call special-case build recipe, e.g. for openssl
1076 buildrecipe(basedir, archList)
1077
1078 if install is not None:
1079 print("Running install for %s"%(name,))
1080 runCommand('{ ' + install + ' ;} 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001081
Ned Deilye1c9794952013-01-29 00:07:46 -08001082 print("Done %s"%(name,))
1083 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001084
1085 os.chdir(curdir)
1086
1087def buildLibraries():
1088 """
1089 Build our dependencies into $WORKDIR/libraries/usr/local
1090 """
Ned Deilye1c9794952013-01-29 00:07:46 -08001091 print("")
1092 print("Building required libraries")
1093 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001094 universal = os.path.join(WORKDIR, 'libraries')
1095 os.mkdir(universal)
1096 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1097 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1098
Ronald Oussoren508282e2009-03-30 19:34:51 +00001099 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001100 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001101
1102
1103
1104def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001105 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001106 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -08001107 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001108 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001109 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001110 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001111 curDir = os.getcwd()
1112 os.chdir(buildDir)
Ned Deily5ceffa12014-09-05 15:51:54 -07001113 # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9
1114 runCommand('make clean')
1115 # Assume sphinx-build is on our PATH, checked in checkEnvironment
1116 runCommand('make html')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001117 os.chdir(curDir)
1118 if not os.path.exists(docdir):
1119 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001120 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001121
1122
1123def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -08001124 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001125
1126 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1127 rootDir = os.path.join(WORKDIR, '_root')
1128
1129 if os.path.exists(buildDir):
1130 shutil.rmtree(buildDir)
1131 if os.path.exists(rootDir):
1132 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +00001133 os.makedirs(buildDir)
1134 os.makedirs(rootDir)
1135 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001136 curdir = os.getcwd()
1137 os.chdir(buildDir)
1138
1139 # Not sure if this is still needed, the original build script
1140 # claims that parts of the install assume python.exe exists.
1141 os.symlink('python', os.path.join(buildDir, 'python.exe'))
1142
1143 # Extract the version from the configure file, needed to calculate
1144 # several paths.
1145 version = getVersion()
1146
Ronald Oussoren008af852009-03-30 20:02:08 +00001147 # Since the extra libs are not in their installed framework location
1148 # during the build, augment the library path so that the interpreter
1149 # will find them during its extension import sanity checks.
1150 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1151 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -08001152 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +00001153 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
1154 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +00001155 "%s "
Ned Deilyebd63dc2014-04-09 16:12:11 -07001156 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +00001157 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deilyf84b5312013-10-25 00:44:46 -07001158 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001159 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1160 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +00001161 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deily30101822014-11-14 18:53:59 -08001162 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +00001163 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001164 shellQuote(WORKDIR)[1:-1]))
1165
Ned Deilyfbb60d52014-05-22 15:27:01 -07001166 print("Running make touch")
1167 runCommand("make touch")
1168
Ned Deilye1c9794952013-01-29 00:07:46 -08001169 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001170 runCommand("make")
1171
Ned Deilye1c9794952013-01-29 00:07:46 -08001172 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +00001173 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001174 shellQuote(rootDir)))
1175
Ned Deilye1c9794952013-01-29 00:07:46 -08001176 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001177 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1178 shellQuote(rootDir)))
1179
Ronald Oussoren008af852009-03-30 20:02:08 +00001180 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -08001181 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001182 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
1183 runCommand("mv %s/* %s"%(
1184 shellQuote(os.path.join(
1185 WORKDIR, 'libraries', 'Library', 'Frameworks',
1186 'Python.framework', 'Versions', getVersion(),
1187 'lib')),
1188 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1189 'Python.framework', 'Versions', getVersion(),
1190 'lib'))))
1191
Ned Deily70f213a2013-10-26 03:16:06 -07001192 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1193 'Python.framework', 'Versions',
1194 version, 'lib', 'python%s'%(version,))
1195
Ned Deilye1c9794952013-01-29 00:07:46 -08001196 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001197 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001198 gid = grp.getgrnam('admin').gr_gid
1199
Ned Deilye1c9794952013-01-29 00:07:46 -08001200 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001201 for dirpath, dirnames, filenames in os.walk(frmDir):
1202 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -08001203 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001204 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +00001205
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001206 for fn in filenames:
1207 if os.path.islink(fn):
1208 continue
1209
1210 # "chmod g+w $fn"
1211 p = os.path.join(dirpath, fn)
1212 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001213 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1214 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001215
Ned Deilye1c9794952013-01-29 00:07:46 -08001216 if fn in EXPECTED_SHARED_LIBS:
1217 # check to see that this file was linked with the
1218 # expected library path and version
1219 data = captureCommand("otool -L %s" % shellQuote(p))
1220 for sl in EXPECTED_SHARED_LIBS[fn]:
1221 if ("\t%s " % sl) not in data:
1222 print("Expected shared lib %s was not linked with %s"
1223 % (sl, p))
1224 shared_lib_error = True
1225
1226 if shared_lib_error:
1227 fatal("Unexpected shared library errors.")
1228
Ned Deily53c460d2011-01-30 01:43:40 +00001229 if PYTHON_3:
1230 LDVERSION=None
1231 VERSION=None
1232 ABIFLAGS=None
1233
1234 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
1235 for ln in fp:
1236 if ln.startswith('VERSION='):
1237 VERSION=ln.split()[1]
1238 if ln.startswith('ABIFLAGS='):
1239 ABIFLAGS=ln.split()[1]
1240 if ln.startswith('LDVERSION='):
1241 LDVERSION=ln.split()[1]
1242 fp.close()
1243
1244 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1245 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1246 config_suffix = '-' + LDVERSION
1247 else:
1248 config_suffix = '' # Python 2.x
1249
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001250 # We added some directories to the search path during the configure
1251 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -08001252 # the end-users system. Also remove the directories from _sysconfigdata.py
1253 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001254
Ned Deilye6ef7022013-10-25 00:46:59 -07001255 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1256 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1257
Ned Deilye6ef7022013-10-25 00:46:59 -07001258 # fix Makefile
1259 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1260 fp = open(path, 'r')
1261 data = fp.read()
1262 fp.close()
1263
1264 for p in (include_path, lib_path):
1265 data = data.replace(" " + p, '')
1266 data = data.replace(p + " ", '')
1267
1268 fp = open(path, 'w')
1269 fp.write(data)
1270 fp.close()
1271
1272 # fix _sysconfigdata if it exists
1273 #
1274 # TODO: make this more robust! test_sysconfig_module of
1275 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1276 # the output from get_config_var in both sysconfig and
1277 # distutils.sysconfig is exactly the same for both CFLAGS and
1278 # LDFLAGS. The fixing up is now complicated by the pretty
1279 # printing in _sysconfigdata.py. Also, we are using the
1280 # pprint from the Python running the installer build which
1281 # may not cosmetically format the same as the pprint in the Python
1282 # being built (and which is used to originally generate
1283 # _sysconfigdata.py).
1284
1285 import pprint
1286 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1287 if os.path.exists(path):
Ned Deilye1c9794952013-01-29 00:07:46 -08001288 fp = open(path, 'r')
1289 data = fp.read()
1290 fp.close()
Ned Deilye6ef7022013-10-25 00:46:59 -07001291 # create build_time_vars dict
1292 exec(data)
1293 vars = {}
1294 for k, v in build_time_vars.items():
1295 if type(v) == type(''):
1296 for p in (include_path, lib_path):
1297 v = v.replace(' ' + p, '')
1298 v = v.replace(p + ' ', '')
1299 vars[k] = v
Ned Deilye1c9794952013-01-29 00:07:46 -08001300
Ned Deilye1c9794952013-01-29 00:07:46 -08001301 fp = open(path, 'w')
Ned Deilye6ef7022013-10-25 00:46:59 -07001302 # duplicated from sysconfig._generate_posix_vars()
1303 fp.write('# system configuration generated and used by'
1304 ' the sysconfig module\n')
1305 fp.write('build_time_vars = ')
1306 pprint.pprint(vars, stream=fp)
Ned Deilye1c9794952013-01-29 00:07:46 -08001307 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001308
1309 # Add symlinks in /usr/local/bin, using relative links
1310 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1311 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1312 'Python.framework', 'Versions', version, 'bin')
1313 if os.path.exists(usr_local_bin):
1314 shutil.rmtree(usr_local_bin)
1315 os.makedirs(usr_local_bin)
1316 for fn in os.listdir(
1317 os.path.join(frmDir, 'Versions', version, 'bin')):
1318 os.symlink(os.path.join(to_framework, fn),
1319 os.path.join(usr_local_bin, fn))
1320
1321 os.chdir(curdir)
1322
Ned Deily53c460d2011-01-30 01:43:40 +00001323 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001324 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001325 # with an already installed version of python 2
1326 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1327 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001328
1329def patchFile(inPath, outPath):
1330 data = fileContents(inPath)
1331 data = data.replace('$FULL_VERSION', getFullVersion())
1332 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001333 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001334 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001335 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily62a86602014-12-09 23:45:13 -08001336 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Ronald Oussorenc5555542006-06-11 20:24:45 +00001337
1338 # This one is not handy as a template variable
1339 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001340 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001341 fp.write(data)
1342 fp.close()
1343
1344def patchScript(inPath, outPath):
Ned Deily30101822014-11-14 18:53:59 -08001345 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001346 data = fileContents(inPath)
Ned Deily30101822014-11-14 18:53:59 -08001347 data = data.replace('@PYMAJOR@', str(major))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001348 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001349 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001350 fp.write(data)
1351 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001352 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001353
1354
1355
1356def packageFromRecipe(targetDir, recipe):
1357 curdir = os.getcwd()
1358 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001359 # The major version (such as 2.5) is included in the package name
1360 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001361 # common.
1362 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001363 srcdir = recipe.get('source')
1364 pkgroot = recipe.get('topdir', srcdir)
1365 postflight = recipe.get('postflight')
1366 readme = textwrap.dedent(recipe['readme'])
1367 isRequired = recipe.get('required', True)
1368
Ned Deilye1c9794952013-01-29 00:07:46 -08001369 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001370
1371 # Substitute some variables
1372 textvars = dict(
1373 VER=getVersion(),
1374 FULLVER=getFullVersion(),
1375 )
1376 readme = readme % textvars
1377
1378 if pkgroot is not None:
1379 pkgroot = pkgroot % textvars
1380 else:
1381 pkgroot = '/'
1382
1383 if srcdir is not None:
1384 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1385 srcdir = srcdir % textvars
1386
1387 if postflight is not None:
1388 postflight = os.path.abspath(postflight)
1389
1390 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1391 os.makedirs(packageContents)
1392
1393 if srcdir is not None:
1394 os.chdir(srcdir)
1395 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1396 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1397 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1398
1399 fn = os.path.join(packageContents, 'PkgInfo')
1400 fp = open(fn, 'w')
1401 fp.write('pmkrpkg1')
1402 fp.close()
1403
1404 rsrcDir = os.path.join(packageContents, "Resources")
1405 os.mkdir(rsrcDir)
1406 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1407 fp.write(readme)
1408 fp.close()
1409
1410 if postflight is not None:
1411 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1412
1413 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001414 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001415 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001416 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1417 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1418 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001419 CFBundleShortVersionString=vers,
1420 IFMajorVersion=major,
1421 IFMinorVersion=minor,
1422 IFPkgFormatVersion=0.10000000149011612,
1423 IFPkgFlagAllowBackRev=False,
1424 IFPkgFlagAuthorizationAction="RootAuthorization",
1425 IFPkgFlagDefaultLocation=pkgroot,
1426 IFPkgFlagFollowLinks=True,
1427 IFPkgFlagInstallFat=True,
1428 IFPkgFlagIsRequired=isRequired,
1429 IFPkgFlagOverwritePermissions=False,
1430 IFPkgFlagRelocatable=False,
1431 IFPkgFlagRestartAction="NoRestart",
1432 IFPkgFlagRootVolumeOnly=True,
1433 IFPkgFlagUpdateInstalledLangauges=False,
1434 )
1435 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1436
1437 pl = Plist(
1438 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001439 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001440 IFPkgDescriptionVersion=vers,
1441 )
1442 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1443
1444 finally:
1445 os.chdir(curdir)
1446
1447
1448def makeMpkgPlist(path):
1449
1450 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001451 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001452
1453 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001454 CFBundleGetInfoString="Python %s"%(vers,),
1455 CFBundleIdentifier='org.python.Python',
1456 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001457 CFBundleShortVersionString=vers,
1458 IFMajorVersion=major,
1459 IFMinorVersion=minor,
1460 IFPkgFlagComponentDirectory="Contents/Packages",
1461 IFPkgFlagPackageList=[
1462 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001463 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001464 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001465 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001466 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001467 ],
1468 IFPkgFormatVersion=0.10000000149011612,
1469 IFPkgFlagBackgroundScaling="proportional",
1470 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001471 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001472 )
1473
1474 writePlist(pl, path)
1475
1476
1477def buildInstaller():
1478
1479 # Zap all compiled files
1480 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1481 for fn in filenames:
1482 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1483 os.unlink(os.path.join(dirpath, fn))
1484
1485 outdir = os.path.join(WORKDIR, 'installer')
1486 if os.path.exists(outdir):
1487 shutil.rmtree(outdir)
1488 os.mkdir(outdir)
1489
Ronald Oussoren508282e2009-03-30 19:34:51 +00001490 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001491 pkgcontents = os.path.join(pkgroot, 'Packages')
1492 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001493 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001494 packageFromRecipe(pkgcontents, recipe)
1495
1496 rsrcDir = os.path.join(pkgroot, 'Resources')
1497
1498 fn = os.path.join(pkgroot, 'PkgInfo')
1499 fp = open(fn, 'w')
1500 fp.write('pmkrpkg1')
1501 fp.close()
1502
1503 os.mkdir(rsrcDir)
1504
1505 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1506 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001507 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001508 IFPkgDescriptionVersion=getVersion(),
1509 )
1510
1511 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1512 for fn in os.listdir('resources'):
1513 if fn == '.svn': continue
1514 if fn.endswith('.jpg'):
1515 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1516 else:
1517 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1518
Ronald Oussorenc5555542006-06-11 20:24:45 +00001519 shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001520
1521
1522def installSize(clear=False, _saved=[]):
1523 if clear:
1524 del _saved[:]
1525 if not _saved:
1526 data = captureCommand("du -ks %s"%(
1527 shellQuote(os.path.join(WORKDIR, '_root'))))
1528 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1529 return _saved[0]
1530
1531
1532def buildDMG():
1533 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001534 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001535 """
1536 outdir = os.path.join(WORKDIR, 'diskimage')
1537 if os.path.exists(outdir):
1538 shutil.rmtree(outdir)
1539
1540 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001541 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001542 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001543 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001544 imagepath = imagepath + '.dmg'
1545
1546 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001547 volname='Python %s'%(getFullVersion())
1548 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1549 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001550 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001551 shellQuote(imagepath + ".tmp.dmg" )))
1552
1553
1554 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1555 os.mkdir(os.path.join(WORKDIR, "mnt"))
1556 runCommand("hdiutil attach %s -mountroot %s"%(
1557 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1558
1559 # Custom icon for the DMG, shown when the DMG is mounted.
1560 shutil.copy("../Icons/Disk Image.icns",
1561 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001562 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001563 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1564
1565 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1566
1567 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1568 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1569 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1570 setIcon(imagepath, "../Icons/Disk Image.icns")
1571
1572 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001573
1574 return imagepath
1575
1576
1577def setIcon(filePath, icnsPath):
1578 """
1579 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001580 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001581
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001582 dirPath = os.path.normpath(os.path.dirname(__file__))
1583 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001584 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1585 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1586 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001587 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1588 if not os.path.exists(appPath):
1589 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001590 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1591 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001592
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001593 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1594 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001595
1596def main():
1597 # First parse options and check if we can perform our work
1598 parseOptions()
1599 checkEnvironment()
1600
Ronald Oussoren508282e2009-03-30 19:34:51 +00001601 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001602 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001603 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001604
1605 if os.path.exists(WORKDIR):
1606 shutil.rmtree(WORKDIR)
1607 os.mkdir(WORKDIR)
1608
Ronald Oussoren287128a2010-04-18 14:01:05 +00001609 os.environ['LC_ALL'] = 'C'
1610
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001611 # Then build third-party libraries such as sleepycat DB4.
1612 buildLibraries()
1613
1614 # Now build python itself
1615 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001616
1617 # And then build the documentation
1618 # Remove the Deployment Target from the shell
1619 # environment, it's no longer needed and
1620 # an unexpected build target can cause problems
1621 # when Sphinx and its dependencies need to
1622 # be (re-)installed.
1623 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001624 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001625
1626
1627 # Prepare the applications folder
Benjamin Petersonc3104762008-10-03 11:52:06 +00001628 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001629 getVersion(),))
Ned Deily62a86602014-12-09 23:45:13 -08001630 fn = os.path.join(folder, "License.rtf")
1631 patchFile("resources/license.rtf", fn)
1632 fn = os.path.join(folder, "ReadMe.rtf")
1633 patchFile("resources/readme.rtf", fn)
1634 fn = os.path.join(folder, "Update Shell Profile.command")
1635 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilye1c9794952013-01-29 00:07:46 -08001636 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001637 setIcon(folder, "../Icons/Python Folder.icns")
1638
1639 # Create the installer
1640 buildInstaller()
1641
1642 # And copy the readme into the directory containing the installer
1643 patchFile('resources/ReadMe.txt', os.path.join(WORKDIR, 'installer', 'ReadMe.txt'))
1644
1645 # Ditto for the license file.
Ronald Oussorenc5555542006-06-11 20:24:45 +00001646 shutil.copy('../../LICENSE', os.path.join(WORKDIR, 'installer', 'License.txt'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001647
1648 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001649 fp.write("# BUILD INFO\n")
1650 fp.write("# Date: %s\n" % time.ctime())
1651 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001652 fp.close()
1653
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001654 # And copy it to a DMG
1655 buildDMG()
1656
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001657if __name__ == "__main__":
1658 main()